diff --git a/apps/hq-web/src/pages/Modules.tsx b/apps/hq-web/src/pages/Modules.tsx index 81210d8..e8c1cd6 100644 --- a/apps/hq-web/src/pages/Modules.tsx +++ b/apps/hq-web/src/pages/Modules.tsx @@ -27,6 +27,8 @@ export function Modules() { const modules = useData(getModules, []) const [creating, setCreating] = useState(false) const [selected, setSelected] = useState() + const [editingId, setEditingId] = useState() // inline "Edit module" form + const editingModule = modules.data?.find((m) => m.id === editingId) // How many clients sit on each module — one lightweight roster count per module, // best-effort so a slow/failed one never blocks the table (cell shows '…' until then). const [clientCounts, setClientCounts] = useState>({}) @@ -46,7 +48,7 @@ export function Modules() {
setCreating(true)}>New module : read-only (staff)} @@ -60,6 +62,7 @@ export function Modules() { { key: 'clients', label: 'Clients', numeric: true }, { key: 'sac', label: 'SAC' }, { key: 'kinds', label: 'Billing kinds' }, { key: 'multi', label: 'Multi-sub' }, { key: 'active', label: 'Active' }, + { key: 'act', label: '' }, ]} onRowClick={(_row, i) => setSelected(modules.data![i])} rows={modules.data.map((m) => ({ @@ -68,12 +71,18 @@ export function Modules() { kinds: m.allowedKinds.map((k) => KIND_LABEL[k]).join(' · '), multi: m.multiSubscription ? yes : '—', active: m.active ? active : inactive, + act: isOwner + ? e.stopPropagation()}> + : null, }))} /> )} + {editingModule !== undefined && ( + modules.reload()} onClose={() => setEditingId(undefined)} /> + )} {selected !== undefined && ( <> - modules.reload()} /> modules.reload()} /> modules.reload()} /> @@ -319,7 +328,7 @@ function ModuleClients(props: { module: Module }) { /** Edit a module's core details (owner only): name, SAC, billing kinds, multi-sub, active. * Code is the identity and is not editable. Field-spec / quote-content / prices have their * own panels below. */ -function ModuleDetails(props: { module: Module; isOwner: boolean; onSaved: () => void }) { +function ModuleDetails(props: { module: Module; isOwner: boolean; onSaved: () => void; onClose: () => void }) { const toast = useToast() const m = props.module const [name, setName] = useState(m.name) @@ -339,7 +348,7 @@ function ModuleDetails(props: { module: Module; isOwner: boolean; onSaved: () => if (kinds.length === 0) { setError('Pick at least one billing kind'); return } setError(undefined); setBusy(true) patchModule(m.id, { name: name.trim(), sac: sac.trim(), allowedKinds: kinds, multiSubscription: multi, active }) - .then(() => { toast.ok('Module saved'); props.onSaved() }) + .then(() => { toast.ok('Module saved'); props.onSaved(); props.onClose() }) .catch((e: Error) => setError(e.message)) .finally(() => setBusy(false)) } @@ -369,6 +378,7 @@ function ModuleDetails(props: { module: Module; isOwner: boolean; onSaved: () =>
+ {error !== undefined && {error}}