From 0d8a36e4d4af1a5569d7635653f8c2c725587d49 Mon Sep 17 00:00:00 2001 From: github-ops Date: Fri, 19 Jun 2026 17:59:57 +0000 Subject: [PATCH] Remove community workspace dropdown UI --- apps/ui/css/pages.css | 101 ------------- apps/ui/html/agents.html | 9 +- apps/ui/html/api-keys.html | 9 +- apps/ui/html/logs.html | 9 +- apps/ui/html/secrets.html | 8 +- apps/ui/html/settings.html | 9 +- apps/ui/html/usage.html | 9 +- apps/ui/html/wizard/index.html | 9 +- apps/ui/index.html | 9 +- apps/ui/js/i18n.js | 4 - apps/ui/js/workspace.js | 207 +++++---------------------- apps/ui/tests/e2e/operations.spec.js | 6 +- 12 files changed, 52 insertions(+), 337 deletions(-) diff --git a/apps/ui/css/pages.css b/apps/ui/css/pages.css index 0922e6d..2bbf1cc 100644 --- a/apps/ui/css/pages.css +++ b/apps/ui/css/pages.css @@ -929,81 +929,6 @@ max-width: 140px; } -.ws-dropdown { - position: absolute; - top: calc(100% + 6px); - left: 0; - min-width: 230px; - background: var(--bg-surface); - border: 1px solid var(--border); - border-radius: 10px; - box-shadow: 0 8px 28px rgba(0,0,0,0.45); - z-index: 200; - overflow: hidden; -} - -.ws-dropdown-label { - padding: 10px 14px 4px; - font-size: 11px; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.06em; - color: var(--text-muted); -} - -.ws-dropdown-item { - display: flex; - align-items: center; - gap: 10px; - padding: 8px 14px; - cursor: pointer; - transition: background 0.1s; -} -.ws-dropdown-item:hover { background: rgba(255,255,255,0.04); } -.ws-dropdown-item.active { background: rgba(56,139,253,0.08); } - -.ws-item-dot { - width: 30px; - height: 30px; - border-radius: 7px; - display: flex; - align-items: center; - justify-content: center; - font-size: 13px; - font-weight: 700; - color: #fff; - flex-shrink: 0; -} - -.ws-item-info { flex: 1; min-width: 0; } -.ws-item-name { - font-size: 13px; - font-weight: 500; - color: var(--text-primary); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} -.ws-item-role { font-size: 11px; color: var(--text-muted); } - -.ws-dropdown-footer { border-top: 1px solid var(--border-subtle); padding: 6px 8px; } -.ws-dropdown-create { - display: flex; - align-items: center; - gap: 8px; - width: 100%; - padding: 6px 8px; - background: none; - border: none; - border-radius: 6px; - cursor: pointer; - font-size: 13px; - color: var(--text-secondary); - transition: background 0.1s, color 0.1s; -} -.ws-dropdown-create:hover { background: rgba(255,255,255,0.04); color: var(--text-primary); } - - /* ═══════════════════════════════════════════════════ Agents page — info callout ═══════════════════════════════════════════════════ */ @@ -1554,32 +1479,6 @@ line-height: 1.5; } -/* ── Workspace dropdown management links ── */ -.ws-dropdown-divider { - height: 1px; - background: var(--border-subtle); - margin: 4px 0; -} -.ws-dropdown-mgmt-link { - display: flex; - align-items: center; - gap: 8px; - padding: 7px 14px; - font-size: 13px; - color: var(--text-secondary); - text-decoration: none; - transition: background 0.1s, color 0.1s; - cursor: pointer; -} -.ws-dropdown-mgmt-link:hover { - background: rgba(255,255,255,0.04); - color: var(--text-primary); -} -.ws-dropdown-mgmt-link svg { - flex-shrink: 0; - opacity: 0.7; -} - @media (max-width: 900px) { .toast-stack { top: 72px; diff --git a/apps/ui/html/agents.html b/apps/ui/html/agents.html index eaa580d..58dd05f 100644 --- a/apps/ui/html/agents.html +++ b/apps/ui/html/agents.html @@ -22,16 +22,11 @@ Crank - +
- -
diff --git a/apps/ui/html/api-keys.html b/apps/ui/html/api-keys.html index e3ce800..51dff63 100644 --- a/apps/ui/html/api-keys.html +++ b/apps/ui/html/api-keys.html @@ -39,16 +39,11 @@ Crank - +
- -
diff --git a/apps/ui/html/logs.html b/apps/ui/html/logs.html index 5ecad7b..d1a943a 100644 --- a/apps/ui/html/logs.html +++ b/apps/ui/html/logs.html @@ -20,16 +20,11 @@ Crank - +
- -
diff --git a/apps/ui/html/secrets.html b/apps/ui/html/secrets.html index 7f11676..65a4bbd 100644 --- a/apps/ui/html/secrets.html +++ b/apps/ui/html/secrets.html @@ -54,15 +54,11 @@ Crank +
- -
diff --git a/apps/ui/html/settings.html b/apps/ui/html/settings.html index 9060db0..6e8bc97 100644 --- a/apps/ui/html/settings.html +++ b/apps/ui/html/settings.html @@ -20,16 +20,11 @@ Crank - +
- -
diff --git a/apps/ui/html/usage.html b/apps/ui/html/usage.html index 8c9bd8b..09acd43 100644 --- a/apps/ui/html/usage.html +++ b/apps/ui/html/usage.html @@ -27,16 +27,11 @@ Crank - +
- -
diff --git a/apps/ui/html/wizard/index.html b/apps/ui/html/wizard/index.html index 13c4c74..17cc14f 100644 --- a/apps/ui/html/wizard/index.html +++ b/apps/ui/html/wizard/index.html @@ -20,16 +20,11 @@ Crank - +
- -
diff --git a/apps/ui/index.html b/apps/ui/index.html index 3b8630a..8f8a9fa 100644 --- a/apps/ui/index.html +++ b/apps/ui/index.html @@ -20,16 +20,11 @@ Crank - +
- -
diff --git a/apps/ui/js/i18n.js b/apps/ui/js/i18n.js index d5af26a..ac250b2 100644 --- a/apps/ui/js/i18n.js +++ b/apps/ui/js/i18n.js @@ -15,7 +15,6 @@ var TRANSLATIONS = { 'nav.settings': 'Settings', 'nav.logout': 'Log out', 'nav.workspaces': 'Your workspaces', - 'nav.create_workspace': 'Create workspace', 'nav.notifications': 'Notifications', 'nav.menu': 'Menu', 'nav.account': 'Account', @@ -469,7 +468,6 @@ var TRANSLATIONS = { 'workspace_setup.slug_hint': 'Only lowercase letters, numbers, and hyphens. Used in MCP endpoint URLs.', 'workspace_setup.description': 'Description', 'workspace_setup.description_placeholder': 'What does this workspace do?', - 'workspace_setup.actions.create': 'Create workspace', 'workspace_setup.actions.save': 'Save changes', 'workspace_setup.create.title': 'Create a new workspace', 'workspace_setup.create.subtitle': 'This Community installation uses one workspace for MCP operations and agents.', @@ -925,7 +923,6 @@ var TRANSLATIONS = { 'nav.settings': 'Настройки', 'nav.logout': 'Выйти', 'nav.workspaces': 'Ваши воркспейсы', - 'nav.create_workspace': 'Создать воркспейс', 'nav.notifications': 'Уведомления', 'nav.menu': 'Меню', 'nav.account': 'Аккаунт', @@ -1383,7 +1380,6 @@ var TRANSLATIONS = { 'workspace_setup.slug_hint': 'Только строчные буквы, цифры и дефисы. Используется в URL MCP endpoint-ов.', 'workspace_setup.description': 'Описание', 'workspace_setup.description_placeholder': 'Чем занимается этот воркспейс?', - 'workspace_setup.actions.create': 'Создать воркспейс', 'workspace_setup.actions.save': 'Сохранить изменения', 'workspace_setup.create.title': 'Создать новый воркспейс', 'workspace_setup.create.subtitle': 'В Community используется один воркспейс для MCP-операций и агентов.', diff --git a/apps/ui/js/workspace.js b/apps/ui/js/workspace.js index d44cd80..0a55261 100644 --- a/apps/ui/js/workspace.js +++ b/apps/ui/js/workspace.js @@ -5,22 +5,10 @@ var WS_LIST = [ var workspaceLoadPromise = null; var workspaceLoadFailed = false; var lastWorkspaceId = null; + function tKey(key) { return typeof t === 'function' ? t(key) : key; } -function tfKey(key, vars) { - return typeof tf === 'function' ? tf(key, vars) : key; -} - -function buildIconSvg(href, width, height) { - var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); - svg.setAttribute('width', String(width)); - svg.setAttribute('height', String(height)); - var use = document.createElementNS('http://www.w3.org/2000/svg', 'use'); - use.setAttribute('href', href); - svg.appendChild(use); - return svg; -} function workspaceColor(index) { return ['#0d9488', '#7c3aed', '#0891b2', '#f59e0b', '#2563eb'][index % 5]; @@ -45,30 +33,6 @@ function mapWorkspace(record, index) { }; } -function cachedWorkspaceId() { - try { - return localStorage.getItem('crank_workspace_id'); - } catch (_error) { - return null; - } -} - -function cachedWorkspaceSlug() { - try { - return localStorage.getItem('crank_workspace_slug'); - } catch (_error) { - return null; - } -} - -function sessionWorkspaceId() { - if (!window.CrankAuth || typeof window.CrankAuth.getCachedSession !== 'function') { - return null; - } - var session = window.CrankAuth.getCachedSession(); - return session && session.current_workspace_id ? session.current_workspace_id : null; -} - function cacheCurrentWorkspace(workspace) { if (!workspace) return; try { @@ -78,30 +42,7 @@ function cacheCurrentWorkspace(workspace) { } function resolveCurrentWorkspace() { - var workspaceId = sessionWorkspaceId() || cachedWorkspaceId(); - var workspaceSlug = cachedWorkspaceSlug(); - return WS_LIST.find(function(item) { return item.id === workspaceId; }) - || WS_LIST.find(function(item) { return item.slug === workspaceSlug; }) - || WS_LIST[0] - || null; -} - -function emitWorkspaceChange(workspace) { - if (!workspace) { - return; - } - lastWorkspaceId = workspace.id; - window.dispatchEvent(new CustomEvent('crank:workspacechange', { detail: workspace })); -} - -function getCurrentWs() { - var workspace = resolveCurrentWorkspace(); - - if (workspace) { - cacheCurrentWorkspace(workspace); - } - - return workspace; + return WS_LIST[0] || null; } function updateWorkspaceHeader(workspace) { @@ -114,77 +55,24 @@ function updateWorkspaceHeader(workspace) { } } -function renderWorkspaceList() { - var current = getCurrentWs(); - updateWorkspaceHeader(current); +function emitWorkspaceChange(workspace) { + if (!workspace || workspace.id === lastWorkspaceId) { + return; + } + lastWorkspaceId = workspace.id; + window.dispatchEvent(new CustomEvent('crank:workspacechange', { detail: workspace })); +} - document.querySelectorAll('.ws-dropdown-footer').forEach(function(footer) { - footer.hidden = true; - }); +function getCurrentWs() { + var workspace = resolveCurrentWorkspace(); + if (workspace) { + cacheCurrentWorkspace(workspace); + } + return workspace; +} - var list = document.getElementById('ws-dropdown-list'); - if (!list) return; - window.CrankDom.clear(list); - - WS_LIST.forEach(function(workspace) { - var active = current && workspace.id === current.id; - var item = document.createElement('div'); - item.className = 'ws-dropdown-item' + (active ? ' active' : ''); - item.addEventListener('click', function() { - var dd = document.getElementById('ws-dropdown'); - if (dd) dd.hidden = true; - }); - - var dot = document.createElement('div'); - dot.className = 'ws-item-dot'; - dot.style.background = workspace.color; - dot.textContent = workspace.letter; - item.appendChild(dot); - - var info = document.createElement('div'); - info.className = 'ws-item-info'; - var name = document.createElement('div'); - name.className = 'ws-item-name'; - name.textContent = workspace.name; - var role = document.createElement('div'); - role.className = 'ws-item-role'; - role.textContent = workspace.role; - info.appendChild(name); - info.appendChild(role); - item.appendChild(info); - - if (active) { - var check = buildIconSvg( - (window.APP_BASE || '') + 'icons/general/check.svg#icon', - 12, - 12 - ); - item.appendChild(check); - } - - list.appendChild(item); - }); - - var divider = document.createElement('div'); - divider.className = 'ws-dropdown-divider'; - list.appendChild(divider); - - var manageLink = document.createElement('a'); - manageLink.className = 'ws-dropdown-mgmt-link'; - manageLink.href = (window.CrankRoutes && window.CrankRoutes.workspaceSetup) || '/workspace-setup'; - manageLink.addEventListener('click', function() { - var dd = document.getElementById('ws-dropdown'); - if (dd) dd.hidden = true; - }); - manageLink.appendChild( - buildIconSvg( - (window.APP_BASE || '') + 'icons/general/settings.svg#icon', - 14, - 14 - ) - ); - manageLink.appendChild(document.createTextNode(tKey('settings.ws.title'))); - list.appendChild(manageLink); +function renderWorkspaceHeader() { + updateWorkspaceHeader(getCurrentWs()); } async function loadWorkspaces() { @@ -192,7 +80,7 @@ async function loadWorkspaces() { workspaceLoadPromise = (async function() { if (!window.CrankApi) { - renderWorkspaceList(); + renderWorkspaceHeader(); return WS_LIST; } @@ -200,14 +88,15 @@ async function loadWorkspaces() { var response = await window.CrankApi.listWorkspaces(); var items = (response && response.items ? response.items : []).map(mapWorkspace); if (items.length > 0) { - WS_LIST = items; + WS_LIST = [items[0]]; } workspaceLoadFailed = false; } catch (_error) { workspaceLoadFailed = true; } - renderWorkspaceList(); + renderWorkspaceHeader(); + emitWorkspaceChange(getCurrentWs()); return WS_LIST; }()); @@ -219,35 +108,14 @@ async function refreshWorkspaces() { return loadWorkspaces(); } -function initWorkspaceSwitcher() { - renderWorkspaceList(); - loadWorkspaces(); -} - -function toggleWsSwitcher(e) { - e.stopPropagation(); - var dd = document.getElementById('ws-dropdown'); - if (dd) dd.hidden = !dd.hidden; -} - -function switchWorkspace(workspaceId) { - var workspace = WS_LIST.find(function(item) { return item.id === workspaceId; }); - if (!workspace) return; - - var dd = document.getElementById('ws-dropdown'); - if (dd) dd.hidden = true; - - cacheCurrentWorkspace(workspace); - renderWorkspaceList(); - emitWorkspaceChange(workspace); - return Promise.resolve(workspace); -} - function setCurrentWorkspace(workspace) { - if (!workspace) { - return Promise.resolve(null); + if (workspace) { + WS_LIST = [workspace]; + cacheCurrentWorkspace(workspace); + renderWorkspaceHeader(); + emitWorkspaceChange(workspace); } - return Promise.resolve(switchWorkspace(workspace.id)); + return Promise.resolve(workspace || null); } window.getCurrentWorkspace = getCurrentWs; @@ -256,19 +124,12 @@ window.refreshWorkspaces = refreshWorkspaces; window.setCurrentWorkspace = setCurrentWorkspace; window.hasWorkspaceApiFailure = function() { return workspaceLoadFailed; }; -document.addEventListener('DOMContentLoaded', initWorkspaceSwitcher); +document.addEventListener('DOMContentLoaded', function() { + renderWorkspaceHeader(); + loadWorkspaces(); +}); window.addEventListener('crank:sessionchange', function() { - var workspace = getCurrentWs(); - renderWorkspaceList(); - if (workspace && workspace.id !== lastWorkspaceId) { - emitWorkspaceChange(workspace); - } -}); - -document.addEventListener('click', function(e) { - if (!e.target.closest('#ws-switcher')) { - var dd = document.getElementById('ws-dropdown'); - if (dd) dd.hidden = true; - } + renderWorkspaceHeader(); + emitWorkspaceChange(getCurrentWs()); }); diff --git a/apps/ui/tests/e2e/operations.spec.js b/apps/ui/tests/e2e/operations.spec.js index 462b633..154df2e 100644 --- a/apps/ui/tests/e2e/operations.spec.js +++ b/apps/ui/tests/e2e/operations.spec.js @@ -4,10 +4,8 @@ const { login, localized } = require('./helpers'); test('operations page shows demo catalog and filter works', async ({ page }) => { await login(page); await expect(page.locator('.page-heading')).toHaveText(localized('Operations', 'Операции')); - await page.locator('.ws-switcher-trigger').click(); - await expect(page.locator('#ws-dropdown')).toBeVisible(); - await page.mouse.click(900, 220); - await expect(page.locator('#ws-dropdown')).toBeHidden(); + await expect(page.locator('.ws-switcher-trigger')).toBeVisible(); + await expect(page.locator('#ws-dropdown')).toHaveCount(0); await expect(page.locator('tbody tr')).toHaveCount(2); await page.getByPlaceholder(localized('Search operations', 'Поиск операций')).fill('crm'); await expect(page.locator('tbody tr')).toHaveCount(1);