fix(ui): group each client module into its own collapsible coloured section

On Client 360 → Modules, a client with many modules was one long flat stack of service
cards + checklists — hard to scroll and find, say, RTGS. Now each module is its own
collapsible section with a coloured accent header (module name + status + kind); all of
that module's data lives inside it. Click a header to collapse/expand. Headings stand
out so you can jump straight to the module you want.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
feat/client-detail-redesign
Thomas Joise 2 days ago
parent 6939115f6c
commit 7742b583c3

@ -153,6 +153,18 @@
.wf-pager { display: flex; align-items: center; gap: 10px; margin-top: 10px; flex-wrap: wrap; font-size: 13px; color: var(--text-dim); }
.wf-pager > span:first-child { margin-right: auto; }
/* Per-module collapsible section on Client 360 Modules: coloured header, everything of
one module lives inside it, so a client with many modules stays scannable. */
.mod-section { border: 1px solid var(--border); border-radius: var(--radius); margin-top: 12px; overflow: hidden; background: var(--bg-raised); }
.mod-section > summary { list-style: none; cursor: pointer; display: flex; align-items: center; gap: 10px;
padding: 10px 14px; background: var(--accent-soft); color: var(--accent-strong); font-weight: 700;
border-left: 4px solid var(--accent); user-select: none; }
.mod-section > summary::-webkit-details-marker { display: none; }
.mod-section > summary::before { content: '▸'; font-size: 12px; transition: transform .15s ease; opacity: .8; }
.mod-section[open] > summary::before { transform: rotate(90deg); }
.mod-section > summary .mod-name { flex: 1; font-size: 14px; letter-spacing: .2px; }
.mod-section-body { padding: 10px 14px 14px; }
/* Section header shared by the queue block and the bento cards */
.dash-head { display: flex; align-items: center; gap: 10px; padding: 2px 0 10px; }
.dash-head h3 { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.07em; color: var(--text-dim); margin: 0; }

@ -293,9 +293,17 @@ export function ClientDetail() {
})}
/>
)}
{(cms.data ?? []).length > 0 && <h3 style={{ marginTop: 20 }}>Service data</h3>}
{(cms.data ?? []).length > 0 && <h3 style={{ marginTop: 20 }}>Service details by module</h3>}
{(cms.data ?? []).map((cm) => (
<Fragment key={cm.id}>
// Everything of one module (service data + onboarding checklist) sits inside its
// own coloured, collapsible section — a client with many modules stays scannable.
<details key={cm.id} open className="mod-section">
<summary>
<span className="mod-name">{moduleName(cm.moduleId)}</span>
<Badge tone={cm.status === 'live' ? 'ok' : undefined}>{cm.status}</Badge>
<Badge>{KIND_LABEL[cm.kind]}</Badge>
</summary>
<div className="mod-section-body">
<ServiceDataCard
cm={cm}
name={moduleName(cm.moduleId)}
@ -303,7 +311,8 @@ export function ClientDetail() {
onChanged={() => cms.reload()}
/>
<MilestoneChecklist clientModuleId={cm.id} name={moduleName(cm.moduleId)} />
</Fragment>
</div>
</details>
))}
</>
)}

Loading…
Cancel
Save