From 7742b583c3a5c95fd57280e9191f2a03330906b0 Mon Sep 17 00:00:00 2001 From: Thomas Joise Date: Sun, 19 Jul 2026 19:08:02 +0530 Subject: [PATCH] fix(ui): group each client module into its own collapsible coloured section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- apps/hq-web/src/app.css | 12 +++++++++++ apps/hq-web/src/pages/ClientDetail.tsx | 29 +++++++++++++++++--------- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/apps/hq-web/src/app.css b/apps/hq-web/src/app.css index 18ce189..5d313d6 100644 --- a/apps/hq-web/src/app.css +++ b/apps/hq-web/src/app.css @@ -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; } diff --git a/apps/hq-web/src/pages/ClientDetail.tsx b/apps/hq-web/src/pages/ClientDetail.tsx index 8d0df8c..a9bf323 100644 --- a/apps/hq-web/src/pages/ClientDetail.tsx +++ b/apps/hq-web/src/pages/ClientDetail.tsx @@ -293,17 +293,26 @@ export function ClientDetail() { })} /> )} - {(cms.data ?? []).length > 0 &&

Service data

} + {(cms.data ?? []).length > 0 &&

Service details by module

} {(cms.data ?? []).map((cm) => ( - - m.id === cm.moduleId)?.fieldSpec ?? []} - onChanged={() => cms.reload()} - /> - - + // Everything of one module (service data + onboarding checklist) sits inside its + // own coloured, collapsible section — a client with many modules stays scannable. +
+ + {moduleName(cm.moduleId)} + {cm.status} + {KIND_LABEL[cm.kind]} + +
+ m.id === cm.moduleId)?.fieldSpec ?? []} + onChanged={() => cms.reload()} + /> + +
+
))} )}