Client homebase redesign — /client/chat
Proposal for the client portal's landing screen — the BriefingPanel view in
frontend/src/components/client/PortalChat.tsx.
It's the first screen a client sees after login and where they're meant to
interact with their Specialist.
Open mockup.html in a browser. Palette, radii and type are the
live tokens from
frontend/src/app/design-tokens.css,
so it's buildable without new design decisions. Light mode only.
What's wrong today
- Nowhere to type. Every interaction starts with New thread → Specialist picker → chat → compose.
- The rail is a dossier before it's a control panel. Avatar → name → title →
email → bio → tags → six capabilities → five integrations, and only then
+ New threadandVideo call with {Specialist}. Both land below the fold. - The briefing doesn't brief. "In progress" and "Resolved this week" need scrolling to reach.
- The headline band can't be acted on. "Today's briefing" + "N things for you" costs ~90 px.
+ New threadappears twice, both in filled brand.
The moves
- Composer at the top, addressed to the Specialist, with attach/voice and a
To {name} · changecontrol replacing the W-1 picker interruption. The starter chips reusespecialistSuggestionPrompts, which exists today but only renders in the zero-thread empty state. - Three counting tiles replace the greeting sentence and filter the list below.
- "Needs your input" stays hoisted and amber — never behind a tab — and rows now carry the question the Specialist is blocked on. Cap of 5, "See more" and per-row Dismiss all kept.
- One work card with segments merges "In progress" and "Resolved this week" so both sit above the fold, plus a last-activity line per row.
- Rail reordered: identity + both buttons in the top ~200 px. Video call takes
the filled treatment (it has no other entry point anywhere in the product);
+ New threadsteps down to outline. The dossier survives underneath as three disclosures — Overview open, the two lists collapsed with counts. - Sidebar unchanged, except Approvals and "Message on…" pair onto one row.
Nothing removed
Every current element has a home. One trade-off is explicit: "Resolved this week" becomes one click instead of always visible — its count stays on screen and it moves from below the fold to above it. A permanently-expanded stacked variant of the same card is available if preferred.
As built
Shipped in BriefingPanel and new components under
frontend/src/components/client/briefing/, on the repo's own UI primitives
(Card, Button, Badge, Textarea, Tabs, Avatar, EmptyState).
Two things the mockup showed that changed in the build:
- No voice note. The mockup had a mic button; there is no voice-recording
capability anywhere in the product, and inventing one is a separate piece of
work. The attach button is real — files are staged in the composer and
uploaded once the thread exists (
uploadChatAttachmentneeds an{orgId, convId}pair that has no value before then). - The primary CTA is brand claret, not
--primary.variant="default"renders near-black, which read as a second, differently-coloured primary next to the sidebar's claret "New thread".
Supporting changes:
GET /conversationsnow returnslastMessagePreview/lastMessageRole/lastMessageAt— transient, read-time fields populated the same way asagentAvatarUrl, with no change to any scoping or IDOR guard infindAll. Internal, held-for-review and Expert↔AI consultation rows are excluded from the preview (#1621).handleSendtakes an optional explicit target conversation, becausehandleNewsetsselectedIdvia state the send closure cannot observe in the same tick.- The per-thread message load now merges rather than replaces, so a message
sent immediately after thread creation is not dropped by the initial
getMessagesresolving empty. That race predates this work and also affected a fast "New thread → type → send".