diff --git a/frontend/css/main.css b/frontend/css/main.css index 6247f1b..3141345 100644 --- a/frontend/css/main.css +++ b/frontend/css/main.css @@ -58,6 +58,11 @@ body { display: block; padding: 8px 10px; color: var(--text-muted); font-size: 12px; font-style: italic; } +/* #19: single-workspace view - name + always-visible manage icons (no dropdown). */ +.workspace-switcher-single { display: flex; align-items: center; gap: 4px; } +.workspace-switcher-single .workspace-switcher-static { flex: 1; min-width: 0; } +.workspace-switcher-single .workspace-switcher-members, +.workspace-switcher-single .workspace-switcher-pencil { visibility: visible; align-self: end; } .workspace-switcher-button .chev { flex-shrink: 0; margin-left: 8px; color: var(--text-muted); transition: transform var(--transition); diff --git a/frontend/js/components/workspace-switcher.js b/frontend/js/components/workspace-switcher.js index 69dd68a..fda759a 100644 --- a/frontend/js/components/workspace-switcher.js +++ b/frontend/js/components/workspace-switcher.js @@ -14,6 +14,51 @@ function formatResourceCount(n, keyBase, zeroKey) { return tn(keyBase, n); } +// Admin affordances shown beside a workspace: manage members + rename. Returns +// '' for non-admins. Shared by the single-workspace view and the multi-workspace +// dropdown items so the two never drift - #19: the single view was missing these, +// locking single-workspace users out of org settings (invite users, perms, slug). +function adminIconsHtml(w) { + if (!w.can_admin) return ''; + return ` + + `; +} + +// Wire the manage-members + rename buttons within `scope`. `list` resolves a +// workspace id to its object (for the rename modal). stopPropagation so a click +// on an icon never triggers the row's switch handler. +function wireAdminIcons(scope, list) { + scope.querySelectorAll('.workspace-switcher-pencil').forEach(btn => { + btn.addEventListener('click', async (e) => { + e.stopPropagation(); + const ws = list.find(w => w.id === btn.dataset.renameId); + if (!ws) return; + scope.classList.remove('open'); + const { openWorkspaceRenameModal } = await import('./workspace-rename-modal.js'); + openWorkspaceRenameModal(ws); + }); + }); + scope.querySelectorAll('.workspace-switcher-members').forEach(btn => { + btn.addEventListener('click', (e) => { + e.stopPropagation(); + scope.classList.remove('open'); + window.location.hash = `#/workspace/${btn.dataset.membersId}/members`; + }); + }); +} + // Render the workspace switcher inside #workspaceSwitcher based on the // /api/auth/me response. Three modes: // - 0 accessible workspaces: muted "No workspace" placeholder @@ -33,8 +78,17 @@ export function renderWorkspaceSwitcher(me) { } if (list.length === 1) { + // #19: a single workspace still needs its admin affordances (manage members / + // rename + slug). Render the name as before, plus the inline manage icons + // when the user can administer it - no dropdown for one item. container.classList.remove('open'); - container.innerHTML = `${esc(list[0].name)}`; + const only = list[0]; + container.innerHTML = ` +
+ ${esc(only.name)} + ${adminIconsHtml(only)} +
`; + wireAdminIcons(container, [only]); return; } @@ -79,21 +133,7 @@ export function renderWorkspaceSwitcher(me) {
${esc(w.name)}
${subtitle}
- ${w.can_admin ? ` - - - ` : ''} + ${adminIconsHtml(w)} `; }).join('')} @@ -179,30 +219,8 @@ export function renderWorkspaceSwitcher(me) { } }); - // Pencil click opens the rename modal. Must stopPropagation so the click - // doesn't bubble up to the switcher-item's switch handler. - container.querySelectorAll('.workspace-switcher-pencil').forEach(btn => { - btn.addEventListener('click', async (e) => { - e.stopPropagation(); - const wsId = btn.dataset.renameId; - const ws = sorted.find(w => w.id === wsId); - if (!ws) return; - container.classList.remove('open'); - const { openWorkspaceRenameModal } = await import('./workspace-rename-modal.js'); - openWorkspaceRenameModal(ws); - }); - }); - - // Members icon navigates to the workspace members page. Same stopPropagation - // pattern as the pencil so the click doesn't trigger workspace-switch. - container.querySelectorAll('.workspace-switcher-members').forEach(btn => { - btn.addEventListener('click', (e) => { - e.stopPropagation(); - const wsId = btn.dataset.membersId; - container.classList.remove('open'); - window.location.hash = `#/workspace/${wsId}/members`; - }); - }); + // Manage-members + rename icons (shared with the single-workspace view). + wireAdminIcons(container, sorted); container.querySelectorAll('.workspace-switcher-item').forEach(item => { item.addEventListener('click', (e) => {