ui: gate settings workspace capabilities
This commit is contained in:
@@ -52,8 +52,9 @@ Progress:
|
|||||||
- `Secrets` page now has a mobile card layout instead of relying only on desktop table presentation
|
- `Secrets` page now has a mobile card layout instead of relying only on desktop table presentation
|
||||||
- `Usage` page now has a mobile card layout instead of relying only on desktop table presentation
|
- `Usage` page now has a mobile card layout instead of relying only on desktop table presentation
|
||||||
- `Agents` page and modal now explain separate agent endpoints, next-step machine access, and stable slug expectations
|
- `Agents` page and modal now explain separate agent endpoints, next-step machine access, and stable slug expectations
|
||||||
|
- `Settings` page now renders workspace protocol choices from build capabilities and uses honest Community notification copy instead of generic future promises
|
||||||
- pending:
|
- pending:
|
||||||
- remaining `Settings` and workspace commercial UX polish
|
- remaining `Settings` and workspace spacing/sticky polish
|
||||||
|
|
||||||
## Planned
|
## Planned
|
||||||
|
|
||||||
|
|||||||
@@ -161,13 +161,14 @@
|
|||||||
<option>Asia/Tokyo</option>
|
<option>Asia/Tokyo</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="field-group">
|
<div class="field-group">
|
||||||
<label class="field-label" data-i18n="settings.ws.protocol">Default protocol</label>
|
<label class="field-label" data-i18n="settings.ws.protocol">Default protocol</label>
|
||||||
<select class="field-select" id="settings-ws-protocol">
|
<select class="field-select" id="settings-ws-protocol">
|
||||||
<option value="rest" selected>REST / HTTP</option>
|
<option value="rest" selected>REST / HTTP</option>
|
||||||
<option value="graphql">GraphQL</option>
|
<option value="graphql">GraphQL</option>
|
||||||
<option value="grpc">gRPC</option>
|
<option value="grpc">gRPC</option>
|
||||||
</select>
|
</select>
|
||||||
|
<div class="field-hint" id="settings-ws-protocol-hint" data-i18n="settings.ws.protocol_hint">This build limits the default protocol to the capabilities available in the current edition.</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Language -->
|
<!-- Language -->
|
||||||
@@ -308,7 +309,7 @@
|
|||||||
<path d="M8 11V8M8 5.5V5"/>
|
<path d="M8 11V8M8 5.5V5"/>
|
||||||
</svg>
|
</svg>
|
||||||
<div>
|
<div>
|
||||||
<strong data-i18n="settings.notifications.not_ready_title">Available later.</strong> <span data-i18n="settings.notifications.not_ready_body">Notification routing and personal preferences will be added later.</span>
|
<strong id="settings-notifications-capability-title" data-i18n="settings.notifications.not_ready_title">Not included in this build.</strong> <span id="settings-notifications-capability-body" data-i18n="settings.notifications.not_ready_body">Notification routing and personal preferences are outside the current Community surface.</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -388,6 +388,8 @@ var TRANSLATIONS = {
|
|||||||
'settings.page.title': 'Account settings',
|
'settings.page.title': 'Account settings',
|
||||||
'settings.page.subtitle': 'Manage your profile, password, workspace settings, and the capability limits of this build.',
|
'settings.page.subtitle': 'Manage your profile, password, workspace settings, and the capability limits of this build.',
|
||||||
'settings.ws.slug_hint': 'Used in API endpoints and across the console. Only lowercase letters, numbers and hyphens.',
|
'settings.ws.slug_hint': 'Used in API endpoints and across the console. Only lowercase letters, numbers and hyphens.',
|
||||||
|
'settings.ws.protocol_hint': 'This build limits the default protocol to the capabilities available in the current edition.',
|
||||||
|
'settings.ws.protocol_hint_capabilities': 'This build allows {protocols} as workspace defaults. Additional protocols depend on the edition.',
|
||||||
'settings.ws.description_optional': 'Description (optional)',
|
'settings.ws.description_optional': 'Description (optional)',
|
||||||
'settings.ws.description_placeholder': 'What does this workspace do?',
|
'settings.ws.description_placeholder': 'What does this workspace do?',
|
||||||
'settings.profile.avatar_hint': 'Your avatar is generated from your display name and email.',
|
'settings.profile.avatar_hint': 'Your avatar is generated from your display name and email.',
|
||||||
@@ -441,6 +443,12 @@ var TRANSLATIONS = {
|
|||||||
'settings.notifications.title': 'Notifications',
|
'settings.notifications.title': 'Notifications',
|
||||||
'settings.notifications.not_ready_title': 'Not included in this build.',
|
'settings.notifications.not_ready_title': 'Not included in this build.',
|
||||||
'settings.notifications.not_ready_body': 'Notification routing and per-user preferences are outside the current Community surface.',
|
'settings.notifications.not_ready_body': 'Notification routing and per-user preferences are outside the current Community surface.',
|
||||||
|
'settings.notifications.capability_title_community': 'Community build boundary',
|
||||||
|
'settings.notifications.capability_body_community': 'Per-user notification routing and delivery channels are outside the current Community surface. Use logs and usage views for operational follow-up in this build.',
|
||||||
|
'settings.notifications.capability_title_enterprise': 'Enterprise delivery controls',
|
||||||
|
'settings.notifications.capability_body_enterprise': 'Notification routing depends on enterprise delivery controls and is wired separately from the Community console surface.',
|
||||||
|
'settings.notifications.capability_title_cloud': 'Cloud delivery controls',
|
||||||
|
'settings.notifications.capability_body_cloud': 'Notification routing depends on hosted delivery controls and is wired separately from the Community console surface.',
|
||||||
'settings.planned_badge': 'Planned',
|
'settings.planned_badge': 'Planned',
|
||||||
'settings.notifications.spike_title': 'Operation error spike',
|
'settings.notifications.spike_title': 'Operation error spike',
|
||||||
'settings.notifications.spike_body': 'Alert when error rate exceeds a rolling threshold for a published tool.',
|
'settings.notifications.spike_body': 'Alert when error rate exceeds a rolling threshold for a published tool.',
|
||||||
@@ -1457,6 +1465,8 @@ var TRANSLATIONS = {
|
|||||||
'settings.page.title': 'Настройки аккаунта',
|
'settings.page.title': 'Настройки аккаунта',
|
||||||
'settings.page.subtitle': 'Управляйте профилем, паролем, настройками воркспейса и границами возможностей текущей сборки.',
|
'settings.page.subtitle': 'Управляйте профилем, паролем, настройками воркспейса и границами возможностей текущей сборки.',
|
||||||
'settings.ws.slug_hint': 'Используется в API endpoint-ах и по всей консоли. Только строчные буквы, цифры и дефисы.',
|
'settings.ws.slug_hint': 'Используется в API endpoint-ах и по всей консоли. Только строчные буквы, цифры и дефисы.',
|
||||||
|
'settings.ws.protocol_hint': 'Эта сборка ограничивает протокол по умолчанию возможностями текущей редакции.',
|
||||||
|
'settings.ws.protocol_hint_capabilities': 'В этой сборке в качестве протокола воркспейса доступны {protocols}. Дополнительные протоколы зависят от редакции.',
|
||||||
'settings.ws.description_optional': 'Описание (необязательно)',
|
'settings.ws.description_optional': 'Описание (необязательно)',
|
||||||
'settings.ws.description_placeholder': 'Чем занимается этот воркспейс?',
|
'settings.ws.description_placeholder': 'Чем занимается этот воркспейс?',
|
||||||
'settings.profile.avatar_hint': 'Аватар строится из отображаемого имени и email.',
|
'settings.profile.avatar_hint': 'Аватар строится из отображаемого имени и email.',
|
||||||
@@ -1510,6 +1520,12 @@ var TRANSLATIONS = {
|
|||||||
'settings.notifications.title': 'Уведомления',
|
'settings.notifications.title': 'Уведомления',
|
||||||
'settings.notifications.not_ready_title': 'Не входит в эту сборку.',
|
'settings.notifications.not_ready_title': 'Не входит в эту сборку.',
|
||||||
'settings.notifications.not_ready_body': 'Маршрутизация уведомлений и персональные настройки находятся вне текущей поверхности Community.',
|
'settings.notifications.not_ready_body': 'Маршрутизация уведомлений и персональные настройки находятся вне текущей поверхности Community.',
|
||||||
|
'settings.notifications.capability_title_community': 'Граница редакции Community',
|
||||||
|
'settings.notifications.capability_body_community': 'Персональные каналы доставки и маршрутизация уведомлений не входят в текущую поверхность Community. В этой сборке для операционного контроля используйте журналы и представления использования.',
|
||||||
|
'settings.notifications.capability_title_enterprise': 'Контур доставки Enterprise',
|
||||||
|
'settings.notifications.capability_body_enterprise': 'Маршрутизация уведомлений зависит от корпоративного контура доставки и подключается отдельно от поверхности консоли Community.',
|
||||||
|
'settings.notifications.capability_title_cloud': 'Контур доставки Cloud',
|
||||||
|
'settings.notifications.capability_body_cloud': 'Маршрутизация уведомлений зависит от облачного контура доставки и подключается отдельно от поверхности консоли Community.',
|
||||||
'settings.planned_badge': 'Запланировано',
|
'settings.planned_badge': 'Запланировано',
|
||||||
'settings.notifications.spike_title': 'Всплеск ошибок операции',
|
'settings.notifications.spike_title': 'Всплеск ошибок операции',
|
||||||
'settings.notifications.spike_body': 'Алерт, когда доля ошибок превышает скользящий порог для опубликованного инструмента.',
|
'settings.notifications.spike_body': 'Алерт, когда доля ошибок превышает скользящий порог для опубликованного инструмента.',
|
||||||
|
|||||||
+53
-1
@@ -75,6 +75,55 @@ function capabilityLabel(key) {
|
|||||||
return tKey('settings.capability.' + key);
|
return tKey('settings.capability.' + key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderWorkspaceProtocolOptions(capabilities, selectedProtocol) {
|
||||||
|
var select = document.getElementById('settings-ws-protocol');
|
||||||
|
if (!select) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var supportedProtocols = capabilities && Array.isArray(capabilities.supported_protocols) && capabilities.supported_protocols.length
|
||||||
|
? capabilities.supported_protocols
|
||||||
|
: ['rest', 'graphql', 'grpc'];
|
||||||
|
var fallbackProtocol = supportedProtocols.indexOf('rest') !== -1 ? 'rest' : supportedProtocols[0];
|
||||||
|
var effectiveSelection = supportedProtocols.indexOf(selectedProtocol) !== -1
|
||||||
|
? selectedProtocol
|
||||||
|
: fallbackProtocol;
|
||||||
|
|
||||||
|
select.innerHTML = '';
|
||||||
|
supportedProtocols.forEach(function (protocol) {
|
||||||
|
var option = document.createElement('option');
|
||||||
|
option.value = protocol;
|
||||||
|
option.textContent = capabilityLabel(protocol);
|
||||||
|
option.selected = protocol === effectiveSelection;
|
||||||
|
select.appendChild(option);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function populateWorkspaceCapabilityHint(capabilities) {
|
||||||
|
var body = document.getElementById('settings-ws-protocol-hint');
|
||||||
|
if (!body || !capabilities) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var protocols = (capabilities.supported_protocols || []).map(capabilityLabel).join(', ');
|
||||||
|
body.textContent = tfKey('settings.ws.protocol_hint_capabilities', {
|
||||||
|
protocols: protocols || capabilityLabel('none'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function populateNotificationsCapabilityHint(capabilities) {
|
||||||
|
var title = document.getElementById('settings-notifications-capability-title');
|
||||||
|
var body = document.getElementById('settings-notifications-capability-body');
|
||||||
|
if (!title || !body || !capabilities) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
title.textContent = tKey('settings.notifications.capability_title_' + capabilities.edition);
|
||||||
|
body.textContent = tfKey('settings.notifications.capability_body_' + capabilities.edition, {
|
||||||
|
edition: capabilities.edition,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function populateCapabilities(capabilities) {
|
function populateCapabilities(capabilities) {
|
||||||
var title = document.getElementById('settings-security-capability-title');
|
var title = document.getElementById('settings-security-capability-title');
|
||||||
var body = document.getElementById('settings-security-capability-body');
|
var body = document.getElementById('settings-security-capability-body');
|
||||||
@@ -96,6 +145,9 @@ function populateCapabilities(capabilities) {
|
|||||||
max_users: limits.max_users_per_workspace == null ? capabilityLabel('unlimited') : String(limits.max_users_per_workspace),
|
max_users: limits.max_users_per_workspace == null ? capabilityLabel('unlimited') : String(limits.max_users_per_workspace),
|
||||||
max_agents: limits.max_agents_per_workspace == null ? capabilityLabel('unlimited') : String(limits.max_agents_per_workspace),
|
max_agents: limits.max_agents_per_workspace == null ? capabilityLabel('unlimited') : String(limits.max_agents_per_workspace),
|
||||||
});
|
});
|
||||||
|
populateWorkspaceCapabilityHint(capabilities);
|
||||||
|
populateNotificationsCapabilityHint(capabilities);
|
||||||
|
renderWorkspaceProtocolOptions(capabilities, document.getElementById('settings-ws-protocol').value || 'rest');
|
||||||
}
|
}
|
||||||
|
|
||||||
function populateProfile(session) {
|
function populateProfile(session) {
|
||||||
@@ -310,7 +362,7 @@ async function loadWorkspaceSettings() {
|
|||||||
document.getElementById('settings-ws-display-name').value = item.display_name || '';
|
document.getElementById('settings-ws-display-name').value = item.display_name || '';
|
||||||
document.getElementById('settings-ws-description').value = settings.description || '';
|
document.getElementById('settings-ws-description').value = settings.description || '';
|
||||||
document.getElementById('settings-ws-timezone').value = settings.timezone || 'UTC';
|
document.getElementById('settings-ws-timezone').value = settings.timezone || 'UTC';
|
||||||
document.getElementById('settings-ws-protocol').value = settings.default_protocol || 'rest';
|
renderWorkspaceProtocolOptions(settingsCapabilities, settings.default_protocol || 'rest');
|
||||||
|
|
||||||
document.querySelectorAll('#section-workspace .lang-btn').forEach(function (button) {
|
document.querySelectorAll('#section-workspace .lang-btn').forEach(function (button) {
|
||||||
button.classList.toggle('active', button.dataset.lang === (settings.language || (localStorage.getItem('crank_lang') || 'en')));
|
button.classList.toggle('active', button.dataset.lang === (settings.language || (localStorage.getItem('crank_lang') || 'en')));
|
||||||
|
|||||||
@@ -15,4 +15,6 @@ test('workspace and settings pages show live session data', async ({ page }) =>
|
|||||||
await expect(page.locator('#section-security')).toBeVisible();
|
await expect(page.locator('#section-security')).toBeVisible();
|
||||||
await expect(page.locator('#settings-security-capability-title')).toContainText(localized('Community build', 'Редакция Community'));
|
await expect(page.locator('#settings-security-capability-title')).toContainText(localized('Community build', 'Редакция Community'));
|
||||||
await expect(page.locator('#settings-security-capability-body')).toContainText(localized('static agent keys', 'статические ключи агентов'));
|
await expect(page.locator('#settings-security-capability-body')).toContainText(localized('static agent keys', 'статические ключи агентов'));
|
||||||
|
await expect(page.locator('#settings-ws-protocol-hint')).toContainText(localized('REST / HTTP', 'REST / HTTP'));
|
||||||
|
await expect(page.locator('#settings-notifications-capability-title')).toContainText(localized('Community build boundary', 'Граница редакции Community'));
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user