mirror of
https://github.com/screentinker/screentinker.git
synced 2026-06-21 05:32:34 -06:00
Merge pull request #25 from screentinker/fix/single-workspace-settings-19
fix(switcher): workspace settings inaccessible with a single workspace (#19)
This commit is contained in:
commit
5433a97bc9
|
|
@ -58,6 +58,11 @@ body {
|
||||||
display: block; padding: 8px 10px;
|
display: block; padding: 8px 10px;
|
||||||
color: var(--text-muted); font-size: 12px; font-style: italic;
|
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 {
|
.workspace-switcher-button .chev {
|
||||||
flex-shrink: 0; margin-left: 8px; color: var(--text-muted);
|
flex-shrink: 0; margin-left: 8px; color: var(--text-muted);
|
||||||
transition: transform var(--transition);
|
transition: transform var(--transition);
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,51 @@ function formatResourceCount(n, keyBase, zeroKey) {
|
||||||
return tn(keyBase, n);
|
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 `
|
||||||
|
<button class="workspace-switcher-members" type="button" data-members-id="${esc(w.id)}" aria-label="Manage members" title="Manage members">
|
||||||
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/>
|
||||||
|
<circle cx="9" cy="7" r="4"/>
|
||||||
|
<path d="M23 21v-2a4 4 0 0 0-3-3.87"/>
|
||||||
|
<path d="M16 3.13a4 4 0 0 1 0 7.75"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<button class="workspace-switcher-pencil" type="button" data-rename-id="${esc(w.id)}" aria-label="Rename workspace" title="Rename">
|
||||||
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<path d="M12 20h9"/><path d="M16.5 3.5a2.121 2.121 0 1 1 3 3L7 19l-4 1 1-4 12.5-12.5z"/>
|
||||||
|
</svg>
|
||||||
|
</button>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
// Render the workspace switcher inside #workspaceSwitcher based on the
|
||||||
// /api/auth/me response. Three modes:
|
// /api/auth/me response. Three modes:
|
||||||
// - 0 accessible workspaces: muted "No workspace" placeholder
|
// - 0 accessible workspaces: muted "No workspace" placeholder
|
||||||
|
|
@ -33,8 +78,17 @@ export function renderWorkspaceSwitcher(me) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (list.length === 1) {
|
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.classList.remove('open');
|
||||||
container.innerHTML = `<span class="workspace-switcher-static">${esc(list[0].name)}</span>`;
|
const only = list[0];
|
||||||
|
container.innerHTML = `
|
||||||
|
<div class="workspace-switcher-single">
|
||||||
|
<span class="workspace-switcher-static">${esc(only.name)}</span>
|
||||||
|
${adminIconsHtml(only)}
|
||||||
|
</div>`;
|
||||||
|
wireAdminIcons(container, [only]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -79,21 +133,7 @@ export function renderWorkspaceSwitcher(me) {
|
||||||
<div class="ws-name">${esc(w.name)}</div>
|
<div class="ws-name">${esc(w.name)}</div>
|
||||||
<div class="ws-org">${subtitle}</div>
|
<div class="ws-org">${subtitle}</div>
|
||||||
</div>
|
</div>
|
||||||
${w.can_admin ? `
|
${adminIconsHtml(w)}
|
||||||
<button class="workspace-switcher-members" type="button" data-members-id="${esc(w.id)}" aria-label="Manage members" title="Manage members">
|
|
||||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
||||||
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/>
|
|
||||||
<circle cx="9" cy="7" r="4"/>
|
|
||||||
<path d="M23 21v-2a4 4 0 0 0-3-3.87"/>
|
|
||||||
<path d="M16 3.13a4 4 0 0 1 0 7.75"/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<button class="workspace-switcher-pencil" type="button" data-rename-id="${esc(w.id)}" aria-label="Rename workspace" title="Rename">
|
|
||||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
||||||
<path d="M12 20h9"/><path d="M16.5 3.5a2.121 2.121 0 1 1 3 3L7 19l-4 1 1-4 12.5-12.5z"/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
` : ''}
|
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}).join('')}
|
}).join('')}
|
||||||
|
|
@ -179,30 +219,8 @@ export function renderWorkspaceSwitcher(me) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Pencil click opens the rename modal. Must stopPropagation so the click
|
// Manage-members + rename icons (shared with the single-workspace view).
|
||||||
// doesn't bubble up to the switcher-item's switch handler.
|
wireAdminIcons(container, sorted);
|
||||||
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`;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
container.querySelectorAll('.workspace-switcher-item').forEach(item => {
|
container.querySelectorAll('.workspace-switcher-item').forEach(item => {
|
||||||
item.addEventListener('click', (e) => {
|
item.addEventListener('click', (e) => {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue