import { api } from '../api.js'; import { showToast } from './toast.js'; import { t, tn } from '../i18n.js'; // Reusable resource-count formatter. Returns localized "1 device" / "N devices" // / "No devices" based on n. Generic so the same shape can wire users / // playlists / schedules counts later without refactor - caller supplies the // i18n key bases. // keyBase: e.g. 'switcher.devices_count' (looks up _one / _other variants via tn) // zeroKey: e.g. 'switcher.no_devices' (direct lookup for n === 0) function formatResourceCount(n, keyBase, zeroKey) { if (n === undefined || n === null) return ''; if (n === 0) return t(zeroKey); return tn(keyBase, n); } // Render the workspace switcher inside #workspaceSwitcher based on the // /api/auth/me response. Three modes: // - 0 accessible workspaces: muted "No workspace" placeholder // - 1 accessible workspace: workspace name as static text // - >1 accessible workspaces: dropdown button + menu with click-to-switch export function renderWorkspaceSwitcher(me) { const container = document.getElementById('workspaceSwitcher'); if (!container) return; const list = Array.isArray(me?.accessible_workspaces) ? me.accessible_workspaces : []; const currentId = me?.current_workspace_id || null; if (list.length === 0) { container.classList.remove('open'); container.innerHTML = `No workspace`; return; } if (list.length === 1) { container.classList.remove('open'); container.innerHTML = `${esc(list[0].name)}`; return; } // >1: dropdown. Alpha sort by workspace name for MVP (no recently-used yet). const sorted = [...list].sort((a, b) => a.name.localeCompare(b.name)); const current = sorted.find(w => w.id === currentId) || sorted[0]; container.innerHTML = `
`; const button = container.querySelector('.workspace-switcher-button'); button.addEventListener('click', (e) => { e.stopPropagation(); const opening = !container.classList.contains('open'); container.classList.toggle('open'); button.setAttribute('aria-expanded', String(opening)); }); // 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); }); }); container.querySelectorAll('.workspace-switcher-item').forEach(item => { item.addEventListener('click', async (e) => { // Ignore clicks that originated on the pencil (it has its own handler). if (e.target.closest('.workspace-switcher-pencil')) return; const wsId = item.dataset.workspaceId; if (wsId === currentId) { container.classList.remove('open'); return; } try { const resp = await api.switchWorkspace(wsId); if (resp?.token) { localStorage.setItem('token', resp.token); window.location.reload(); } else { showToast('Switch returned no token', 'error'); } } catch (err) { showToast(err.message || 'Failed to switch workspace', 'error'); } }); }); // Click-outside closes the menu. document.addEventListener('click', (e) => { if (!container.contains(e.target)) { container.classList.remove('open'); button.setAttribute('aria-expanded', 'false'); } }); } function esc(s) { return String(s ?? '').replace(/[&<>"']/g, c => ({ '&':'&','<':'<','>':'>','"':'"',"'":''' }[c])); }