COMPONENT_INVENTORY.md โ h.work
Every UI component with location, role usage, props/states, and design notes. Cross-reference: VIEWS_BY_ROLE.md, FRONTEND_ARCHITECTURE.md, DESIGN_PRINCIPLES.md. Last updated: 2026-05-04.
ADR-046 correction (2026-08-05): Conversation components render canonical SessionDB rows by exact Hermes locator, deliver success directly, apply scoped events, and never poll, project, release held drafts, or content/time-deduplicate. Components marked legacy below are removal inventory, not supported design. See the remediation ledger.
Note (2026-05-03): Expert pool UI (
PoolCard,/ops/pools,/workspace/pools) is disabled pending redesign on the new direct Expert โ Org model (pool tables removed).PortalChat.tsxnow usesEXPERT_ROLESfrom/client/PortalChat.tsx./portal/*prefix is gone โ all client routes use/client/*.Note (2026-05-25 doc refresh): Added 17 missing components; marked
PoolCardandWhatsAppQrModaldeprecated. shadcn/ui primitives inui/tracked viacomponents.json(shadcn convention) โ lowercase filenames, PascalCase exported names. See theui/section for the additive list. Update (2026-06-01): Pools are now fully dead per ADR-007 + CD-22 (seeVIEWS_BY_ROLE.md).PoolCardsection below retained as historical reference but the component should not be used in new code. Newer components added below:NotificationBell(#915),ReconsentBanner(#1068/#1141),DraftEditModal(#979), and a Framer Motion animations conventions row (#859). Last updated: 2026-06-02.
Layout: components are organized into 5 directories under frontend/src/components/:
| Directory | Owner | Role-gating allowed |
|---|---|---|
ui/ | Design system | No โ pure primitives |
shared/ | Any | No โ must be role-agnostic |
client/ | Client portal team | Yes โ client-only logic |
internal/ | Expert / AM / Superadmin team | Yes โ operator-only logic |
analytics/ | Analytics surfaces (cross-role) | No |
workspace/ | Expert workspace queue | Yes โ expert-only |
runtime/ | Agent runtime surfaces | Yes โ operator-only |
ops/kb/ | Ops KB management | Yes โ AM / SuperAdmin |
brand/ | Brand assets | No |
Plus root-level: components/Nav.tsx, components/UserFooter.tsx, components/IntegrationStatus.tsx, components/ImpersonationBanner.tsx, components/slug-availability-input.tsx.
1. ui/ โ primitives (design system)
These have no business logic and import only from utilities. They are the lowest layer.
Buttonโ
- File:
components/ui/Button.tsx - Used by: All roles (Account Manager wizard, login, integrations, etc.)
- Props/states:
variant: "primary" | "secondary" | "ghost" | "danger",size: "sm" | "md" | "lg",loading,disabled,children, native button props. - Design notes:
- Primary uses
--accentbg + white text; hover--accent-hover; disabled 0.5 opacity. - Secondary uses
--bg-surface+--border+--text-primary; hover--bg-hover. - Ghost has no background; hover
--bg-hover. - Danger uses
--dangersemantic. - Heights: sm 28px, md 36px, lg 44px (touch target).
- 8px border-radius.
- Primary uses
Badgeโ
- File:
components/ui/Badge.tsx - Used by: All roles (status pills, role tags, etc.)
- Props/states:
variant: "default" | "success" | "warning" | "danger" | "info" | "neutral",children. - Design notes:
- 10โ11px font, 600 weight, uppercase, 0.04em tracking.
- 2px vertical / 6โ8px horizontal padding, 4px radius.
- Each variant uses paired
--{variant}-subtlebg +--{variant}`` text. - Do not use raw hex โ always tokens.
Avatarโ
- File:
components/ui/Avatar.tsx - Used by: All roles
- Props/states:
name,src?,size: number(default 32),color?(override). - Design notes:
- Falls back to deterministic initial avatar using a hash of the name โ background color from a curated 8-tone palette.
- The Specialist persona always uses
--accent(claret) regardless of name โ this is the workspace identity. - Image avatars are object-cover, circle-clipped.
- Online dot is a child element, not part of Avatar (callers add it).
TicketStatusBadgeโ
- File:
components/ui/TicketStatusBadge.tsx - Used by: Client (read-only), Expert (interactive variant lives elsewhere)
- Props/states:
status: "pending" | "awaiting_client" | "resolved" | "snoozed"(canonicalConversationStatus;"open"is accepted as a legacy alias ofpending). Noarchivedโ collapsed intoresolved(#2002). See conversation-status-lifecycle. - Design notes:
- Color mapping: pending โ warning, awaiting_client โ info, resolved โ neutral, snoozed โ muted.
- 11px font, 20px pill radius.
2. shared/ โ role-agnostic utilities
ChannelBadgeโ
- File:
components/shared/ChannelBadge.tsx - Used by: All roles wherever a conversation appears (PortalChat header, queue list, ticket detail)
- Props/states:
channel: "web" | "email" | "whatsapp" | "slack" | "telegram" | "wechat" | "teams". - Design notes:
- Uses
ChannelIconSVG + label. - Each channel has a brand-tinted bg + text via tokens (not raw hex). E.g. WhatsApp: success-subtle bg + success-fg text.
- 11px font, 4px radius, 2/8 padding.
- Uses
ChannelIconโ
- File:
components/shared/ChannelIcon.tsx - Used by:
ChannelBadge, integration tiles - Props/states:
channel,size. - Design notes: SVG, currentColor. Use semantically (no embedded color).
RelativeTimeโ
- File:
components/shared/RelativeTime.tsx - Used by: Queue rows, message metadata, ticket lists
- Props/states:
timestamp,format?: "short" | "full". - Design notes: tabular-nums for alignment,
--text-mutedcolor, 11โ12px.
Skeletonโ
- File:
components/shared/Skeleton.tsx - Used by: Queue list loading state, ticket detail loading state, dashboard loading
- Props/states:
width,height,variant?: "text" | "rect" | "circle". - Design notes:
--bg-hoverbg, 4โ8px radius, optional pulse animation.
Toastโ
- File:
components/shared/Toast.tsx - Used by: All roles (mounted at root via
Toaster) - Props/states:
toast.success(msg),toast.error(msg|Error),toast.info(msg). Auto-dismiss 4s. - Design notes:
- Bottom-right stacked, max-width 400.
- Each toast: surface bg + 1px border + 4px left bar in semantic color (success/danger/info/warning).
- Slide-up + fade-in animation (
animate-slide-up). - Click dismisses.
ErrorBoundaryโ
- File:
components/shared/ErrorBoundary.tsx - Used by: Wraps each top-level layout
- Props/states:
fallback?: ReactNode. - Design notes: Friendly fallback UI ("Something went wrong โ refresh") with claret retry button.
DarkModeProviderโ
- File:
components/shared/DarkModeProvider.tsx - Used by: Mounted in
app/layout.tsx; consumed byUserFooterand any component callinguseTheme() - Props/states:
theme: "light" | "dark",toggle(). Persisted inlocalStorage.hw_theme. Sets[data-theme]attribute on<html>. - Design notes: System preference (
prefers-color-scheme: dark) honored on first visit when no stored value.
ServiceWorkerRegistrarโ
- File:
components/shared/ServiceWorkerRegistrar.tsx - Used by: Mounted in root layout
- Props/states: none (side-effect only).
- Design notes: Registers
/sw.js. Hooks for push notification subscription are stubbed (E-P3 gap).
AutoreplyThresholdโ
- File:
components/shared/AutoreplyThreshold.tsx - Status: legacy compatibility component; remove it from managed-delivery settings
- Props/states:
value: number(0โ101 scale),onChange: (v: number) => void. - Design notes: the stored number is compatibility/analytics metadata only and defaults to 0. It must not be labelled as a review/auto-send control or influence successful managed delivery.
AvatarUploadโ
- File:
components/shared/AvatarUpload.tsx - Used by: Specialist config, AM profile edit
- Props/states:
currentUrl: string | null,initials: string,onUpload: (url: string) => void,uploadType: UploadType. - Design notes: Square avatar with hover overlay (camera icon). Click opens hidden
<input type="file">. CallsuploadAvatar()API. ShowsAvatarfallback (initials) whencurrentUrlis null.
FormFieldโ
- File:
components/shared/FormField.tsx - Used by: All forms in wizard flows and settings pages
- Props/states:
label,required?,hint?,state: FieldState,children, optional trailing icon slot. - Design notes: Wraps
<label>+ child input + optionalInfoTooltip+ validation message.FieldStatedrives border color (idle/valid/invalid/checking). Conditional-wrapper pitfall: wrapper always renders โ do not conditionally render<div>vschildrenor controlled inputs lose in-flight keystrokes on state flip (React unmounts on tree position change).
SetupStepHeaderโ
- File:
components/shared/SetupStepHeader.tsx - Used by: AM New Client wizard steps 1โ4
- Props/states:
stepNumber: number,totalSteps?: number,title: string,subtitle: string. - Design notes: Eyebrow (step N of M, muted), heading (
--text-primary, font-display), subtitle (muted). Consistent across all 4 AM wizard steps.
StepProgressBarโ
- File:
components/shared/StepProgressBar.tsx - Used by: AM New Client wizard header
- Props/states:
currentStep: number(0-based),totalSteps?: number. - Design notes: Horizontal 4-step indicator (
WIZARD_LABELS/WIZARD_LABELS_5). Active step uses--accentfill; completed =--success; pending =--border. Label text 11px muted.
Tooltipโ
- File:
components/shared/Tooltip.tsx - Used by:
FormField(viaInfoTooltip), any surface needing hover/tap explainers - Props/states:
content: string | ReactNode,position?: "top" | "bottom" | "left" | "right",maxWidth?: number,children. - Design notes: Desktop: hover-triggered. Mobile: tap-to-toggle.
InfoTooltipis a pre-wired?icon variant exported from the same file. Portal-rendered viauseIdanchor to avoid overflow clipping issues.
3. client/ โ client portal
PortalChatโ
- File:
components/client/PortalChat.tsx(~1,500 LOC) - Used by:
/client/chat,/client/chat/[id] - Props/states:
initialId?: string. Internal state:conversations,selectedId,messages,selectedExpertId,sidebarOpen,threadNames. - Design notes:
- Two main subcomponents inside:
Sidebar,ChatPanel(+ExpertProfile,MessageBubble,WorkforceEmptyState,WorkerAvatar). - Sidebar:
--bg-sidebar, 256px wide, three sections (workspace header, Specialists, Threads),UserFooterat bottom. - Bubble shape: outgoing claret
18px 18px 4px 18px, incoming bg-surface with 1px border4px 18px 18px 18px. - Role gating lives here:
EXPERT_ROLES = ["expert", "account_manager", "superadmin"].- Internal notes filtered for non-experts (
if (msg.isInternal && !isExpertViewer) return null). - Status dropdown vs read-only badge:
isExpert ? <ExpertChatOverlay/> : <span/>. - Status, assignment, internal-note, exact-locator correction, and genuine follow-up controls are authorization-gated. Canonical Specialist messages retain Specialist identity; there is no held-draft release or confidence control.
- Real-time: Socket.io live update for the open thread; built-in reconnect handles transient disconnects; reconnect banner surfaces sustained drops; no polling fallback (removed in #843).
- Active thread row:
--accent-subtlebg + 3px--accentleft border. - Hover:
--bg-hover.
- Two main subcomponents inside:
PortalNavโ
- File:
components/client/PortalNav.tsx - Used by: All
/client/*non-chat routes (tickets, settings) - Props/states: stateless except
pathnamefromusePathname(). - Design notes:
- Top bar layout, claret avatar logo + workspace name, nav links (Chat / Tickets / Settings), user avatar right.
- Hidden on
/client/chat(full-screen layout).
TrialBannerโ
- File:
components/client/TrialBanner.tsx - Used by: Top of
/client/*(except chat full-screen) - Props/states: hooks
GET /client/trial-info. Internal:dismissedThisSession. - Design notes:
- Sticky top,
--warning-subtlebg +--warningleft bar, 32px tall. - Copy: "Your trial ends in N days. Add billing to keep service. [Add billing โ]"
- Dismissible per session (sessionStorage flag).
- Sticky top,
ChannelConfigFormโ
- File:
components/client/ChannelConfigForm.tsx - Used by:
/client/settings/channels/new,/client/settings/channels/[id] - Props/states:
channel,initialConfig,onSave. - Design notes: Per-channel form (token, webhook URL, whitelist editor). Test-inbound button. Save / Disconnect.
IntegrationCardโ
- File:
components/client/IntegrationCard.tsx - Used by:
/client/settings/channels - Props/states:
channel,connected,health: "healthy" | "warning" | "error" | "unknown",onClick. - Design notes: Card 220px tall, channel icon + name + connection status pill + last-event timestamp. Connected uses
--bg-surface, available uses--bg-canvas+ dashed border.
TestResultโ
- File:
components/client/TestResult.tsx - Used by: Integration detail page (test inbound feature)
- Props/states:
result: { status: "success" | "fail", message, latency, payload }. - Design notes: Inline result panel, semantic color left bar + monospace JSON preview.
WhatsAppPairingPanelโ
- File:
components/client/WhatsAppPairingPanel.tsx - Used by: Client WhatsApp settings (
/client/settings/integrations) - Props/states: none (self-contained via API calls).
- Design notes: Replaces the deprecated
WhatsAppQrModal. Shows linked numbers, generates Twilio pairing codes, receives a provider/mutation event when pairing completes, and offers an explicit status refresh after reconnect. Idle time must not run a five-second status loop.
ThreadListItemโ
- File:
components/client/ThreadListItem.tsx - Used by:
PortalChatsidebar thread list - Props/states:
conversation: Conversation,selected: boolean,onClick: () => void,editingId?,onRename?,onDelete?. - Design notes: Shows thread name (or fallback from message preview), channel dot indicator (emoji/color per channel), relative timestamp. Hover reveals inline rename/delete action icons.
SlackConnectCardโ
- File:
components/client/SlackConnectCard.tsx - Used by: Client integrations page
- Props/states: self-contained (reads org context from auth).
- Design notes: Shows Slack webhook URL in a copyable code block. Form to test the integration (
testIntegrationAPI). Expandable "what is this?" explainer. UsesChannelIconfor Slack brand treatment.
WhatsAppQrModalโ
- File:
components/client/WhatsAppQrModal.tsx - Used by: WhatsApp integration setup (legacy Baileys path)
- Props/states:
open,qrDataUrl,onClose. - Design notes: Centered modal, QR centered, instructions below, "Refresh QR" button.
- โ ๏ธ DEPRECATED โ Baileys WhatsApp service removed per ADR-002. WhatsApp is now Twilio-only using pairing codes (see
WhatsAppPairingPanel). File kept for back-compat reference only; pending deletion in a follow-up chore.
4. internal/ โ operator surfaces (expert / AM / superadmin)
QueueItemโ
- File:
components/internal/QueueItem.tsx - Used by:
/workspace/queue,/ops/queue - Props/states:
item: QueueItem,selected,onClick,currentUserId?. - Design notes:
- 64px min-height row.
- Left risk bar 3px (theme-aware bar token: critical โ danger, high/medium โ warning, low โ teal).
- Selected overrides bar with
--accent, bg becomes--accent-subtle. - Customer name (13/500), two-line message preview (12/secondary), genuine
attention/failure state,
AssigneePill, andChannelBadge. No confidence or reply-review state is rendered. - Hover:
--bg-hover.
TicketDetailโ
- File:
app/workspace/queue/TicketDetail.tsx(lives at the route, not ininternal/, but functionally an internal component) - Used by:
/workspace/queue/[id],/ops/queueright pane - Props/states:
item: QueueItem,onResolved. - Design notes:
- Header: exact Hermes locator, channel badge, and product-task status.
- Direct canonical SessionDB thread plus genuine failure/native audit evidence.
- Actions: correct the exact delivered locator, send a genuine follow-up, add an internal note, defer, reassign, or resolve. There is no Accept/Release action for an ordinary Hermes reply.
Internal-collab thread rendered inline, dashed amber border.(REMOVED โ Collab gone; the only internal surface is the "+ Internal note" composer that posts into the same conversation. Seedocs/features/collab.md.)- Risk badge bg/text uses theme-aware
--risk-*tokens.
ExpertChatOverlayโ
- File:
components/internal/ExpertChatOverlay.tsx - Used by: Inside
PortalChat.tsx(header) whenisExpert === true - Props/states:
conversation,userId,onStatusChange,onAssign. - Design notes:
- Status dropdown (
<select>styled to match design system). - "Assign to me" button.
- Renders only when the viewer has expert role.
- Status dropdown (
ConfidenceBarโ
- File:
components/internal/ConfidenceBar.tsx - Status: legacy component pending deletion. It must not appear in managed message delivery, queue admission, correction, or tool availability.
ResolveFormโ
- File:
components/internal/ResolveForm.tsx - Used by: Ticket detail "Resolve" action (modal)
- Props/states:
ticketId,onResolved, optional correction fields. - Design notes:
- Modal with: resolution outcome (radio), optional correction notes (textarea), error-type select (E-L2 โ currently a gap).
- Primary "Mark resolved", secondary "Cancel".
ConversationHistoryโ
- File:
components/internal/ConversationHistory.tsx - Used by: Expert ticket detail, superadmin org detail
- Props/states:
conversationId,paginate?. - Design notes: Renders authorized canonical SessionDB rows once, in locator order, plus access-controlled product metadata and internal notes. It must not construct projections or hide rows using content/time matching.
StatusBadgeโ
- File:
components/internal/StatusBadge.tsx - Used by: Queue rows, ticket detail header, superadmin org list
- Props/states:
status(lifecycle status: pending / awaiting_client / resolved / snoozed; noarchivedโ collapsed intoresolved, #2002). See conversation-status-lifecycle. - Design notes:
- Pill, 11px font, 4px radius.
- Each status has a paired
--{semantic}-subtlebg +--{semantic}`` text.
LiveBadgeโ
- File:
components/internal/LiveBadge.tsx - Used by: Live-online indicators (e.g. "Online ยท Support Specialist" in chat header) โ though that specific copy is currently inline
- Props/states:
status: "online" | "away" | "offline". - Design notes:
- 8px green dot with
animate-pulse-livefor online (1.5s pulse). - Inline-flex, 6px gap to label.
- 8px green dot with
PoolCardโ
- File:
components/internal/PoolCard.tsx - Used by:
/workspace/pools,/ops/pools(legacy โ routes removed) - Props/states:
pool: { id, name, domain, memberCount, queueDepth, slaPct },onOpen. - Design notes: Card 200px tall, header (name + domain badge), body stats, footer "View โ" link.
- โ ๏ธ DEPRECATED (2026-06-01) โ pool concept removed (CD-22 โ ADR-007). Routes
/workspace/poolsand/ops/poolsno longer exist. Component retained only for git history reference; pending deletion. Do not import.
NotificationBellโ
- File:
components/shared/NotificationBell.tsx(#915) - Pipeline: backed by the AppNotification REST + Socket.io pipeline โ see features/notifications.md for the delivery model, event names, and producers.
- Status (2026-06-05): DEPRECATED on client shell; scoped to non-client shells only. Replaced on
/client/*by the dedicated/client/inboxroute (#1866, commitb20b36c0). Still used by Expert (/workspace/*) and Ops (/ops/*) top-nav layouts. - Used by: Expert + Ops authenticated layouts (top-nav). NOT used in
/client/*layouts โ those now link to/client/inboxinstead. - Props/states: pulls from
AppNotificationfeed via/v1/notifications; renders unread count badge + dropdown list. - Design notes:
- 20px bell icon; unread count pill in
--danger-subtlebg /--dangertext. - Dropdown 360px wide, lazy-loaded list, "Mark all read" footer action.
- Real-time updates via Socket.io
notification:newevent.
- 20px bell icon; unread count pill in
ReconsentBannerโ
- File:
components/client/ReconsentBanner.tsx(#1068, #1141) - Used by:
/client/*layout when org policy version has bumped and member hasn't re-consented. - Props/states:
policyVersion,onAccept,onView. - Design notes:
- Sticky banner at top of viewport,
--warning-subtlebg. - Two CTAs: "View changes" (link) + "I agree" (primary button). Dismissible only via accept.
- Sticky banner at top of viewport,
DraftEditModalโ
- File:
components/internal/DraftEditModal.tsx(#979) - Used by: legacy surface pending replacement by canonical after-delivery correction/follow-up editing.
- Props/states:
draft,onSave,onCancel; tracks diff vs original for audit. - Design notes:
- Modal 720px wide, textarea autoresizes, character count footer.
- Diff preview records the intentional correction; it must not become a pre-delivery gate.
Framer Motion animations (conventions)โ
- Introduced: #859 (2026-05 motion pass).
- Where used: Drawer/Sheet enter/exit, ticket-status transitions, NotificationBell dropdown, ReconsentBanner reveal.
- Conventions:
- Standard ease:
[0.4, 0, 0.2, 1](Material standard), duration 180ms (micro) / 240ms (modal). - Reduced-motion: respect
prefers-reduced-motionโ fall back to opacity-only fades, no translate. - No layout-shifting animations on the chat thread (jank risk during streaming).
- Standard ease:
AutoReplyConfigCardโ
- File:
components/internal/AutoReplyConfigCard.tsx - Used by: AM org config, SuperAdmin settings
- Props/states: self-contained (reads/writes org auto-reply config via
getAutoReplyConfig/updateAutoReplyConfigAPIs). - Design notes: Mode selector (
agent_reply,system_reply,none) with radio-style options + descriptions.system_replyreveals a template textarea (max 2000 chars). Inline save with success/error feedback.
ConfidenceIndicatorโ
- File:
components/internal/ConfidenceIndicator.tsx - Status: legacy managed-review component pending deletion. A compatibility score cannot gate, relabel, or create a second representation of a canonical message.
CorrectionCategorySelectโ
- File:
components/internal/CorrectionCategorySelect.tsx - Used by:
ResolveFormcorrection fields - Props/states:
value: string,onChange: (v: string) => void,categories: CategoryOption[],placeholder?. - Design notes: Styled replacement for native
<select>. Keyboard navigable (โโ Enter Esc). Design-system token colours. Popup list usesz-indexabove modal; managed open/close on outside click.
Nav (legacy)โ
- File:
components/Nav.tsx - Used by:
/integrations,/dashboard(legacy routes pre-role-split) - Status: Will be deprecated as those routes migrate to role-prefixed equivalents.
5. analytics/ โ chart components (used in any analytics surface)
SummaryCardsโ
- File:
components/analytics/SummaryCards.tsx - Used by:
/workspace/analytics,/ops/analytics(the client/portal/admin/analyticsroute was retired in the 2026-05-02 refactor) - Props/states:
cards: { label, value, delta?, trend? }[]. - Design notes:
- 4-up grid, each card: label (uppercase 10/700 muted), value (24/700), delta (12 with semantic color).
- Border 1px subtle, 12px radius.
VolumeChartโ
- File:
components/analytics/VolumeChart.tsx - Used by: All analytics surfaces
- Props/states:
data: { date, value }[],range. - Design notes: Line chart, 240px tall. Stroke
--accent, fill--accent-subtle. X-axis ticks 11/muted.
AiVsExpertChartโ
- File:
components/analytics/AiVsExpertChart.tsx - Used by: All analytics surfaces (esp. C-V4)
- Props/states:
data: { date, ai, expert }[]. - Design notes: Stacked bar chart. AI =
--info, Expert =--accent. Legend top-right.
ConfidenceHistogramโ
- File:
components/analytics/ConfidenceHistogram.tsx - Status: legacy review-contract analytics pending deletion; it is not a managed-delivery metric.
ResponseTimeChartโ
- File:
components/analytics/ResponseTimeChart.tsx - Used by: All analytics surfaces
- Props/states:
data: { date, p50, p95 }[]. - Design notes: Two-line chart. P50
--info, P95--warning. Y-axis seconds/minutes.
StatusBreakdownChartโ
- File:
components/analytics/StatusBreakdownChart.tsx - Used by: All analytics surfaces
- Props/states:
breakdown: { status, count }[]. - Design notes: Donut chart. Each slice uses status-paired token (open/success, pending/warning, etc.). Center label = total count.
TopOrgsTableโ
- File:
components/analytics/TopOrgsTable.tsx - Used by:
/ops/analytics - Props/states:
orgs: { id, name, slug, ticketCount, mrr, plan }[]. - Design notes: Standard table, 13px body. Row hover
--bg-hover. Click โ/ops/clients/[id].
AnalyticsFiltersโ
- File:
components/analytics/AnalyticsFilters.tsx - Used by: All analytics surfaces
- Props/states:
range,onRangeChange, optionalorg,pool,expertfilters. - Design notes: Right-aligned filter row above the dashboard. Date-range picker presets (7d / 30d / 90d / Custom).
shared.tsxโ
- File:
components/analytics/shared.tsx - Used by: Internal helpers for the analytics charts
- Design notes: Common axes, tooltip styling, color tokens. Centralized to keep all charts consistent.
6. Root-level
UserFooterโ
- File:
components/UserFooter.tsx - Used by: PortalChat sidebar, Portal admin sidebar. (The unified
/ops/*operator surface and/workspace/*expert surface use the top-nav avatar instead โ see INFORMATION_ARCHITECTURE.md ยง7.) - Props/states: reads JWT (
hw_token) for name/email/initials/platformRole. UsesuseTheme()for the toggle. - Design notes:
- 12px top padding, 8px gap stack.
- Avatar 32px claret circle with initials.
- Name 12/600 primary, email 11/muted.
- Theme toggle 16px icon (sun in dark, moon in light), hover
--text-secondary+--bg-hover. - Footer links 11/muted, role-aware (Expert sees "My Profile", admin sees "Account Settings"+"Billing", etc.).
- "Sign out" 11/muted button.
IntegrationStatusโ
- File:
components/IntegrationStatus.tsx - Used by: Integration detail pages (currently legacy)
- Status: Keeping for compatibility; new code should use
IntegrationCard+ a fresh detail layout.
slug-availability-inputโ
- File:
components/slug-availability-input.tsx - Used by:
/ops/clients/new(Step 1 โ Company Info) - Props/states:
value,onChange,onValidityChange. Internal: debounced API check. - Design notes:
- Input with live validation: โ green when available, โ red when taken/invalid, spinner while checking.
- Inline preview to the right: ``{value}
.h.workin muted monospace. - Reserved-words list and personal-domain list enforced client-side; server is the source of truth.
7. workspace/ โ expert workspace queue components
Card (workspace)โ
- File:
components/workspace/Card.tsx - Used by: Queue list items, ticket panels, expert workspace surfaces
- Props/states:
children,variant?,style?. - Design notes: Standardizes 8px border-radius for cards, 6px for buttons, 4px for badges. Uses
cardTokensfor background/border tokens.
RiskBadgeโ
- File:
components/workspace/RiskBadge.tsx - Used by: Queue rows, ticket detail header
- Props/states:
level: string | null | undefined,showBar?: boolean,style?. - Design notes: Unified risk-badge replacing duplicated inline implementations. 10px/700 uppercase text, 4px radius.
showBar=trueadds a 3px left border. Token source:RiskTokens.ts(getRiskTokens(level)).
RiskTokens (utility)โ
- File:
components/workspace/RiskTokens.ts - Used by:
RiskBadge,QueueItem - Design notes: Exports
getRiskTokens(level): { bg, text, bar }andRiskLeveltype. Single color source for risk levels: critical โ danger, high/medium โ warning, low โ teal, null โ neutral.
8. ui/ additions โ custom-built primitives
File-casing: shadcn-derived primitives use lowercase filenames (
card.tsx,dialog.tsx); custom-built ones use PascalCase. Both export PascalCase names. shadcn/ui primitives tracked viacomponents.json.
ActionsDropdownโ
- File:
components/ui/ActionsDropdown.tsx - Used by: Data table rows, list action menus
- Props/states:
items: ActionItem[](label, onClick, icon?, variant?). - Design notes: Three-dot
EllipsisVerticaltrigger. Keyboard-navigable.variant: "danger"items render in--dangertext.
DataTableโ
- File:
components/ui/DataTable.tsx - Used by: All ops list views (clients, specialists, experts, invoices)
- Props/states:
columns: Column<T>[],data: T[],emptyMessage?,loading?,onRowClick?. - Design notes: Sortable columns (click header to cycle asc/desc/none). Empty state via
EmptyState. Uses--border,--bg-hovertokens.
StatCardโ
- File:
components/ui/StatCard.tsx - Used by: Analytics summary, ops dashboard
- Props/states:
label,value,hint?,loading?,badge?. - Design notes: Surface-bg card, 2px border, 8px radius. Label 11px muted; value font-display 24px; optional badge slot top-right.
UserStatusBadgeโ
- File:
components/ui/UserStatusBadge.tsx - Used by: User list rows, expert profile headers
- Props/states:
status: string. - Design notes:
Badgewrapper โ active โ success, invited โ warning, suspended โ danger, else โ default.
EmptyState (empty-state.tsx)โ
- File:
components/ui/empty-state.tsx - Used by:
DataTable, any zero-data surface - Props/states:
icon?,title,body?,description?,action?,className?. - Design notes: Centered card with optional icon, bold title, muted body, optional CTA slot.
PageHeader (page-header.tsx)โ
- File:
components/ui/page-header.tsx - Used by: All ops and workspace page tops
- Props/states:
eyebrow?,title,subtitle?,action?: ReactNode. - Design notes: Two variants โ
"page"(text-3xl) and"section"(text-xl).actionright-aligned.
9. Root-level additions
ImpersonationBannerโ
- File:
components/ImpersonationBanner.tsx - Used by: All authenticated layouts (root)
- Props/states: none (self-contained via
/auth/impersonation-infoAPI). - Design notes: Amber banner pinned at top during SuperAdmin impersonation. Shows impersonated name, role, "End Impersonation" CTA.
10. ops/kb/ โ ops KB management components
KbAssignmentNavโ
- File:
components/ops/kb/KbAssignmentNav.tsx - Used by:
/ops/kb/[orgId]/[specialistId]layout - Design notes: Sidebar nav for per-(org ร Specialist) KB management.
KbDocumentListโ
- File:
components/ops/kb/KbDocumentList.tsx - Used by: Ops KB management page
- Props/states:
orgId,specialistId. - Design notes: Paginated list of KB documents. Delete + re-upload actions.
KbUploadDropzoneโ
- File:
components/ops/kb/KbUploadDropzone.tsx - Used by: Ops KB management page
- Props/states:
orgId,specialistId,onUploaded. - Design notes: Drag-and-drop or click-to-upload. Accepts PDF, DOCX, TXT. Progress indicator.
11. brand/ โ brand assets
Logoโ
- File:
components/brand/Logo.tsx - Used by: Navigation bars, login, onboarding
- Props/states:
size?,variant?: "full" | "icon". - Design notes: h.work wordmark.
variant="icon"= standalone icon only.--text-primaryfill (theme-aware).
12. Open issues per component (cross-reference IMPLEMENTATION_STATUS.md)
| Component | Issue |
|---|---|
PortalChat | Remove any residual held-review/confidence UI; preserve explicit human requests and separately configured human-owned action decisions. Search input is not in the sidebar (C-W6). |
TicketDetail | Defer action shipped (#1012 โ Snooze โ Defer copy + flow). Reassign UI not yet shipped (E-R11; legacy expert-pool reassign predates ADR-007 expert_access; see USER_FLOWS Flow 5). |
ResolveForm | No error-type select (E-L2). |
ServiceWorkerRegistrar | Push notification subscription is stubbed (E-P3). |
LiveBadge | Online/away/offline only โ no "in another conversation" state. |
PoolCard | No detail page yet โ clicking goes nowhere meaningful for non-superadmin. โ ๏ธ Deprecated โ see above. |
WhatsAppQrModal | Deprecated โ Baileys path removed. Replaced by WhatsAppPairingPanel. Pending deletion. |
Nav (legacy) | To be deleted once /integrations and /dashboard migrate. |
| Theme toggle | Sun/moon icons live inline in UserFooter; consider extracting <ThemeToggle/> to standardize. |
Owner: Design + Frontend team