Skip to main content

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.

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.tsx now uses EXPERT_ROLES from /client/PortalChat.tsx. /portal/* prefix is gone β€” all client routes use /client/*.

Note (2026-05-25 doc refresh): Added 17 missing components; marked PoolCard and WhatsAppQrModal deprecated. shadcn/ui primitives in ui/ tracked via components.json (shadcn convention) β€” lowercase filenames, PascalCase exported names. See the ui/ section for the additive list. Update (2026-06-01): Pools are now fully dead per ADR-007 + CD-22 (see VIEWS_BY_ROLE.md). PoolCard section 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/:

DirectoryOwnerRole-gating allowed
ui/Design systemNo β€” pure primitives
shared/AnyNo β€” must be role-agnostic
client/Client portal teamYes β€” client-only logic
internal/Expert / AM / Superadmin teamYes β€” operator-only logic
analytics/Analytics surfaces (cross-role)No
workspace/Expert workspace queueYes β€” expert-only
runtime/Agent runtime surfacesYes β€” operator-only
ops/kb/Ops KB managementYes β€” AM / SuperAdmin
brand/Brand assetsNo

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 --accent bg + 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 --danger semantic.
    • Heights: sm 28px, md 36px, lg 44px (touch target).
    • 8px border-radius.

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}-subtle bg + --{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" (canonical ConversationStatus; "open" is accepted as a legacy alias of pending). No archived β€” collapsed into resolved (#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 ChannelIcon SVG + 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.

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-muted color, 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-hover bg, 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 by UserFooter and any component calling useTheme()
  • Props/states: theme: "light" | "dark", toggle(). Persisted in localStorage.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
  • Used by: AM setup wizard Step 3, SuperAdmin org config
  • Props/states: value: number (0–101 scale), onChange: (v: number) => void.
  • Design notes: 4-step radio selector mapping human labels to internal threshold integers β€” Never=101, Sometimes=90, Often=75, Always=50. Labels are human-readable; internal scale is numeric (101 = never auto-send). Inline with the ConfidenceBar threshold marker.

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">. Calls uploadAvatar() API. Shows Avatar fallback (initials) when currentUrl is 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 + optional InfoTooltip + validation message. FieldState drives border color (idle/valid/invalid/checking). Conditional-wrapper pitfall: wrapper always renders β€” do not conditionally render <div> vs children or 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 --accent fill; completed = --success; pending = --border. Label text 11px muted.

Tooltip​

  • File: components/shared/Tooltip.tsx
  • Used by: FormField (via InfoTooltip), 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. InfoTooltip is a pre-wired ? icon variant exported from the same file. Portal-rendered via useId anchor 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), UserFooter at bottom.
    • Bubble shape: outgoing claret 18px 18px 4px 18px, incoming bg-surface with 1px border 4px 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/>.
      • Approval banner / confidence scores / "Expert" sender label / internal note toggle: all isExpert gated.
    • 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-subtle bg + 3px --accent left border.
    • Hover: --bg-hover.

PortalNav​

  • File: components/client/PortalNav.tsx
  • Used by: All /client/* non-chat routes (tickets, settings)
  • Props/states: stateless except pathname from usePathname().
  • 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-subtle bg + --warning left bar, 32px tall.
    • Copy: "Your trial ends in N days. Add billing to keep service. [Add billing β†’]"
    • Dismissible per session (sessionStorage flag).

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 list of linked numbers; generates Twilio pairing codes (polling createWhatsAppPairingCode). Copy-to-clipboard, 5s poll for new-number confirmation, unlink with confirmation. Manages local state: pairing codes, linked numbers list.

ThreadListItem​

  • File: components/client/ThreadListItem.tsx
  • Used by: PortalChat sidebar 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 (testIntegration API). Expandable "what is this?" explainer. Uses ChannelIcon for 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), 2-line message preview (12/secondary), badge row (risk badge with --risk-* tokens, confidence %, AssigneePill, ChannelBadge).
    • Hover: --bg-hover.

TicketDetail​

  • File: app/workspace/queue/TicketDetail.tsx (lives at the route, not in internal/, but functionally an internal component)
  • Used by: /workspace/queue/[id], /ops/queue right pane
  • Props/states: item: QueueItem, onResolved.
  • Design notes:
    • Header: id, risk badge, channel badge, status dropdown.
    • Customer thread + AI suggestion panel with ConfidenceBar.
    • Action bar: Accept & Send / Edit / Ignore & Write / Defer / Reassign / Resolve.
    • 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. See docs/features/collab.md.)
    • Risk badge bg/text uses theme-aware --risk-* tokens.

ExpertChatOverlay​

  • File: components/internal/ExpertChatOverlay.tsx
  • Used by: Inside PortalChat.tsx (header) when isExpert === 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.

ConfidenceBar​

  • File: components/internal/ConfidenceBar.tsx
  • Used by: AI suggestion panel in TicketDetail, message metadata for experts
  • Props/states: confidence: number (0–100).
  • Design notes:
    • 6px tall horizontal bar.
    • Color: 0–49 danger, 50–69 warning, 70–84 info, 85+ success.
    • Numeric label right-aligned next to the bar (12/600 tabular-nums).
    • Threshold marker (vertical line) at the org's configured threshold.

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: Same MessageBubble rendering as PortalChat but full-history scrollable view; expert-mode by default (shows confidence, internal notes).

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; no archived β€” collapsed into resolved, #2002). See conversation-status-lifecycle.
  • Design notes:
    • Pill, 11px font, 4px radius.
    • Each status has a paired --{semantic}-subtle bg + --{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-live for online (1.5s pulse).
    • Inline-flex, 6px gap to label.

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/pools and /ops/pools no 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/inbox route (#1866, commit b20b36c0). 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/inbox instead.
  • Props/states: pulls from AppNotification feed via /v1/notifications; renders unread count badge + dropdown list.
  • Design notes:
    • 20px bell icon; unread count pill in --danger-subtle bg / --danger text.
    • Dropdown 360px wide, lazy-loaded list, "Mark all read" footer action.
    • Real-time updates via Socket.io notification:new event.

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-subtle bg.
    • Two CTAs: "View changes" (link) + "I agree" (primary button). Dismissible only via accept.

DraftEditModal​

  • File: components/internal/DraftEditModal.tsx (#979)
  • Used by: Expert workspace draft review surface β€” opens when the Expert clicks "Edit" on an AI draft before sending.
  • Props/states: draft, onSave, onCancel; tracks diff vs original for audit.
  • Design notes:
    • Modal 720px wide, textarea autoresizes, character count footer.
    • Diff preview tab (red/green inline) before final send.

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).

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 / updateAutoReplyConfig APIs).
  • Design notes: Mode selector (agent_reply, system_reply, none) with radio-style options + descriptions. system_reply reveals a template textarea (max 2000 chars). Inline save with success/error feedback.

ConfidenceIndicator​

  • File: components/internal/ConfidenceIndicator.tsx
  • Used by: Expert ticket review panels, message metadata
  • Props/states: score: number (0–100), className?.
  • Design notes: Horizontal bar + "XX% confidence" label. Color: β‰₯80 β†’ --success, 50–79 β†’ --warning, <50 β†’ --danger. Inline-flex. Companion to ConfidenceBar (which is thinner and label-less).

CorrectionCategorySelect​

  • File: components/internal/CorrectionCategorySelect.tsx
  • Used by: ResolveForm correction 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 uses z-index above modal; managed open/close on outside click.
  • 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/analytics route 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
  • Used by: /workspace/analytics, /ops/analytics
  • Props/states: bins: number[].
  • Design notes: 10 bins (0-9, 10-19, …, 90-100). Each bar colored by confidence band (same scale as ConfidenceBar).

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, optional org, pool, expert filters.
  • 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. Uses useTheme() 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.work in 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 cardTokens for 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=true adds 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 } and RiskLevel type. 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 via components.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 EllipsisVertical trigger. Keyboard-navigable. variant: "danger" items render in --danger text.

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-hover tokens.

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: Badge wrapper β€” 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). action right-aligned.

9. Root-level additions

ImpersonationBanner​

  • File: components/ImpersonationBanner.tsx
  • Used by: All authenticated layouts (root)
  • Props/states: none (self-contained via /auth/impersonation-info API).
  • 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

  • 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-primary fill (theme-aware).

12. Open issues per component (cross-reference IMPLEMENTATION_STATUS.md)

ComponentIssue
PortalChatApproval banner is expert-only; client variant deferred to P2 (C-W8 demoted 2026-05-23 β€” manual approval via chat for MVP). Search input not in sidebar (C-W6).
TicketDetailDefer 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).
ResolveFormNo error-type select (E-L2).
ServiceWorkerRegistrarPush notification subscription is stubbed (E-P3).
LiveBadgeOnline/away/offline only β€” no "in another conversation" state.
PoolCardNo detail page yet β€” clicking goes nowhere meaningful for non-superadmin. ⚠️ Deprecated β€” see above.
WhatsAppQrModalDeprecated β€” Baileys path removed. Replaced by WhatsAppPairingPanel. Pending deletion.
Nav (legacy)To be deleted once /integrations and /dashboard migrate.
Theme toggleSun/moon icons live inline in UserFooter; consider extracting <ThemeToggle/> to standardize.

Owner: Design + Frontend team