import { useState, type ReactNode } from 'react'; import { CURRENCIES, LOCALE_LABELS, SUPPORTED_LOCALES, currencySymbol, isLocale, } from '@wellformed/shared'; import { api } from '../api/client'; import { useI18n } from '../i18n'; import { useSession } from '../state/session'; import { useToasts } from '../state/toasts'; import { goTo, workspacePath } from '../state/route'; import { Modal } from './Modal'; /** * Settings for the open workspace: what it is called, where it lives, how to * get the data out, and how to leave or end it. * * Export is a plain link rather than a fetch because the response is a file — * letting the browser do what it is good at beats holding a database in a blob. */ export function WorkspaceDialog({ onClose }: { onClose(): void }): ReactNode { const session = useSession(); const toasts = useToasts(); const { t } = useI18n(); const workspace = session.workspace!; const isAdmin = session.can('admin'); const [name, setName] = useState(workspace.name); const [slug, setSlug] = useState(workspace.slug); const [currency, setCurrency] = useState(workspace.currency); const [locale, setLocale] = useState(workspace.locale); const [confirm, setConfirm] = useState(''); const [busy, setBusy] = useState(false); // A workspace on a currency the list does not carry keeps it, and keeps it // selectable: the dropdown is a shortcut, not the set of allowed answers. const currencies = CURRENCIES.some((c) => c.code === workspace.currency) ? CURRENCIES : [{ code: workspace.currency, name: workspace.currency }, ...CURRENCIES]; const changed = name.trim() !== workspace.name || slug !== workspace.slug || currency !== workspace.currency || locale !== workspace.locale; async function save() { setBusy(true); try { const { workspace: updated } = await api.updateWorkspace({ ...(name.trim() !== workspace.name ? { name: name.trim() } : {}), ...(slug !== workspace.slug ? { slug } : {}), ...(currency !== workspace.currency ? { currency } : {}), ...(locale !== workspace.locale ? { locale } : {}), }); // The address is part of the URL, so a changed slug means this page is // now at the wrong one. if (updated.slug !== workspace.slug) { goTo(workspacePath(updated.slug)); return; } await session.refresh(); toasts.push(t('workspace.updated'), 'success'); onClose(); } catch (error) { toasts.fail(error); } finally { setBusy(false); } } async function leave() { if (!window.confirm(t('workspace.leaveConfirm', workspace.name))) return; try { await api.leaveWorkspace(); goTo('/'); } catch (error) { toasts.fail(error); } } async function destroy() { setBusy(true); try { await api.deleteWorkspace(workspace.slug); goTo('/'); } catch (error) { toasts.fail(error); setBusy(false); } } return ( {isAdmin && ( )} } >
{isAdmin && ( <>

{t('workspace.exportHead')}

{t('workspace.exportHint')}

{t('workspace.download')} )}

{t('workspace.leavingHead')}

{t('workspace.leavingHint')}

{isAdmin && ( <>

{t('workspace.deleteHead')}

{t('workspace.deleteHintBefore')} {workspace.slug} {t('workspace.deleteHintAfter')}

setConfirm(e.target.value)} placeholder={workspace.slug} aria-label={t('workspace.deleteConfirmAria')} />
)}
); }