Skip to main content

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

  1. Nowhere to type. Every interaction starts with New thread → Specialist picker → chat → compose.
  2. 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 thread and Video call with {Specialist}. Both land below the fold.
  3. The briefing doesn't brief. "In progress" and "Resolved this week" need scrolling to reach.
  4. The headline band can't be acted on. "Today's briefing" + "N things for you" costs ~90 px.
  5. + New thread appears twice, both in filled brand.

The moves

  • Composer at the top, addressed to the Specialist, with attach/voice and a To {name} · change control replacing the W-1 picker interruption. The starter chips reuse specialistSuggestionPrompts, 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 thread steps 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 (uploadChatAttachment needs 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 /conversations now returns lastMessagePreview / lastMessageRole / lastMessageAt — transient, read-time fields populated the same way as agentAvatarUrl, with no change to any scoping or IDOR guard in findAll. Internal, held-for-review and Expert↔AI consultation rows are excluded from the preview (#1621).
  • handleSend takes an optional explicit target conversation, because handleNew sets selectedId via 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 getMessages resolving empty. That race predates this work and also affected a fast "New thread → type → send".