@handled-ai/design-system 0.20.16 → 0.20.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/comment-composer.d.ts +1 -3
- package/dist/components/comment-composer.js +4 -8
- package/dist/components/comment-composer.js.map +1 -1
- package/dist/components/rich-text-toolbar.d.ts +9 -2
- package/dist/components/rich-text-toolbar.js +75 -16
- package/dist/components/rich-text-toolbar.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/prototype/prototype-inbox-view.js +14 -28
- package/dist/prototype/prototype-inbox-view.js.map +1 -1
- package/package.json +1 -1
- package/src/components/__tests__/comment-composer.test.tsx +0 -15
- package/src/components/__tests__/rich-text-toolbar.test.tsx +28 -1
- package/src/components/comment-composer.tsx +4 -18
- package/src/components/rich-text-toolbar.tsx +77 -13
- package/src/prototype/__tests__/detail-view-case-panel-v2.test.tsx +0 -18
- package/src/prototype/__tests__/detail-view-timeline-system-events.test.tsx +0 -52
- package/src/prototype/prototype-inbox-view.tsx +18 -41
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/prototype/prototype-inbox-view.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport {\n ArrowLeft,\n ArrowUpDown,\n ChevronDown,\n ChevronRight,\n Filter,\n FileText,\n Clock,\n CheckSquare,\n Eye,\n Plus,\n\n Building,\n LayoutList,\n Columns2,\n Square,\n Tag,\n} from \"lucide-react\"\n\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuTrigger,\n} from \"../components/dropdown-menu\"\nimport { Button } from \"../components/button\"\nimport { Badge } from \"../components/badge\"\nimport { Input } from \"../components/input\"\nimport { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from \"../components/tooltip\"\nimport { ViewModeToggle } from \"../components/view-mode-toggle\"\nimport {\n InboxToolbar,\n type AssigneeFilter,\n type InboxFilterCategory,\n} from \"../components/inbox-toolbar\"\nimport { GroupedListView, type GroupedListGroup } from \"../components/item-list\"\nimport { SignalApproval, type ApprovalState, type OpportunityDraft, type OpportunityPreview } from \"../components/signal-feedback-inline\"\nimport { ScoreWhyChips } from \"../components/score-why-chips\"\nimport { SignalPriorityPopover } from \"../components/signal-priority-popover\"\nimport { type SourceDef } from \"../components/detail-view\"\nimport {\n SuggestedActions,\n type SuggestedAction,\n type SuggestedContact,\n} from \"../components/suggested-actions\"\nimport { TimelineActivity, type TimelineEvent } from \"../components/timeline-activity\"\nimport { cn } from \"../lib/utils\"\nimport type {\n QueueItem,\n InboxViewConfig,\n InboxDetailSections,\n SignalScoreData,\n BriefStyleVariant,\n TimelineSystemEventsConfig,\n} from \"./prototype-config\"\n\n// ---------------------------------------------------------------------------\n// Dot color utility — maps statusColor to a Tailwind background class.\n// Centralised here so the list row and split-view card stay in sync and\n// future color additions are a single-edit change.\n// ---------------------------------------------------------------------------\n\nfunction dotColorClass(statusColor: string): string {\n if (statusColor === \"red\") return \"bg-[#f43f5e]\"\n if (statusColor === \"yellow\" || statusColor === \"amber\" || statusColor === \"orange\") return \"bg-[#eab308]\"\n if (statusColor === \"green\") return \"bg-[#22c55e]\"\n return \"bg-[#9ca3af]\"\n}\n\n// ---------------------------------------------------------------------------\n// Props\n// ---------------------------------------------------------------------------\n\nexport interface PrototypeInboxViewProps extends InboxViewConfig {\n /** Extra ReactNode rendered at the end of the header bar (e.g. exit button). */\n headerActions?: React.ReactNode\n onOpenEntityPanel?: () => void\n onOpenRecentActivity?: () => void\n onNavigateToInbox?: () => void\n onItemSelect?: (item: QueueItem) => void\n defaultViewMode?: \"list\" | \"split\"\n}\n\n// ---------------------------------------------------------------------------\n// Default detail sections\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_DETAIL_SECTIONS: InboxDetailSections = {\n signalBrief: true,\n suggestedActions: true,\n timeline: true,\n}\n\nconst DEFAULT_SIGNAL_SCORE: SignalScoreData = {\n score: 65,\n factors: [\n { key: \"trigger\", label: \"Trigger strength\", score: 70, why: \"Moderate signal detected based on account activity\" },\n { key: \"fit\", label: \"Company fit\", score: 65, why: \"Reasonable fit based on company profile\" },\n { key: \"timing\", label: \"Timing\", score: 58, why: \"Within general evaluation window\" },\n ],\n whyNow: \"Moderate signals detected that warrant review and potential outreach.\",\n evidence: [\n \"Activity patterns suggest potential opportunity\",\n \"Company profile aligns with target segment\",\n ],\n confidence: 72,\n}\n\n// ---------------------------------------------------------------------------\n// Detail View\n// ---------------------------------------------------------------------------\n\n/** Up to 2-character uppercase initials for a company name (e.g. \"Northwind Systems\" -> \"NW\"). */\nfunction getCompanyInitials(company: string): string {\n const words = company.trim().split(/\\s+/).filter(Boolean)\n if (words.length === 0) return \"?\"\n if (words.length === 1) return words[0].slice(0, 2).toUpperCase()\n return (words[0][0] + words[1][0]).toUpperCase()\n}\n\n/** Maps a QueueItem.statusColor to a Tailwind dot background class. */\nconst STATUS_DOT_CLASS: Record<string, string> = {\n red: \"bg-red-500\",\n orange: \"bg-orange-500\",\n amber: \"bg-amber-500\",\n yellow: \"bg-yellow-500\",\n green: \"bg-emerald-500\",\n emerald: \"bg-emerald-500\",\n blue: \"bg-blue-500\",\n gray: \"bg-gray-300\",\n grey: \"bg-gray-300\",\n}\n\nexport interface DetailViewProps {\n item: QueueItem\n sections: InboxDetailSections\n getSignalScore: (company: string, item?: QueueItem) => SignalScoreData\n buildSuggestedActions: (item: QueueItem) => SuggestedAction[]\n buildSourceItems: (item: QueueItem) => SourceDef[]\n getTimelineEvents?: (item: QueueItem) => TimelineEvent[]\n accountContacts: SuggestedContact[]\n emailSignature: string | React.ReactNode\n iconMap: Record<string, string>\n onOpenEntityPanel?: () => void\n onOpenRecentActivity?: () => void\n onSuggestedActionFeedback?: (actionId: number | string, feedback: string, actionTitle?: string) => void\n /** @deprecated The compact score UX no longer renders score-level thumbs by default. */\n onScoreFeedback?: (type: \"up\" | \"down\", pills: string[], detail: string) => void\n onSignalApprove?: (item: QueueItem, draft?: OpportunityDraft) => void | Promise<boolean>\n getSignalApprovalState?: (item: QueueItem) => ApprovalState | undefined\n signalLabels?: InboxViewConfig[\"signalLabels\"]\n hideApproveButton?: boolean\n signalBriefCopy?: InboxViewConfig[\"signalBriefCopy\"]\n briefStyleVariant?: BriefStyleVariant\n /** Opt-in detail panel section ordering. Defaults to the existing layout. */\n sectionLayout?: InboxViewConfig[\"sectionLayout\"]\n renderDetailExtra?: (item: QueueItem) => React.ReactNode\n /** Render content between the signal brief text and the signal score bar (e.g. \"Signals on Case\" chips). */\n renderBeforeScore?: (item: QueueItem) => React.ReactNode\n /** Render content between the signal score section and the activity timeline. */\n renderAfterScore?: (item: QueueItem) => React.ReactNode\n /** Render primary case-panel content between the opportunity section and comment area. */\n renderPrimaryAction?: (item: QueueItem) => React.ReactNode\n /** Render case-panel comment content before the activity timeline. */\n renderCommentArea?: (item: QueueItem) => React.ReactNode\n lastActivityTime?: string\n /** Render extra content inline with the detail title. */\n renderTitleExtra?: (item: QueueItem) => React.ReactNode\n /** Render a full-width action row below the detail title row. */\n renderTitleActionRow?: (item: QueueItem) => React.ReactNode\n /** Render supporting content below the detail title. */\n renderTitleSubtext?: (item: QueueItem) => React.ReactNode\n /** Render extra metadata chips (e.g. assignee) inside the chips row below the title. */\n renderMetadataExtra?: (item: QueueItem) => React.ReactNode\n /**\n * Where the metadata chips row (priority · deadline · account · renderMetadataExtra) sits.\n * - \"default\": above the signal brief (legacy layout, unchanged for existing consumers).\n * - \"below-brief\": moved beneath the brief text, just before `renderBeforeScore`\n * (case-panel redesign — chips + owners read as one block under the brief).\n */\n metadataLayout?: \"default\" | \"below-brief\"\n /** Override the built-in account details metadata button label. */\n accountDetailsButtonLabel?: (item: QueueItem) => React.ReactNode\n /** Accessible label for the built-in account details metadata button. */\n getAccountDetailsButtonAriaLabel?: (item: QueueItem) => string\n onOpenSignalBucket?: (args: { item: QueueItem; bucketKey: string; signalId: string }) => void\n approveButtonIconUrl?: string\n opportunityPreview?: OpportunityPreview\n onRequestApproval?: () => Promise<void>\n /** Number of important/attention-worthy events to highlight on the collapsed timeline header. */\n attentionCount?: number\n /** Configuration for the system-noise events toggle (score changes, etc.). */\n timelineSystemEventsConfig?: TimelineSystemEventsConfig\n\n // ── Deprecated individual props (use timelineSystemEventsConfig instead) ──\n /** @deprecated Use `timelineSystemEventsConfig.toggleLabel`. */\n timelineSystemEventsToggleLabel?: string\n /** @deprecated Use `timelineSystemEventsConfig.storageKey`. */\n timelineSystemEventsStorageKey?: string\n /** @deprecated Use `timelineSystemEventsConfig.defaultVisible`. */\n timelineSystemEventsDefaultVisible?: boolean\n /** @deprecated Use `timelineSystemEventsConfig.hiddenHint`. */\n timelineSystemEventsHiddenHint?: string\n /** @deprecated Use `timelineSystemEventsConfig.visibleHint`. */\n timelineSystemEventsVisibleHint?: string\n}\n\n// ---------------------------------------------------------------------------\n// TimelineSection — extracted from the IIFE in DetailView for readability\n// ---------------------------------------------------------------------------\n\nfunction TimelineSection({\n timelineEvents,\n showTimeline,\n setShowTimeline,\n showSystemEvents,\n setShowSystemEvents,\n attentionCount,\n sysEvtConfig,\n lastActivityTime,\n isCasePanel = false,\n}: {\n timelineEvents: TimelineEvent[]\n showTimeline: boolean\n setShowTimeline: React.Dispatch<React.SetStateAction<boolean>>\n showSystemEvents: boolean\n setShowSystemEvents: React.Dispatch<React.SetStateAction<boolean>>\n attentionCount?: number\n sysEvtConfig?: TimelineSystemEventsConfig\n lastActivityTime?: string\n isCasePanel?: boolean\n}) {\n // Single-pass partition: compute visibleEvents and hiddenCount together\n const visibleEvents: TimelineEvent[] = []\n let hiddenCount = 0\n for (const e of timelineEvents) {\n if (e.isSystemNoise) hiddenCount++\n if (!e.isSystemNoise || showSystemEvents) visibleEvents.push(e)\n }\n const hasSystemNoise = hiddenCount > 0\n\n // The toggle renders whenever there are system-noise events — even if no\n // config was provided — so consumers that emit `isSystemNoise: true` always\n // give users a way to reveal those events.\n const toggleLabel = sysEvtConfig?.toggleLabel ?? \"System events\"\n const toggleHelp = showSystemEvents\n ? sysEvtConfig?.visibleHint?.replace(\"{count}\", String(hiddenCount)) ?? \"Hide system events\"\n : sysEvtConfig?.hiddenHint ?? \"Show system events\"\n\n // Derive \"Last activity\" from the first *visible* event so the collapsed\n // header never points at a hidden score-update. The caller-supplied\n // `lastActivityTime` is only used when system-noise filtering is NOT active\n // (i.e. all events are visible) since it may come from an unfiltered source\n // such as `case.last_activity_at`.\n const firstVisibleTime =\n (!hasSystemNoise || showSystemEvents) && lastActivityTime\n ? lastActivityTime\n : visibleEvents.length > 0\n ? visibleEvents[0].time\n : \"\"\n\n const visibleCount = visibleEvents.length\n const eventCountLabel = `${visibleCount} ${visibleCount === 1 ? \"event\" : \"events\"}`\n\n return (\n <div\n className={cn(\n isCasePanel ? \"mt-8 border-t border-border pt-8 pb-8\" : \"mb-8\"\n )}\n >\n {/* Header — outer non-interactive container */}\n <div\n className={cn(\n \"flex w-full items-center justify-between\",\n isCasePanel\n ? \"gap-4 border-b border-border pb-5\"\n : \"group/timeline gap-2 rounded-md py-2 transition-colors hover:bg-muted/40 -mx-2 px-2\"\n )}\n data-testid=\"timeline-header\"\n >\n {/* Left: collapse/expand button */}\n <button\n type=\"button\"\n onClick={() => setShowTimeline((prev) => !prev)}\n className=\"flex min-w-0 cursor-pointer items-center gap-2 border-0 bg-transparent p-0 text-left\"\n data-testid=\"timeline-collapse-btn\"\n >\n <h3 className=\"text-xs font-bold uppercase tracking-[0.16em] text-muted-foreground transition-colors group-hover/timeline:text-foreground\">ACTIVITY TIMELINE</h3>\n {!showTimeline && attentionCount != null && attentionCount > 0 && (\n <span className=\"inline-flex items-center gap-1 rounded-full bg-destructive/10 px-1.5 py-0.5 text-[10px] font-semibold text-destructive border border-destructive/20\">\n {attentionCount} new\n </span>\n )}\n {!isCasePanel && !showTimeline && firstVisibleTime && (\n <span className=\"text-[11px] text-muted-foreground/60\" data-testid=\"last-activity-hint\">\n · Last activity {firstVisibleTime}\n </span>\n )}\n </button>\n\n {/* Right: system-events toggle, event count, and collapse affordance */}\n <div className=\"flex shrink-0 items-center gap-4\">\n {hasSystemNoise && (\n <button\n type=\"button\"\n onClick={() => setShowSystemEvents((prev) => !prev)}\n className={cn(\n \"inline-flex shrink-0 cursor-pointer items-center rounded-full border transition-colors\",\n isCasePanel ? \"gap-2 px-2.5 py-1 text-xs font-medium\" : \"gap-3 px-3.5 py-2 text-sm font-semibold\",\n showSystemEvents\n ? isCasePanel\n ? \"border-border bg-muted text-foreground shadow-sm hover:bg-muted/80\"\n : \"border-foreground bg-foreground text-background shadow-sm hover:bg-foreground/90\"\n : \"border-border bg-background text-muted-foreground shadow-sm hover:bg-muted/40 hover:text-foreground\"\n )}\n aria-pressed={showSystemEvents}\n aria-label={toggleHelp}\n title={toggleHelp}\n data-testid=\"system-events-toggle\"\n >\n <span\n className={cn(\n \"relative inline-flex shrink-0 items-center rounded-full p-0.5 transition-colors\",\n isCasePanel ? \"h-3.5 w-7\" : \"h-4 w-8\",\n showSystemEvents ? \"bg-teal-600\" : \"bg-muted-foreground/25\"\n )}\n aria-hidden=\"true\"\n data-testid=\"system-events-indicator\"\n >\n <span\n className={cn(\n \"block rounded-full bg-white shadow-sm transition-transform\",\n isCasePanel ? \"h-2.5 w-2.5\" : \"h-3 w-3\",\n showSystemEvents ? (isCasePanel ? \"translate-x-3.5\" : \"translate-x-4\") : \"translate-x-0\"\n )}\n />\n </span>\n <span>{toggleLabel}</span>\n {!showSystemEvents ? (\n <span\n className={cn(\"inline-flex items-center justify-center rounded-full bg-muted px-1.5 font-bold tabular-nums text-muted-foreground\", isCasePanel ? \"min-w-5 text-[11px]\" : \"min-w-[22px] text-xs\")}\n data-testid=\"hidden-count-badge\"\n >\n {hiddenCount}\n </span>\n ) : null}\n </button>\n )}\n\n <button\n type=\"button\"\n onClick={() => setShowTimeline((prev) => !prev)}\n className={cn(\n \"inline-flex shrink-0 cursor-pointer items-center border-0 bg-transparent p-0 text-muted-foreground transition-colors hover:text-foreground\",\n isCasePanel ? \"gap-3 text-sm\" : \"gap-1.5 text-[11px]\"\n )}\n aria-label={showTimeline ? \"Collapse activity timeline\" : \"Expand activity timeline\"}\n >\n <span className=\"font-medium\" data-testid=\"event-count\">{eventCountLabel}</span>\n <ChevronDown className={`h-3.5 w-3.5 transition-transform duration-200 ${showTimeline ? \"rotate-180\" : \"\"}`} />\n </button>\n </div>\n </div>\n\n {/* Timeline body */}\n {showTimeline && visibleEvents.length > 0 && (\n <div className=\"mt-6\">\n <TimelineActivity events={visibleEvents} variant=\"case-panel\" />\n </div>\n )}\n </div>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Detail View\n// ---------------------------------------------------------------------------\n\nexport function DetailView({\n item,\n sections,\n getSignalScore,\n buildSuggestedActions,\n buildSourceItems: _buildSourceItems,\n getTimelineEvents,\n accountContacts,\n emailSignature,\n iconMap,\n onOpenEntityPanel,\n onOpenRecentActivity,\n onSuggestedActionFeedback: _onSuggestedActionFeedback,\n onScoreFeedback: _onScoreFeedback,\n onSignalApprove,\n getSignalApprovalState,\n signalLabels,\n hideApproveButton,\n signalBriefCopy,\n briefStyleVariant = \"default\",\n sectionLayout = \"default\",\n renderDetailExtra,\n renderBeforeScore,\n renderAfterScore,\n renderPrimaryAction,\n renderCommentArea,\n lastActivityTime,\n renderTitleExtra,\n renderTitleActionRow,\n renderTitleSubtext,\n renderMetadataExtra,\n metadataLayout = \"default\",\n accountDetailsButtonLabel,\n getAccountDetailsButtonAriaLabel,\n onOpenSignalBucket,\n approveButtonIconUrl,\n opportunityPreview,\n onRequestApproval,\n attentionCount,\n timelineSystemEventsConfig: configProp,\n timelineSystemEventsToggleLabel,\n timelineSystemEventsStorageKey,\n timelineSystemEventsDefaultVisible,\n timelineSystemEventsHiddenHint,\n timelineSystemEventsVisibleHint,\n}: DetailViewProps) {\n // Resolve system-events config: prefer the config object, fall back to deprecated individual props.\n const sysEvtConfig = React.useMemo<TimelineSystemEventsConfig | undefined>(() => {\n if (configProp) return configProp\n // Build from deprecated individual props if any are provided\n if (\n timelineSystemEventsToggleLabel ||\n timelineSystemEventsStorageKey ||\n timelineSystemEventsDefaultVisible !== undefined ||\n timelineSystemEventsHiddenHint ||\n timelineSystemEventsVisibleHint\n ) {\n return {\n toggleLabel: timelineSystemEventsToggleLabel,\n storageKey: timelineSystemEventsStorageKey,\n defaultVisible: timelineSystemEventsDefaultVisible,\n hiddenHint: timelineSystemEventsHiddenHint,\n visibleHint: timelineSystemEventsVisibleHint,\n }\n }\n return undefined\n }, [\n configProp,\n timelineSystemEventsToggleLabel,\n timelineSystemEventsStorageKey,\n timelineSystemEventsDefaultVisible,\n timelineSystemEventsHiddenHint,\n timelineSystemEventsVisibleHint,\n ])\n\n const isCasePanelV2 = sectionLayout === \"case-panel-v2\"\n\n const [showTimeline, setShowTimeline] = React.useState(false)\n const [extraActions, setExtraActions] = React.useState<SuggestedAction[]>([])\n\n // ---- System-noise toggle state ----\n const sysEvtDefaultVisible = sysEvtConfig?.defaultVisible ?? false\n const sysEvtStorageKey = sysEvtConfig?.storageKey\n const [showSystemEvents, setShowSystemEvents] = React.useState(sysEvtDefaultVisible)\n const initialReadDoneRef = React.useRef(false)\n\n // Read persisted value from localStorage on mount. Case-panel timelines always\n // start from the configured default so stale local state cannot make system\n // events prominent by default in the inbox detail panel.\n React.useEffect(() => {\n if (isCasePanelV2) {\n setShowSystemEvents(sysEvtDefaultVisible)\n initialReadDoneRef.current = true\n return\n }\n if (!sysEvtStorageKey) {\n setShowSystemEvents(sysEvtDefaultVisible)\n initialReadDoneRef.current = true\n return\n }\n try {\n const stored = localStorage.getItem(sysEvtStorageKey)\n if (stored !== null) {\n setShowSystemEvents(stored === \"true\")\n } else {\n setShowSystemEvents(sysEvtDefaultVisible)\n }\n } catch {\n // localStorage unavailable — ignore\n setShowSystemEvents(sysEvtDefaultVisible)\n }\n initialReadDoneRef.current = true\n }, [isCasePanelV2, item.id, sysEvtDefaultVisible, sysEvtStorageKey])\n\n // Write to localStorage when the toggle changes (skip case-panel timelines so\n // they stay off by default on the next case/session).\n React.useEffect(() => {\n if (!sysEvtStorageKey || isCasePanelV2) return\n if (!initialReadDoneRef.current) return\n try {\n localStorage.setItem(sysEvtStorageKey, String(showSystemEvents))\n } catch {\n // localStorage unavailable — ignore\n }\n }, [isCasePanelV2, showSystemEvents, sysEvtStorageKey])\n\n React.useEffect(() => {\n setShowTimeline(false)\n setExtraActions([])\n }, [item.id])\n\n const signalData = React.useMemo(\n () => getSignalScore(item.company, item),\n [getSignalScore, item],\n )\n\n const suggestedActions = React.useMemo(\n () => [...buildSuggestedActions(item), ...extraActions],\n [buildSuggestedActions, item, extraActions],\n )\n const timelineEvents = React.useMemo(\n () => getTimelineEvents?.(item) ?? [],\n [getTimelineEvents, item],\n )\n\n const handleDuplicate = React.useCallback(\n (id: number | string) => {\n const base = suggestedActions.find((a) => a.id === id)\n if (!base || base.type !== \"email\") return\n const clone: SuggestedAction = {\n ...base,\n id: `${base.id}-dup-${Date.now()}`,\n emailMeta: base.emailMeta ? { ...base.emailMeta, to: undefined } : undefined,\n }\n setExtraActions((prev) => [...prev, clone])\n },\n [suggestedActions],\n )\n\n // Deadline chip tone (case-panel redesign): amber as the deadline nears, red when due/overdue.\n const timeChipToneClass =\n signalData.timeChipTone === \"red\"\n ? \"border-red-300 bg-red-50 text-red-700 hover:bg-red-50\"\n : signalData.timeChipTone === \"amber\"\n ? \"border-amber-300 bg-amber-50 text-amber-700 hover:bg-amber-50\"\n : \"hover:bg-muted/50\"\n\n // The metadata chips row (priority · deadline · account · renderMetadataExtra). Rendered above\n // the brief by default, or beneath it when `metadataLayout === \"below-brief\"` (case-panel redesign).\n const metadataChips = (\n <>\n <SignalPriorityPopover\n score={signalData.score}\n urgencyLabel={signalData.urgencyLabel}\n urgencyExplanation={signalData.urgencyExplanation ?? signalData.signalBrief}\n scoreDisplay={signalData.priorityScoreDisplay}\n formulaLabel={signalData.priorityFormulaLabel}\n factors={signalData.priorityFactors ?? []}\n metaText={undefined}\n feedbackChips={signalData.priorityFeedbackChips}\n onFeedbackSubmit={signalData.onPriorityFeedback}\n initialFactorFeedback={signalData.initialFactorPopoverFeedback}\n onFactorFeedback={signalData.onFactorFeedback}\n initialPriorityFeedback={signalData.initialPriorityFeedback}\n />\n {signalData.timeChipLabel && (\n <Badge\n variant=\"outline\"\n title={signalData.timeChipDetail ?? undefined}\n className={`transition-colors ${timeChipToneClass}`}\n >\n {signalData.timeChipLabel}\n </Badge>\n )}\n <TooltipProvider delayDuration={300}>\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n onClick={onOpenEntityPanel}\n className=\"group/account ml-1 inline-flex max-w-full items-center gap-2 rounded-md border border-border/60 bg-background px-2 py-1 transition-colors hover:border-border hover:bg-muted/50\"\n aria-label={getAccountDetailsButtonAriaLabel?.(item) ?? `View account details for ${item.company}`}\n >\n <span className=\"flex h-5 w-5 shrink-0 items-center justify-center rounded bg-foreground text-[9px] font-semibold uppercase leading-none text-background\">\n {getCompanyInitials(item.company)}\n </span>\n {accountDetailsButtonLabel ? (\n <span className=\"inline-flex min-w-0 items-center gap-1.5 text-xs font-medium text-foreground\">\n {accountDetailsButtonLabel(item)}\n </span>\n ) : (\n <span className=\"truncate text-xs font-medium text-foreground\">{item.company}</span>\n )}\n <span className=\"flex shrink-0 items-center gap-1.5\">\n <span\n aria-hidden\n className={`h-1.5 w-1.5 shrink-0 rounded-full ${STATUS_DOT_CLASS[item.statusColor] ?? \"bg-muted-foreground/40\"}`}\n />\n {/* \"View account\" expands in on hover via an animated grid column */}\n <span className=\"-ml-1.5 grid grid-cols-[0fr] transition-all duration-200 ease-out group-hover/account:ml-0 group-hover/account:grid-cols-[1fr]\">\n <span className=\"overflow-hidden\">\n <span className=\"block whitespace-nowrap text-xs font-medium text-muted-foreground/80\">\n View account\n </span>\n </span>\n </span>\n <ChevronRight className=\"h-3 w-3 shrink-0 text-muted-foreground/50 transition-transform duration-150 group-hover/account:translate-x-0.5 group-hover/account:text-muted-foreground\" />\n </span>\n </button>\n </TooltipTrigger>\n <TooltipContent>Open account panel</TooltipContent>\n </Tooltip>\n </TooltipProvider>\n {renderMetadataExtra?.(item)}\n </>\n )\n\n const timelineSection = sections.timeline && timelineEvents.length > 0 ? (\n <TimelineSection\n timelineEvents={timelineEvents}\n showTimeline={showTimeline}\n setShowTimeline={setShowTimeline}\n showSystemEvents={showSystemEvents}\n setShowSystemEvents={setShowSystemEvents}\n attentionCount={attentionCount}\n sysEvtConfig={sysEvtConfig}\n lastActivityTime={lastActivityTime}\n isCasePanel={isCasePanelV2}\n />\n ) : null\n\n const suggestedActionsSection = sections.suggestedActions ? (\n <SignalApproval.Gate>\n <SuggestedActions\n actions={suggestedActions}\n accountContacts={accountContacts}\n signature={emailSignature}\n iconMap={iconMap}\n onDismiss={(id) => console.log(\"Dismiss action:\", id)}\n onSend={(id) => console.log(\"Send action:\", id)}\n onSaveDraft={(id) => console.log(\"Save draft:\", id)}\n onDuplicate={handleDuplicate}\n onOpenAccountDetails={onOpenEntityPanel}\n onOpenRecentActivity={onOpenRecentActivity}\n onMarkComplete={(id) => console.log(\"Mark complete:\", id)}\n onDispatchAgent={(id) => console.log(\"Dispatch agent:\", id)}\n />\n </SignalApproval.Gate>\n ) : null\n\n return (\n <SignalApproval.Root\n key={item.id}\n companyName={item.company}\n labels={signalLabels}\n hideApproveButton={hideApproveButton}\n approveButtonIconUrl={approveButtonIconUrl}\n opportunityPreview={opportunityPreview}\n onRequestApproval={onRequestApproval}\n initialApprovalState={getSignalApprovalState?.(item)}\n onApprove={(draft) => onSignalApprove?.(item, draft)}\n onApproveFeedback={(reasons, detail) => {\n signalData.onApproveFeedback?.(reasons, detail)\n console.log(\"Approval feedback:\", { taskId: item.id, company: item.company, reasons, detail })\n }}\n onDismiss={(reasons, detail, subReason) => {\n signalData.onDismissFeedback?.(reasons, detail, subReason)\n }}\n >\n <div className=\"mx-auto w-full max-w-3xl p-6 pb-12 md:p-8\">\n <div className=\"pb-8\">\n {/* Header */}\n <div className=\"mb-4 flex items-center gap-2\">\n <span className=\"text-xs text-muted-foreground\">{item.company}</span>\n </div>\n\n <div className=\"mb-3 space-y-3\">\n <div className=\"flex flex-wrap items-start gap-x-3 gap-y-2\">\n <div className=\"min-w-0 flex-1\">\n <h1 className=\"text-2xl font-bold tracking-tight text-foreground\">{item.title}</h1>\n {renderTitleSubtext?.(item)}\n </div>\n {renderTitleExtra?.(item)}\n </div>\n {renderTitleActionRow ? (\n <div className=\"flex w-full flex-wrap items-center gap-2\">\n {renderTitleActionRow(item)}\n </div>\n ) : null}\n </div>\n\n {!isCasePanelV2 && metadataLayout === \"default\" ? (\n <div className=\"mb-6 flex flex-wrap items-center gap-2\">{metadataChips}</div>\n ) : null}\n\n {/* Signal Brief */}\n {sections.signalBrief && (() => {\n const briefHeading = signalBriefCopy?.heading ?? \"Signal brief\"\n const introOpt = signalBriefCopy?.intro\n const briefIntro =\n introOpt === null\n ? null\n : typeof introOpt === \"function\"\n ? introOpt(item)\n : introOpt ?? `Signals indicate a potential opportunity for ${item.company}.`\n const isProminent = briefStyleVariant === \"prominent\";\n return (\n <div className=\"mb-8\">\n {briefHeading ? (\n <h3 className=\"text-xs font-bold text-muted-foreground uppercase tracking-wider mb-3\">{briefHeading}</h3>\n ) : null}\n {briefIntro && !signalData.signalBrief ? (\n <p className={isProminent\n ? \"text-sm text-foreground/70 leading-relaxed mb-2\"\n : \"text-sm text-muted-foreground leading-relaxed mb-2\"\n }>\n {briefIntro}\n </p>\n ) : null}\n {signalData.signalBrief ? (\n <p className={isProminent\n ? \"text-base text-foreground leading-relaxed mb-4\"\n : \"text-sm text-foreground/90 leading-relaxed mb-4\"\n }>\n {signalData.signalBrief}\n </p>\n ) : (\n <p className={isProminent\n ? \"text-base text-foreground leading-relaxed mb-4\"\n : \"text-sm text-foreground/90 leading-relaxed mb-4\"\n }>\n {signalData.whyNow}\n </p>\n )}\n\n {/* Metadata chips relocated beneath the brief (case-panel redesign). */}\n {isCasePanelV2 || metadataLayout === \"below-brief\" ? (\n <div className=\"mb-4 flex flex-wrap items-center gap-2\">{metadataChips}</div>\n ) : null}\n\n {!isCasePanelV2 ? (\n <>\n {/* Before-score content slot (e.g. \"Signals on Case\" chips) */}\n {renderBeforeScore?.(item)}\n\n <ScoreWhyChips\n item={item}\n signalData={signalData}\n onOpenSignalBucket={onOpenSignalBucket}\n />\n <div className=\"mt-4\">\n <SignalApproval.Actions />\n </div>\n </>\n ) : null}\n </div>\n )\n })()}\n\n {isCasePanelV2 ? (\n <>\n {sections.signalBrief ? (\n <>\n {/* Before-score content slot (e.g. status/attention pills) */}\n {renderBeforeScore?.(item)}\n\n <div className=\"mb-8\">\n <ScoreWhyChips\n item={item}\n signalData={signalData}\n onOpenSignalBucket={onOpenSignalBucket}\n />\n <div className=\"mt-4\">\n <SignalApproval.Actions />\n </div>\n </div>\n </>\n ) : null}\n\n <div data-slot=\"case-panel-workflow-stack\" className=\"space-y-10\">\n {/* After-score content slot (e.g. OpportunityPanel) */}\n {renderAfterScore?.(item)}\n {renderPrimaryAction?.(item)}\n {renderCommentArea ? (\n <div data-slot=\"case-panel-comment-area\">\n {renderCommentArea(item)}\n </div>\n ) : null}\n </div>\n {timelineSection}\n </>\n ) : (\n <>\n {/* After-score content slot (e.g. OpportunityPanel) */}\n {renderAfterScore?.(item)}\n\n {/* Activity Timeline */}\n {timelineSection}\n </>\n )}\n </div>\n\n {!isCasePanelV2 ? suggestedActionsSection : null}\n {!isCasePanelV2 ? renderDetailExtra?.(item) : null}\n </div>\n </SignalApproval.Root>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Main Component\n// ---------------------------------------------------------------------------\n\nexport function PrototypeInboxView({\n items,\n filterCategories,\n detailSections,\n accountContacts = [],\n buildAccountContacts,\n emailSignature = \"\",\n buildSuggestedActions: buildSuggestedActionsProp,\n buildSourceItems: buildSourceItemsProp,\n getSignalScore: getSignalScoreProp,\n getTimelineEvents,\n iconMap = {},\n hideToolbarActions,\n hideHoverActions,\n onSuggestedActionFeedback,\n onScoreFeedback,\n onOpenSignalBucket,\n headerActions,\n onOpenEntityPanel,\n onOpenRecentActivity,\n onItemSelect,\n defaultViewMode,\n buildEntityChips,\n quickFilterTabs,\n hideAccountsButton,\n accountDetailsLabel,\n onSignalApprove,\n getSignalApprovalState,\n signalLabels,\n hideApproveButton,\n signalBriefCopy,\n briefStyleVariant,\n sectionLayout,\n renderDetailExtra,\n renderBeforeScore,\n renderAfterScore,\n renderPrimaryAction,\n renderCommentArea,\n lastActivityTime,\n timelineSystemEventsConfig,\n attentionCount,\n renderTitleExtra,\n renderTitleActionRow,\n renderTitleSubtext,\n accountDetailsButtonLabel,\n getAccountDetailsButtonAriaLabel,\n sortOptions,\n activeSortId,\n onSortChange,\n}: PrototypeInboxViewProps) {\n const [inboxViewMode, setInboxViewMode] = React.useState<\"inbox\" | \"list\" | \"detail\">(\n defaultViewMode === \"list\" ? \"list\" : defaultViewMode === \"split\" ? \"inbox\" : \"inbox\"\n )\n const [previousViewMode, setPreviousViewMode] = React.useState<\"inbox\" | \"list\">(\"inbox\")\n const [selectedTask, setSelectedTask] = React.useState(items[0])\n const [inboxAssignee, setInboxAssignee] = React.useState<AssigneeFilter>(\"me\")\n const [inboxFilters, setInboxFilters] = React.useState<Record<string, string>>({})\n const [activeQuickFilter, setActiveQuickFilter] = React.useState<string>(\"all\")\n const [splitViewSearch, setSplitViewSearch] = React.useState(\"\")\n\n const sections = React.useMemo(\n () => ({ ...DEFAULT_DETAIL_SECTIONS, ...detailSections }),\n [detailSections],\n )\n\n const resolvedFilterCategories: InboxFilterCategory[] = React.useMemo(\n () =>\n filterCategories ?? [\n {\n id: \"category\",\n label: \"Category\",\n icon: <Tag className=\"h-3.5 w-3.5 text-muted-foreground\" />,\n options: [...new Set(items.map((i) => i.tag1))],\n },\n {\n id: \"account\",\n label: \"Account\",\n icon: <Building className=\"h-3.5 w-3.5 text-muted-foreground\" />,\n options: [...new Set(items.map((i) => i.company))],\n },\n ],\n [filterCategories, items],\n )\n\n const buildSuggestedActions = React.useMemo(\n () => buildSuggestedActionsProp ?? (() => []),\n [buildSuggestedActionsProp],\n )\n\n const buildSourceItems = React.useMemo(\n () => buildSourceItemsProp ?? (() => []),\n [buildSourceItemsProp],\n )\n\n const getSignalScore = React.useMemo(\n () => getSignalScoreProp ?? (() => DEFAULT_SIGNAL_SCORE),\n [getSignalScoreProp],\n )\n\n // Build a map from filter category id → QueueItem field for targeted filtering.\n // Known category ids are mapped explicitly; unknown categories fall back to a\n // broad search across display fields so consumer-defined filters still work.\n const filterFieldMap = React.useMemo<\n Record<string, (item: QueueItem, value: string) => boolean>\n >(() => {\n const map: Record<string, (item: QueueItem, value: string) => boolean> = {}\n for (const cat of resolvedFilterCategories) {\n switch (cat.id) {\n case \"category\":\n case \"signalType\":\n map[cat.id] = (item, v) => item.tag1.toLowerCase() === v.toLowerCase()\n break\n case \"account\":\n map[cat.id] = (item, v) => item.company.toLowerCase() === v.toLowerCase()\n break\n default:\n // Fallback: check all display fields\n map[cat.id] = (item, v) => {\n const lv = v.toLowerCase()\n return (\n item.tag1.toLowerCase() === lv ||\n item.company.toLowerCase() === lv ||\n item.title.toLowerCase().includes(lv) ||\n item.details.toLowerCase().includes(lv)\n )\n }\n }\n }\n return map\n }, [resolvedFilterCategories])\n\n // Filter items for list view based on toolbar filters\n const filteredItems = React.useMemo(() => {\n const activeFilters = Object.entries(inboxFilters).filter(\n ([, value]) => value && value !== \"all\"\n )\n if (activeFilters.length === 0) return items\n return items.filter((item) =>\n activeFilters.every(([key, value]) => {\n const matcher = filterFieldMap[key]\n return matcher ? matcher(item, value) : true\n })\n )\n }, [items, inboxFilters, filterFieldMap])\n\n // Resolve quick filter tabs once — used by both the split view filter and\n // the tab bar render. Each tab's `matchValue` (falling back to `label`) is\n // compared against `item.tag1` so consumer labels can differ from data values.\n type QuickFilterTab = { id: string; label: string; matchValue?: string; count?: number }\n const resolvedQuickFilterTabs = React.useMemo<QuickFilterTab[]>(() => {\n if (quickFilterTabs) return quickFilterTabs\n // Derive default tabs from the actual item tag1 values\n const uniqueTags = [...new Set(items.map((i) => i.tag1))]\n return uniqueTags.map((tag) => ({\n id: tag.toLowerCase().replace(/\\s+/g, \"-\"),\n label: tag,\n }))\n }, [quickFilterTabs, items])\n\n // Compute per-tab counts once so they can be displayed in the tab bar\n const quickFilterTabCounts = React.useMemo(() => {\n const counts: Record<string, number> = {}\n for (const tab of resolvedQuickFilterTabs) {\n const match = (tab.matchValue ?? tab.label).toLowerCase()\n counts[tab.id] = items.filter((i) => i.tag1.toLowerCase() === match).length\n }\n return counts\n }, [resolvedQuickFilterTabs, items])\n\n // Filter items for split view based on quick filter tabs and search\n const splitViewItems = React.useMemo(() => {\n let filtered = items\n // Apply quick filter tab\n if (activeQuickFilter !== \"all\") {\n const activeTab = resolvedQuickFilterTabs.find((t) => t.id === activeQuickFilter)\n if (activeTab) {\n const match = (activeTab.matchValue ?? activeTab.label).toLowerCase()\n filtered = filtered.filter(\n (item) => item.tag1.toLowerCase() === match\n )\n }\n }\n // Apply search input\n if (splitViewSearch.trim()) {\n const q = splitViewSearch.trim().toLowerCase()\n filtered = filtered.filter(\n (item) =>\n item.tag1.toLowerCase().includes(q) ||\n item.company.toLowerCase().includes(q) ||\n item.title.toLowerCase().includes(q)\n )\n }\n return filtered\n }, [items, activeQuickFilter, resolvedQuickFilterTabs, splitViewSearch])\n\n // Grouped items for list view — one group per severity tier so items\n // are labelled correctly (e.g. \"Urgent\", \"High\", \"Active\").\n const inboxGroups = React.useMemo<GroupedListGroup<QueueItem>[]>(() => {\n const urgent = filteredItems.filter((i) => i.statusColor === \"red\")\n const high = filteredItems.filter((i) => i.statusColor === \"orange\")\n const active = filteredItems.filter((i) => i.statusColor !== \"red\" && i.statusColor !== \"orange\")\n return [\n { key: \"urgent\", label: \"Urgent\", items: urgent },\n { key: \"high\", label: \"High\", items: high },\n { key: \"active\", label: \"Active\", items: active },\n ].filter((g) => g.items.length > 0)\n }, [filteredItems])\n\n const renderInboxRow = React.useCallback(\n (item: QueueItem) => (\n <>\n <span className={`h-2 w-2 shrink-0 rounded-full ${dotColorClass(item.statusColor)}`} />\n <span className=\"w-[80px] shrink-0 font-mono text-xs text-muted-foreground/80\">{item.id}</span>\n <span className=\"shrink-0 rounded-md border border-border bg-muted px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground whitespace-nowrap\">{item.tag1}</span>\n <span className=\"min-w-0 flex-1 truncate text-sm font-semibold text-foreground\">{item.title}</span>\n <span className=\"w-[120px] shrink-0 truncate text-xs font-medium text-foreground\">{item.company}</span>\n <span className=\"w-[80px] shrink-0 text-right text-xs text-muted-foreground\">{item.time}</span>\n </>\n ),\n [],\n )\n\n const handleInboxItemSelect = React.useCallback(\n (item: QueueItem) => {\n setSelectedTask(item)\n if (onItemSelect) {\n onItemSelect(item)\n } else if (inboxViewMode === \"list\") {\n setPreviousViewMode(\"list\")\n setInboxViewMode(\"detail\")\n }\n },\n [inboxViewMode, onItemSelect],\n )\n\n const handleBackFromDetail = React.useCallback(() => {\n setInboxViewMode(previousViewMode)\n }, [previousViewMode])\n\n const handleViewModeChange = React.useCallback((id: string) => {\n const mode = id as \"inbox\" | \"list\" | \"detail\"\n if (mode !== \"detail\") {\n setPreviousViewMode(mode)\n }\n setInboxViewMode(mode)\n }, [])\n\n React.useEffect(() => {\n const mql = window.matchMedia(\"(max-width: 768px)\")\n function handleChange(e: MediaQueryListEvent | MediaQueryList) {\n if (e.matches && inboxViewMode === \"inbox\") {\n setPreviousViewMode(\"inbox\")\n setInboxViewMode(\"detail\")\n }\n }\n handleChange(mql)\n mql.addEventListener(\"change\", handleChange)\n return () => mql.removeEventListener(\"change\", handleChange)\n }, [inboxViewMode])\n\n const detailViewProps: DetailViewProps = {\n item: selectedTask,\n sections,\n getSignalScore,\n buildSuggestedActions,\n buildSourceItems,\n getTimelineEvents,\n accountContacts: buildAccountContacts?.(selectedTask) ?? accountContacts,\n emailSignature,\n iconMap,\n onOpenEntityPanel,\n onOpenRecentActivity,\n onSuggestedActionFeedback,\n onScoreFeedback,\n onSignalApprove,\n getSignalApprovalState,\n signalLabels,\n hideApproveButton,\n signalBriefCopy,\n briefStyleVariant,\n sectionLayout,\n renderDetailExtra,\n renderBeforeScore,\n renderAfterScore,\n renderPrimaryAction,\n renderCommentArea,\n lastActivityTime,\n timelineSystemEventsConfig,\n attentionCount,\n renderTitleExtra,\n renderTitleActionRow,\n renderTitleSubtext,\n accountDetailsButtonLabel,\n getAccountDetailsButtonAriaLabel,\n onOpenSignalBucket,\n }\n\n return (\n <div className=\"flex h-full w-full flex-col\">\n {/* Toolbar */}\n <div className=\"flex items-center justify-between border-b border-border bg-background px-4 py-3 shrink-0\">\n <div className=\"flex items-center gap-3\">\n {inboxViewMode === \"detail\" ? (\n <button\n type=\"button\"\n onClick={handleBackFromDetail}\n className=\"flex items-center gap-2 text-sm font-medium text-muted-foreground hover:text-foreground transition-colors\"\n >\n <ArrowLeft className=\"h-4 w-4\" />\n Back\n </button>\n ) : null}\n <h2 className=\"text-lg font-semibold text-foreground\">Inbox</h2>\n <Badge variant=\"secondary\" className=\"bg-muted text-muted-foreground hover:bg-muted font-medium text-[11px] px-2 py-0.5 rounded-md\">\n {items.length}\n </Badge>\n </div>\n <div className=\"flex items-center gap-3\">\n <ViewModeToggle\n modes={[\n { id: \"inbox\", icon: <Columns2 className=\"h-3.5 w-3.5\" />, label: \"Split View\" },\n { id: \"list\", icon: <LayoutList className=\"h-3.5 w-3.5\" />, label: \"List View\" },\n { id: \"detail\", icon: <Square className=\"h-3.5 w-3.5\" />, label: \"Detail View\" },\n ]}\n activeMode={inboxViewMode}\n onModeChange={handleViewModeChange}\n />\n {headerActions}\n </div>\n </div>\n\n {/* View modes */}\n {inboxViewMode === \"detail\" ? (\n <div className=\"flex h-full flex-1 flex-col overflow-hidden bg-background\">\n <div className=\"flex-1 overflow-y-auto\">\n <DetailView {...detailViewProps} />\n </div>\n </div>\n ) : inboxViewMode === \"list\" ? (\n <div className=\"flex-1 overflow-y-auto bg-background\">\n <InboxToolbar\n assignee={inboxAssignee}\n onAssigneeChange={setInboxAssignee}\n filterCategories={resolvedFilterCategories}\n selectedFilters={inboxFilters}\n onFilterChange={(catId, val) =>\n setInboxFilters((prev) => ({ ...prev, [catId]: val }))\n }\n onClearFilters={() => setInboxFilters({})}\n />\n <GroupedListView<QueueItem>\n groups={inboxGroups}\n renderRow={renderInboxRow}\n getItemKey={(item) => item.id}\n selectedKey={selectedTask.id}\n onItemClick={handleInboxItemSelect}\n emptyMessage=\"No inbox items\"\n />\n </div>\n ) : (\n /* Split view */\n <div className=\"flex h-full min-h-0 w-full flex-1\">\n <div className=\"flex h-full min-w-[380px] w-[380px] flex-col border-r border-border bg-background shadow-sm z-10\">\n <div className=\"flex flex-col gap-4 border-b border-border p-4 shrink-0\">\n {!hideToolbarActions && (\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-1\">\n <Button variant=\"outline\" size=\"icon\" className=\"h-8 w-8 text-muted-foreground\"><Eye className=\"w-4 h-4\" /></Button>\n <Button variant=\"outline\" size=\"icon\" className=\"h-8 w-8 text-muted-foreground\"><FileText className=\"w-4 h-4\" /></Button>\n <Button variant=\"outline\" size=\"icon\" className=\"h-8 w-8 text-muted-foreground\"><Clock className=\"w-4 h-4\" /></Button>\n <Button variant=\"outline\" size=\"icon\" className=\"h-8 w-8 text-muted-foreground\"><CheckSquare className=\"w-4 h-4\" /></Button>\n </div>\n <Button size=\"sm\" className=\"h-8 px-4 bg-foreground text-background hover:bg-foreground/90 text-xs font-semibold gap-1.5 rounded-md\">\n <Plus className=\"w-4 h-4\" /> Add Task\n </Button>\n </div>\n )}\n <div className=\"flex items-center gap-2\">\n <div className=\"relative flex-1\">\n <Filter className=\"absolute left-2.5 top-1.5 w-4 h-4 text-muted-foreground\" />\n <Input\n className=\"h-8 pl-8 text-xs bg-background border-border rounded-md shadow-none\"\n placeholder=\"Filter by category...\"\n value={splitViewSearch}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) => setSplitViewSearch(e.target.value)}\n />\n </div>\n {sortOptions && sortOptions.length > 0 && (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button variant=\"outline\" size=\"sm\" className=\"h-8 text-xs font-medium rounded-md shadow-none gap-1.5\">\n <ArrowUpDown className=\"w-3.5 h-3.5\" />\n {sortOptions.find(o => o.id === activeSortId)?.label ?? 'Sort'}\n <ChevronDown className=\"w-3 h-3\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuRadioGroup value={activeSortId ?? ''} onValueChange={(val) => onSortChange?.(val)}>\n {sortOptions.map(option => (\n <DropdownMenuRadioItem key={option.id} value={option.id}>\n {option.label}\n </DropdownMenuRadioItem>\n ))}\n </DropdownMenuRadioGroup>\n </DropdownMenuContent>\n </DropdownMenu>\n )}\n {!hideAccountsButton && (\n <Button variant=\"outline\" size=\"sm\" className=\"h-8 text-xs font-medium rounded-md shadow-none\">\n <Building className=\"w-3.5 h-3.5 mr-1.5\" /> {accountDetailsLabel ?? \"Accounts\"}\n </Button>\n )}\n </div>\n <div className=\"flex items-center gap-1.5 overflow-x-auto pb-1 mt-1 scrollbar-hide\">\n <Button\n size=\"sm\"\n variant={activeQuickFilter === \"all\" ? \"default\" : \"outline\"}\n className={`h-7 rounded-full px-3.5 text-[11px] font-semibold shadow-none ${\n activeQuickFilter === \"all\"\n ? \"bg-foreground text-background hover:bg-foreground/90\"\n : \"bg-transparent border-border text-muted-foreground hover:text-foreground\"\n }`}\n onClick={() => setActiveQuickFilter(\"all\")}\n >\n All\n </Button>\n {resolvedQuickFilterTabs.map((tab) => {\n const count = tab.count ?? quickFilterTabCounts[tab.id]\n return (\n <Button\n key={tab.id}\n size=\"sm\"\n variant={activeQuickFilter === tab.id ? \"default\" : \"outline\"}\n className={`h-7 rounded-full px-3.5 text-[11px] font-medium shadow-none ${\n activeQuickFilter === tab.id\n ? \"bg-foreground text-background hover:bg-foreground/90\"\n : \"bg-transparent border-border text-muted-foreground hover:text-foreground\"\n }`}\n onClick={() => setActiveQuickFilter(tab.id)}\n >\n {tab.label}{count != null && count > 0 ? ` (${count})` : \"\"}\n </Button>\n )\n })}\n </div>\n </div>\n\n <div className=\"flex-1 overflow-y-auto\">\n {splitViewItems.map((item) => (\n <div\n key={item.id}\n onClick={() => { setSelectedTask(item); onItemSelect?.(item) }}\n className={`cursor-pointer border-b border-border p-4 transition-colors group relative border-l-2 ${\n selectedTask.id === item.id\n ? \"bg-muted/30 border-l-brand-purple\"\n : \"bg-transparent border-l-transparent hover:bg-muted/10\"\n }`}\n >\n <div className=\"mb-1.5 flex items-center gap-2\">\n <span className=\"min-w-0 truncate text-[13px] font-semibold text-foreground leading-tight\">{item.title}</span>\n {selectedTask.id !== item.id && item.tag1 && (\n <span className=\"shrink-0 rounded-md border border-border bg-muted/60 px-2 py-0.5 text-[10px] font-medium text-muted-foreground\">\n {item.tag1}\n </span>\n )}\n <span className=\"ml-auto shrink-0 text-[10px] font-medium text-muted-foreground/80\">{item.time}</span>\n </div>\n <div className=\"flex items-start gap-2 mt-2\">\n <span className={`w-1.5 h-1.5 rounded-full shrink-0 mt-1.5 ${dotColorClass(item.statusColor)}`} />\n <span className=\"text-xs text-muted-foreground leading-tight\">{item.details}</span>\n </div>\n {buildEntityChips && (() => {\n const chips = buildEntityChips(item)\n if (!chips.length) return null\n return (\n <div className=\"flex items-center gap-1.5 mt-2 flex-wrap\">\n {chips.map((chip) => (\n <button\n key={chip.id}\n type=\"button\"\n onClick={(e) => { e.stopPropagation(); chip.onClick?.() }}\n className=\"inline-flex items-center gap-1 rounded-md border border-border/60 bg-muted/30 px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground transition-colors hover:bg-muted/50 hover:text-foreground\"\n >\n <span className=\"flex h-3.5 w-3.5 items-center justify-center rounded bg-muted-foreground/10 text-[8px] font-semibold\">{chip.avatarLetter}</span>\n {chip.label}\n </button>\n ))}\n </div>\n )\n })()}\n {!hideHoverActions && (\n <div className={`absolute right-4 bottom-4 flex items-center gap-1.5 bg-background shadow-sm rounded-md px-1 py-0.5 border border-border ${\n selectedTask.id === item.id ? \"opacity-100\" : \"opacity-0 group-hover:opacity-100 transition-opacity\"\n }`}>\n <Button variant=\"ghost\" size=\"icon\" className=\"h-6 w-6 rounded text-muted-foreground hover:text-foreground\"><CheckSquare className=\"w-3.5 h-3.5\" /></Button>\n <Button variant=\"ghost\" size=\"icon\" className=\"h-6 w-6 rounded text-muted-foreground hover:text-foreground\"><Clock className=\"w-3.5 h-3.5\" /></Button>\n </div>\n )}\n </div>\n ))}\n <div className=\"p-4\">\n <Button variant=\"outline\" size=\"sm\" className=\"h-8 text-xs font-semibold rounded-md shadow-none\">See more</Button>\n </div>\n </div>\n </div>\n\n <div className=\"flex h-full flex-1 flex-col overflow-hidden bg-background\">\n <div className=\"flex-1 overflow-y-auto\">\n <DetailView {...detailViewProps} />\n </div>\n </div>\n </div>\n )}\n </div>\n )\n}\n"],"mappings":"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","names":["_a"]}
|
|
1
|
+
{"version":3,"sources":["../../src/prototype/prototype-inbox-view.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport {\n ArrowLeft,\n ArrowUpDown,\n ChevronDown,\n ChevronRight,\n Filter,\n FileText,\n Clock,\n CheckSquare,\n Eye,\n Plus,\n\n Building,\n LayoutList,\n Columns2,\n Square,\n Tag,\n} from \"lucide-react\"\n\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuTrigger,\n} from \"../components/dropdown-menu\"\nimport { Button } from \"../components/button\"\nimport { Badge } from \"../components/badge\"\nimport { Input } from \"../components/input\"\nimport { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from \"../components/tooltip\"\nimport { ViewModeToggle } from \"../components/view-mode-toggle\"\nimport {\n InboxToolbar,\n type AssigneeFilter,\n type InboxFilterCategory,\n} from \"../components/inbox-toolbar\"\nimport { GroupedListView, type GroupedListGroup } from \"../components/item-list\"\nimport { SignalApproval, type ApprovalState, type OpportunityDraft, type OpportunityPreview } from \"../components/signal-feedback-inline\"\nimport { ScoreWhyChips } from \"../components/score-why-chips\"\nimport { SignalPriorityPopover } from \"../components/signal-priority-popover\"\nimport { type SourceDef } from \"../components/detail-view\"\nimport {\n SuggestedActions,\n type SuggestedAction,\n type SuggestedContact,\n} from \"../components/suggested-actions\"\nimport { TimelineActivity, type TimelineEvent } from \"../components/timeline-activity\"\nimport { cn } from \"../lib/utils\"\nimport type {\n QueueItem,\n InboxViewConfig,\n InboxDetailSections,\n SignalScoreData,\n BriefStyleVariant,\n TimelineSystemEventsConfig,\n} from \"./prototype-config\"\n\n// ---------------------------------------------------------------------------\n// Dot color utility — maps statusColor to a Tailwind background class.\n// Centralised here so the list row and split-view card stay in sync and\n// future color additions are a single-edit change.\n// ---------------------------------------------------------------------------\n\nfunction dotColorClass(statusColor: string): string {\n if (statusColor === \"red\") return \"bg-[#f43f5e]\"\n if (statusColor === \"yellow\" || statusColor === \"amber\" || statusColor === \"orange\") return \"bg-[#eab308]\"\n if (statusColor === \"green\") return \"bg-[#22c55e]\"\n return \"bg-[#9ca3af]\"\n}\n\n// ---------------------------------------------------------------------------\n// Props\n// ---------------------------------------------------------------------------\n\nexport interface PrototypeInboxViewProps extends InboxViewConfig {\n /** Extra ReactNode rendered at the end of the header bar (e.g. exit button). */\n headerActions?: React.ReactNode\n onOpenEntityPanel?: () => void\n onOpenRecentActivity?: () => void\n onNavigateToInbox?: () => void\n onItemSelect?: (item: QueueItem) => void\n defaultViewMode?: \"list\" | \"split\"\n}\n\n// ---------------------------------------------------------------------------\n// Default detail sections\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_DETAIL_SECTIONS: InboxDetailSections = {\n signalBrief: true,\n suggestedActions: true,\n timeline: true,\n}\n\nconst DEFAULT_SIGNAL_SCORE: SignalScoreData = {\n score: 65,\n factors: [\n { key: \"trigger\", label: \"Trigger strength\", score: 70, why: \"Moderate signal detected based on account activity\" },\n { key: \"fit\", label: \"Company fit\", score: 65, why: \"Reasonable fit based on company profile\" },\n { key: \"timing\", label: \"Timing\", score: 58, why: \"Within general evaluation window\" },\n ],\n whyNow: \"Moderate signals detected that warrant review and potential outreach.\",\n evidence: [\n \"Activity patterns suggest potential opportunity\",\n \"Company profile aligns with target segment\",\n ],\n confidence: 72,\n}\n\n// ---------------------------------------------------------------------------\n// Detail View\n// ---------------------------------------------------------------------------\n\n/** Up to 2-character uppercase initials for a company name (e.g. \"Northwind Systems\" -> \"NW\"). */\nfunction getCompanyInitials(company: string): string {\n const words = company.trim().split(/\\s+/).filter(Boolean)\n if (words.length === 0) return \"?\"\n if (words.length === 1) return words[0].slice(0, 2).toUpperCase()\n return (words[0][0] + words[1][0]).toUpperCase()\n}\n\n/** Maps a QueueItem.statusColor to a Tailwind dot background class. */\nconst STATUS_DOT_CLASS: Record<string, string> = {\n red: \"bg-red-500\",\n orange: \"bg-orange-500\",\n amber: \"bg-amber-500\",\n yellow: \"bg-yellow-500\",\n green: \"bg-emerald-500\",\n emerald: \"bg-emerald-500\",\n blue: \"bg-blue-500\",\n gray: \"bg-gray-300\",\n grey: \"bg-gray-300\",\n}\n\nexport interface DetailViewProps {\n item: QueueItem\n sections: InboxDetailSections\n getSignalScore: (company: string, item?: QueueItem) => SignalScoreData\n buildSuggestedActions: (item: QueueItem) => SuggestedAction[]\n buildSourceItems: (item: QueueItem) => SourceDef[]\n getTimelineEvents?: (item: QueueItem) => TimelineEvent[]\n accountContacts: SuggestedContact[]\n emailSignature: string | React.ReactNode\n iconMap: Record<string, string>\n onOpenEntityPanel?: () => void\n onOpenRecentActivity?: () => void\n onSuggestedActionFeedback?: (actionId: number | string, feedback: string, actionTitle?: string) => void\n /** @deprecated The compact score UX no longer renders score-level thumbs by default. */\n onScoreFeedback?: (type: \"up\" | \"down\", pills: string[], detail: string) => void\n onSignalApprove?: (item: QueueItem, draft?: OpportunityDraft) => void | Promise<boolean>\n getSignalApprovalState?: (item: QueueItem) => ApprovalState | undefined\n signalLabels?: InboxViewConfig[\"signalLabels\"]\n hideApproveButton?: boolean\n signalBriefCopy?: InboxViewConfig[\"signalBriefCopy\"]\n briefStyleVariant?: BriefStyleVariant\n /** Opt-in detail panel section ordering. Defaults to the existing layout. */\n sectionLayout?: InboxViewConfig[\"sectionLayout\"]\n renderDetailExtra?: (item: QueueItem) => React.ReactNode\n /** Render content between the signal brief text and the signal score bar (e.g. \"Signals on Case\" chips). */\n renderBeforeScore?: (item: QueueItem) => React.ReactNode\n /** Render content between the signal score section and the activity timeline. */\n renderAfterScore?: (item: QueueItem) => React.ReactNode\n /** Render primary case-panel content between the opportunity section and comment area. */\n renderPrimaryAction?: (item: QueueItem) => React.ReactNode\n /** Render case-panel comment content before the activity timeline. */\n renderCommentArea?: (item: QueueItem) => React.ReactNode\n lastActivityTime?: string\n /** Render extra content inline with the detail title. */\n renderTitleExtra?: (item: QueueItem) => React.ReactNode\n /** Render a full-width action row below the detail title row. */\n renderTitleActionRow?: (item: QueueItem) => React.ReactNode\n /** Render supporting content below the detail title. */\n renderTitleSubtext?: (item: QueueItem) => React.ReactNode\n /** Render extra metadata chips (e.g. assignee) inside the chips row below the title. */\n renderMetadataExtra?: (item: QueueItem) => React.ReactNode\n /**\n * Where the metadata chips row (priority · deadline · account · renderMetadataExtra) sits.\n * - \"default\": above the signal brief (legacy layout, unchanged for existing consumers).\n * - \"below-brief\": moved beneath the brief text, just before `renderBeforeScore`\n * (case-panel redesign — chips + owners read as one block under the brief).\n */\n metadataLayout?: \"default\" | \"below-brief\"\n /** Override the built-in account details metadata button label. */\n accountDetailsButtonLabel?: (item: QueueItem) => React.ReactNode\n /** Accessible label for the built-in account details metadata button. */\n getAccountDetailsButtonAriaLabel?: (item: QueueItem) => string\n onOpenSignalBucket?: (args: { item: QueueItem; bucketKey: string; signalId: string }) => void\n approveButtonIconUrl?: string\n opportunityPreview?: OpportunityPreview\n onRequestApproval?: () => Promise<void>\n /** Number of important/attention-worthy events to highlight on the collapsed timeline header. */\n attentionCount?: number\n /** Configuration for the system-noise events toggle (score changes, etc.). */\n timelineSystemEventsConfig?: TimelineSystemEventsConfig\n\n // ── Deprecated individual props (use timelineSystemEventsConfig instead) ──\n /** @deprecated Use `timelineSystemEventsConfig.toggleLabel`. */\n timelineSystemEventsToggleLabel?: string\n /** @deprecated Use `timelineSystemEventsConfig.storageKey`. */\n timelineSystemEventsStorageKey?: string\n /** @deprecated Use `timelineSystemEventsConfig.defaultVisible`. */\n timelineSystemEventsDefaultVisible?: boolean\n /** @deprecated Use `timelineSystemEventsConfig.hiddenHint`. */\n timelineSystemEventsHiddenHint?: string\n /** @deprecated Use `timelineSystemEventsConfig.visibleHint`. */\n timelineSystemEventsVisibleHint?: string\n}\n\n// ---------------------------------------------------------------------------\n// TimelineSection — extracted from the IIFE in DetailView for readability\n// ---------------------------------------------------------------------------\n\nfunction TimelineSection({\n timelineEvents,\n showTimeline,\n setShowTimeline,\n showSystemEvents,\n setShowSystemEvents,\n attentionCount,\n sysEvtConfig,\n lastActivityTime,\n isCasePanel = false,\n}: {\n timelineEvents: TimelineEvent[]\n showTimeline: boolean\n setShowTimeline: React.Dispatch<React.SetStateAction<boolean>>\n showSystemEvents: boolean\n setShowSystemEvents: React.Dispatch<React.SetStateAction<boolean>>\n attentionCount?: number\n sysEvtConfig?: TimelineSystemEventsConfig\n lastActivityTime?: string\n isCasePanel?: boolean\n}) {\n // Single-pass partition: compute visibleEvents and hiddenCount together\n const visibleEvents: TimelineEvent[] = []\n let hiddenCount = 0\n for (const e of timelineEvents) {\n if (e.isSystemNoise) hiddenCount++\n if (!e.isSystemNoise || showSystemEvents) visibleEvents.push(e)\n }\n const hasSystemNoise = hiddenCount > 0\n\n // The toggle renders whenever there are system-noise events — even if no\n // config was provided — so consumers that emit `isSystemNoise: true` always\n // give users a way to reveal those events.\n const toggleLabel = sysEvtConfig?.toggleLabel ?? \"System events\"\n const toggleHelp = showSystemEvents\n ? sysEvtConfig?.visibleHint?.replace(\"{count}\", String(hiddenCount)) ?? \"Hide system events\"\n : sysEvtConfig?.hiddenHint ?? \"Show system events\"\n\n // Derive \"Last activity\" from the first *visible* event so the collapsed\n // header never points at a hidden score-update. The caller-supplied\n // `lastActivityTime` is only used when system-noise filtering is NOT active\n // (i.e. all events are visible) since it may come from an unfiltered source\n // such as `case.last_activity_at`.\n const firstVisibleTime =\n (!hasSystemNoise || showSystemEvents) && lastActivityTime\n ? lastActivityTime\n : visibleEvents.length > 0\n ? visibleEvents[0].time\n : \"\"\n\n const visibleCount = visibleEvents.length\n const eventCountLabel = `${visibleCount} ${visibleCount === 1 ? \"event\" : \"events\"}`\n\n return (\n <div\n className={cn(\n isCasePanel ? \"mt-8 border-t border-border pt-8 pb-8\" : \"mb-8\"\n )}\n >\n {/* Header — outer non-interactive container */}\n <div\n className={cn(\n \"flex w-full items-center justify-between\",\n isCasePanel\n ? \"gap-4 border-b border-border pb-5\"\n : \"group/timeline gap-2 rounded-md py-2 transition-colors hover:bg-muted/40 -mx-2 px-2\"\n )}\n data-testid=\"timeline-header\"\n >\n {/* Left: collapse/expand button */}\n <button\n type=\"button\"\n onClick={() => setShowTimeline((prev) => !prev)}\n className=\"flex min-w-0 cursor-pointer items-center gap-2 border-0 bg-transparent p-0 text-left\"\n data-testid=\"timeline-collapse-btn\"\n >\n <h3 className=\"text-xs font-bold uppercase tracking-[0.16em] text-muted-foreground transition-colors group-hover/timeline:text-foreground\">ACTIVITY TIMELINE</h3>\n {!showTimeline && attentionCount != null && attentionCount > 0 && (\n <span className=\"inline-flex items-center gap-1 rounded-full bg-destructive/10 px-1.5 py-0.5 text-[10px] font-semibold text-destructive border border-destructive/20\">\n {attentionCount} new\n </span>\n )}\n {!isCasePanel && !showTimeline && firstVisibleTime && (\n <span className=\"text-[11px] text-muted-foreground/60\" data-testid=\"last-activity-hint\">\n · Last activity {firstVisibleTime}\n </span>\n )}\n </button>\n\n {/* Right: system-events toggle, event count, and collapse affordance */}\n <div className=\"flex shrink-0 items-center gap-4\">\n {hasSystemNoise && (\n <button\n type=\"button\"\n onClick={() => setShowSystemEvents((prev) => !prev)}\n className={cn(\n \"inline-flex shrink-0 cursor-pointer items-center gap-3 rounded-full border px-3.5 py-2 text-sm font-semibold transition-colors\",\n showSystemEvents\n ? \"border-foreground bg-foreground text-background shadow-sm hover:bg-foreground/90\"\n : \"border-border bg-background text-muted-foreground shadow-sm hover:bg-muted/40 hover:text-foreground\"\n )}\n aria-pressed={showSystemEvents}\n aria-label={toggleHelp}\n title={toggleHelp}\n data-testid=\"system-events-toggle\"\n >\n <span\n className={cn(\n \"relative inline-flex h-4 w-8 shrink-0 items-center rounded-full p-0.5 transition-colors\",\n showSystemEvents ? \"bg-teal-600\" : \"bg-muted-foreground/30\"\n )}\n aria-hidden=\"true\"\n data-testid=\"system-events-indicator\"\n >\n <span\n className={cn(\n \"block h-3 w-3 rounded-full bg-white shadow-sm transition-transform\",\n showSystemEvents ? \"translate-x-4\" : \"translate-x-0\"\n )}\n />\n </span>\n <span>{toggleLabel}</span>\n {!showSystemEvents ? (\n <span\n className=\"inline-flex min-w-[22px] items-center justify-center rounded-full bg-muted px-1.5 text-xs font-bold tabular-nums text-muted-foreground\"\n data-testid=\"hidden-count-badge\"\n >\n {hiddenCount}\n </span>\n ) : null}\n </button>\n )}\n\n <button\n type=\"button\"\n onClick={() => setShowTimeline((prev) => !prev)}\n className={cn(\n \"inline-flex shrink-0 cursor-pointer items-center border-0 bg-transparent p-0 text-muted-foreground transition-colors hover:text-foreground\",\n isCasePanel ? \"gap-3 text-sm\" : \"gap-1.5 text-[11px]\"\n )}\n aria-label={showTimeline ? \"Collapse activity timeline\" : \"Expand activity timeline\"}\n >\n <span className=\"font-medium\" data-testid=\"event-count\">{eventCountLabel}</span>\n <ChevronDown className={`h-3.5 w-3.5 transition-transform duration-200 ${showTimeline ? \"rotate-180\" : \"\"}`} />\n </button>\n </div>\n </div>\n\n {/* Timeline body */}\n {showTimeline && visibleEvents.length > 0 && (\n <div className=\"mt-6\">\n <TimelineActivity events={visibleEvents} variant=\"case-panel\" />\n </div>\n )}\n </div>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Detail View\n// ---------------------------------------------------------------------------\n\nexport function DetailView({\n item,\n sections,\n getSignalScore,\n buildSuggestedActions,\n buildSourceItems: _buildSourceItems,\n getTimelineEvents,\n accountContacts,\n emailSignature,\n iconMap,\n onOpenEntityPanel,\n onOpenRecentActivity,\n onSuggestedActionFeedback: _onSuggestedActionFeedback,\n onScoreFeedback: _onScoreFeedback,\n onSignalApprove,\n getSignalApprovalState,\n signalLabels,\n hideApproveButton,\n signalBriefCopy,\n briefStyleVariant = \"default\",\n sectionLayout = \"default\",\n renderDetailExtra,\n renderBeforeScore,\n renderAfterScore,\n renderPrimaryAction,\n renderCommentArea,\n lastActivityTime,\n renderTitleExtra,\n renderTitleActionRow,\n renderTitleSubtext,\n renderMetadataExtra,\n metadataLayout = \"default\",\n accountDetailsButtonLabel,\n getAccountDetailsButtonAriaLabel,\n onOpenSignalBucket,\n approveButtonIconUrl,\n opportunityPreview,\n onRequestApproval,\n attentionCount,\n timelineSystemEventsConfig: configProp,\n timelineSystemEventsToggleLabel,\n timelineSystemEventsStorageKey,\n timelineSystemEventsDefaultVisible,\n timelineSystemEventsHiddenHint,\n timelineSystemEventsVisibleHint,\n}: DetailViewProps) {\n // Resolve system-events config: prefer the config object, fall back to deprecated individual props.\n const sysEvtConfig = React.useMemo<TimelineSystemEventsConfig | undefined>(() => {\n if (configProp) return configProp\n // Build from deprecated individual props if any are provided\n if (\n timelineSystemEventsToggleLabel ||\n timelineSystemEventsStorageKey ||\n timelineSystemEventsDefaultVisible !== undefined ||\n timelineSystemEventsHiddenHint ||\n timelineSystemEventsVisibleHint\n ) {\n return {\n toggleLabel: timelineSystemEventsToggleLabel,\n storageKey: timelineSystemEventsStorageKey,\n defaultVisible: timelineSystemEventsDefaultVisible,\n hiddenHint: timelineSystemEventsHiddenHint,\n visibleHint: timelineSystemEventsVisibleHint,\n }\n }\n return undefined\n }, [\n configProp,\n timelineSystemEventsToggleLabel,\n timelineSystemEventsStorageKey,\n timelineSystemEventsDefaultVisible,\n timelineSystemEventsHiddenHint,\n timelineSystemEventsVisibleHint,\n ])\n\n const [showTimeline, setShowTimeline] = React.useState(false)\n const [extraActions, setExtraActions] = React.useState<SuggestedAction[]>([])\n\n // ---- System-noise toggle state ----\n const sysEvtDefaultVisible = sysEvtConfig?.defaultVisible ?? false\n const sysEvtStorageKey = sysEvtConfig?.storageKey\n const [showSystemEvents, setShowSystemEvents] = React.useState(sysEvtDefaultVisible)\n const initialReadDoneRef = React.useRef(false)\n\n // Read persisted value from localStorage on mount\n React.useEffect(() => {\n if (!sysEvtStorageKey) {\n initialReadDoneRef.current = true\n return\n }\n try {\n const stored = localStorage.getItem(sysEvtStorageKey)\n if (stored !== null) {\n setShowSystemEvents(stored === \"true\")\n }\n } catch {\n // localStorage unavailable — ignore\n }\n initialReadDoneRef.current = true\n }, [sysEvtStorageKey])\n\n // Write to localStorage when the toggle changes (skip initial if matching default)\n React.useEffect(() => {\n if (!sysEvtStorageKey) return\n if (!initialReadDoneRef.current) return\n try {\n localStorage.setItem(sysEvtStorageKey, String(showSystemEvents))\n } catch {\n // localStorage unavailable — ignore\n }\n }, [showSystemEvents, sysEvtStorageKey])\n\n React.useEffect(() => {\n setShowTimeline(false)\n setExtraActions([])\n }, [item.id])\n\n const signalData = React.useMemo(\n () => getSignalScore(item.company, item),\n [getSignalScore, item],\n )\n\n const suggestedActions = React.useMemo(\n () => [...buildSuggestedActions(item), ...extraActions],\n [buildSuggestedActions, item, extraActions],\n )\n const timelineEvents = React.useMemo(\n () => getTimelineEvents?.(item) ?? [],\n [getTimelineEvents, item],\n )\n\n const handleDuplicate = React.useCallback(\n (id: number | string) => {\n const base = suggestedActions.find((a) => a.id === id)\n if (!base || base.type !== \"email\") return\n const clone: SuggestedAction = {\n ...base,\n id: `${base.id}-dup-${Date.now()}`,\n emailMeta: base.emailMeta ? { ...base.emailMeta, to: undefined } : undefined,\n }\n setExtraActions((prev) => [...prev, clone])\n },\n [suggestedActions],\n )\n\n // Deadline chip tone (case-panel redesign): amber as the deadline nears, red when due/overdue.\n const timeChipToneClass =\n signalData.timeChipTone === \"red\"\n ? \"border-red-300 bg-red-50 text-red-700 hover:bg-red-50\"\n : signalData.timeChipTone === \"amber\"\n ? \"border-amber-300 bg-amber-50 text-amber-700 hover:bg-amber-50\"\n : \"hover:bg-muted/50\"\n\n const isCasePanelV2 = sectionLayout === \"case-panel-v2\"\n\n // The metadata chips row (priority · deadline · account · renderMetadataExtra). Rendered above\n // the brief by default, or beneath it when `metadataLayout === \"below-brief\"` (case-panel redesign).\n const metadataChips = (\n <>\n <SignalPriorityPopover\n score={signalData.score}\n urgencyLabel={signalData.urgencyLabel}\n urgencyExplanation={signalData.urgencyExplanation ?? signalData.signalBrief}\n scoreDisplay={signalData.priorityScoreDisplay}\n formulaLabel={signalData.priorityFormulaLabel}\n factors={signalData.priorityFactors ?? []}\n metaText={undefined}\n feedbackChips={signalData.priorityFeedbackChips}\n onFeedbackSubmit={signalData.onPriorityFeedback}\n initialFactorFeedback={signalData.initialFactorPopoverFeedback}\n onFactorFeedback={signalData.onFactorFeedback}\n initialPriorityFeedback={signalData.initialPriorityFeedback}\n />\n {signalData.timeChipLabel && (\n <Badge\n variant=\"outline\"\n title={signalData.timeChipDetail ?? undefined}\n className={`transition-colors ${timeChipToneClass}`}\n >\n {signalData.timeChipLabel}\n </Badge>\n )}\n <TooltipProvider delayDuration={300}>\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n onClick={onOpenEntityPanel}\n className=\"group/account ml-1 inline-flex max-w-full items-center gap-2 rounded-md border border-border/60 bg-background px-2 py-1 transition-colors hover:border-border hover:bg-muted/50\"\n aria-label={getAccountDetailsButtonAriaLabel?.(item) ?? `View account details for ${item.company}`}\n >\n <span className=\"flex h-5 w-5 shrink-0 items-center justify-center rounded bg-foreground text-[9px] font-semibold uppercase leading-none text-background\">\n {getCompanyInitials(item.company)}\n </span>\n {accountDetailsButtonLabel ? (\n <span className=\"inline-flex min-w-0 items-center gap-1.5 text-xs font-medium text-foreground\">\n {accountDetailsButtonLabel(item)}\n </span>\n ) : (\n <span className=\"truncate text-xs font-medium text-foreground\">{item.company}</span>\n )}\n <span className=\"flex shrink-0 items-center gap-1.5\">\n <span\n aria-hidden\n className={`h-1.5 w-1.5 shrink-0 rounded-full ${STATUS_DOT_CLASS[item.statusColor] ?? \"bg-muted-foreground/40\"}`}\n />\n {/* \"View account\" expands in on hover via an animated grid column */}\n <span className=\"-ml-1.5 grid grid-cols-[0fr] transition-all duration-200 ease-out group-hover/account:ml-0 group-hover/account:grid-cols-[1fr]\">\n <span className=\"overflow-hidden\">\n <span className=\"block whitespace-nowrap text-xs font-medium text-muted-foreground/80\">\n View account\n </span>\n </span>\n </span>\n <ChevronRight className=\"h-3 w-3 shrink-0 text-muted-foreground/50 transition-transform duration-150 group-hover/account:translate-x-0.5 group-hover/account:text-muted-foreground\" />\n </span>\n </button>\n </TooltipTrigger>\n <TooltipContent>Open account panel</TooltipContent>\n </Tooltip>\n </TooltipProvider>\n {renderMetadataExtra?.(item)}\n </>\n )\n\n const timelineSection = sections.timeline && timelineEvents.length > 0 ? (\n <TimelineSection\n timelineEvents={timelineEvents}\n showTimeline={showTimeline}\n setShowTimeline={setShowTimeline}\n showSystemEvents={showSystemEvents}\n setShowSystemEvents={setShowSystemEvents}\n attentionCount={attentionCount}\n sysEvtConfig={sysEvtConfig}\n lastActivityTime={lastActivityTime}\n isCasePanel={isCasePanelV2}\n />\n ) : null\n\n const suggestedActionsSection = sections.suggestedActions ? (\n <SignalApproval.Gate>\n <SuggestedActions\n actions={suggestedActions}\n accountContacts={accountContacts}\n signature={emailSignature}\n iconMap={iconMap}\n onDismiss={(id) => console.log(\"Dismiss action:\", id)}\n onSend={(id) => console.log(\"Send action:\", id)}\n onSaveDraft={(id) => console.log(\"Save draft:\", id)}\n onDuplicate={handleDuplicate}\n onOpenAccountDetails={onOpenEntityPanel}\n onOpenRecentActivity={onOpenRecentActivity}\n onMarkComplete={(id) => console.log(\"Mark complete:\", id)}\n onDispatchAgent={(id) => console.log(\"Dispatch agent:\", id)}\n />\n </SignalApproval.Gate>\n ) : null\n\n return (\n <SignalApproval.Root\n key={item.id}\n companyName={item.company}\n labels={signalLabels}\n hideApproveButton={hideApproveButton}\n approveButtonIconUrl={approveButtonIconUrl}\n opportunityPreview={opportunityPreview}\n onRequestApproval={onRequestApproval}\n initialApprovalState={getSignalApprovalState?.(item)}\n onApprove={(draft) => onSignalApprove?.(item, draft)}\n onApproveFeedback={(reasons, detail) => {\n signalData.onApproveFeedback?.(reasons, detail)\n console.log(\"Approval feedback:\", { taskId: item.id, company: item.company, reasons, detail })\n }}\n onDismiss={(reasons, detail, subReason) => {\n signalData.onDismissFeedback?.(reasons, detail, subReason)\n }}\n >\n <div className=\"mx-auto w-full max-w-3xl p-6 pb-12 md:p-8\">\n <div className=\"pb-8\">\n {/* Header */}\n <div className=\"mb-4 flex items-center gap-2\">\n <span className=\"text-xs text-muted-foreground\">{item.company}</span>\n </div>\n\n <div className=\"mb-3 space-y-3\">\n <div className=\"flex flex-wrap items-start gap-x-3 gap-y-2\">\n <div className=\"min-w-0 flex-1\">\n <h1 className=\"text-2xl font-bold tracking-tight text-foreground\">{item.title}</h1>\n {renderTitleSubtext?.(item)}\n </div>\n {renderTitleExtra?.(item)}\n </div>\n {renderTitleActionRow ? (\n <div className=\"flex w-full flex-wrap items-center gap-2\">\n {renderTitleActionRow(item)}\n </div>\n ) : null}\n </div>\n\n {!isCasePanelV2 && metadataLayout === \"default\" ? (\n <div className=\"mb-6 flex flex-wrap items-center gap-2\">{metadataChips}</div>\n ) : null}\n\n {/* Signal Brief */}\n {sections.signalBrief && (() => {\n const briefHeading = signalBriefCopy?.heading ?? \"Signal brief\"\n const introOpt = signalBriefCopy?.intro\n const briefIntro =\n introOpt === null\n ? null\n : typeof introOpt === \"function\"\n ? introOpt(item)\n : introOpt ?? `Signals indicate a potential opportunity for ${item.company}.`\n const isProminent = briefStyleVariant === \"prominent\";\n return (\n <div className=\"mb-8\">\n {briefHeading ? (\n <h3 className=\"text-xs font-bold text-muted-foreground uppercase tracking-wider mb-3\">{briefHeading}</h3>\n ) : null}\n {briefIntro && !signalData.signalBrief ? (\n <p className={isProminent\n ? \"text-sm text-foreground/70 leading-relaxed mb-2\"\n : \"text-sm text-muted-foreground leading-relaxed mb-2\"\n }>\n {briefIntro}\n </p>\n ) : null}\n {signalData.signalBrief ? (\n <p className={isProminent\n ? \"text-base text-foreground leading-relaxed mb-4\"\n : \"text-sm text-foreground/90 leading-relaxed mb-4\"\n }>\n {signalData.signalBrief}\n </p>\n ) : (\n <p className={isProminent\n ? \"text-base text-foreground leading-relaxed mb-4\"\n : \"text-sm text-foreground/90 leading-relaxed mb-4\"\n }>\n {signalData.whyNow}\n </p>\n )}\n\n {/* Metadata chips relocated beneath the brief (case-panel redesign). */}\n {isCasePanelV2 || metadataLayout === \"below-brief\" ? (\n <div className=\"mb-4 flex flex-wrap items-center gap-2\">{metadataChips}</div>\n ) : null}\n\n {!isCasePanelV2 ? (\n <>\n {/* Before-score content slot (e.g. \"Signals on Case\" chips) */}\n {renderBeforeScore?.(item)}\n\n <ScoreWhyChips\n item={item}\n signalData={signalData}\n onOpenSignalBucket={onOpenSignalBucket}\n />\n <div className=\"mt-4\">\n <SignalApproval.Actions />\n </div>\n </>\n ) : null}\n </div>\n )\n })()}\n\n {isCasePanelV2 ? (\n <>\n {sections.signalBrief ? (\n <>\n {/* Before-score content slot (e.g. status/attention pills) */}\n {renderBeforeScore?.(item)}\n\n <div className=\"mb-8\">\n <ScoreWhyChips\n item={item}\n signalData={signalData}\n onOpenSignalBucket={onOpenSignalBucket}\n />\n <div className=\"mt-4\">\n <SignalApproval.Actions />\n </div>\n </div>\n </>\n ) : null}\n\n {/* After-score content slot (e.g. OpportunityPanel) */}\n {renderAfterScore?.(item)}\n {renderPrimaryAction?.(item)}\n {renderCommentArea?.(item)}\n {timelineSection}\n </>\n ) : (\n <>\n {/* After-score content slot (e.g. OpportunityPanel) */}\n {renderAfterScore?.(item)}\n\n {/* Activity Timeline */}\n {timelineSection}\n </>\n )}\n </div>\n\n {!isCasePanelV2 ? suggestedActionsSection : null}\n {!isCasePanelV2 ? renderDetailExtra?.(item) : null}\n </div>\n </SignalApproval.Root>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Main Component\n// ---------------------------------------------------------------------------\n\nexport function PrototypeInboxView({\n items,\n filterCategories,\n detailSections,\n accountContacts = [],\n buildAccountContacts,\n emailSignature = \"\",\n buildSuggestedActions: buildSuggestedActionsProp,\n buildSourceItems: buildSourceItemsProp,\n getSignalScore: getSignalScoreProp,\n getTimelineEvents,\n iconMap = {},\n hideToolbarActions,\n hideHoverActions,\n onSuggestedActionFeedback,\n onScoreFeedback,\n onOpenSignalBucket,\n headerActions,\n onOpenEntityPanel,\n onOpenRecentActivity,\n onItemSelect,\n defaultViewMode,\n buildEntityChips,\n quickFilterTabs,\n hideAccountsButton,\n accountDetailsLabel,\n onSignalApprove,\n getSignalApprovalState,\n signalLabels,\n hideApproveButton,\n signalBriefCopy,\n briefStyleVariant,\n sectionLayout,\n renderDetailExtra,\n renderBeforeScore,\n renderAfterScore,\n renderPrimaryAction,\n renderCommentArea,\n lastActivityTime,\n timelineSystemEventsConfig,\n attentionCount,\n renderTitleExtra,\n renderTitleActionRow,\n renderTitleSubtext,\n accountDetailsButtonLabel,\n getAccountDetailsButtonAriaLabel,\n sortOptions,\n activeSortId,\n onSortChange,\n}: PrototypeInboxViewProps) {\n const [inboxViewMode, setInboxViewMode] = React.useState<\"inbox\" | \"list\" | \"detail\">(\n defaultViewMode === \"list\" ? \"list\" : defaultViewMode === \"split\" ? \"inbox\" : \"inbox\"\n )\n const [previousViewMode, setPreviousViewMode] = React.useState<\"inbox\" | \"list\">(\"inbox\")\n const [selectedTask, setSelectedTask] = React.useState(items[0])\n const [inboxAssignee, setInboxAssignee] = React.useState<AssigneeFilter>(\"me\")\n const [inboxFilters, setInboxFilters] = React.useState<Record<string, string>>({})\n const [activeQuickFilter, setActiveQuickFilter] = React.useState<string>(\"all\")\n const [splitViewSearch, setSplitViewSearch] = React.useState(\"\")\n\n const sections = React.useMemo(\n () => ({ ...DEFAULT_DETAIL_SECTIONS, ...detailSections }),\n [detailSections],\n )\n\n const resolvedFilterCategories: InboxFilterCategory[] = React.useMemo(\n () =>\n filterCategories ?? [\n {\n id: \"category\",\n label: \"Category\",\n icon: <Tag className=\"h-3.5 w-3.5 text-muted-foreground\" />,\n options: [...new Set(items.map((i) => i.tag1))],\n },\n {\n id: \"account\",\n label: \"Account\",\n icon: <Building className=\"h-3.5 w-3.5 text-muted-foreground\" />,\n options: [...new Set(items.map((i) => i.company))],\n },\n ],\n [filterCategories, items],\n )\n\n const buildSuggestedActions = React.useMemo(\n () => buildSuggestedActionsProp ?? (() => []),\n [buildSuggestedActionsProp],\n )\n\n const buildSourceItems = React.useMemo(\n () => buildSourceItemsProp ?? (() => []),\n [buildSourceItemsProp],\n )\n\n const getSignalScore = React.useMemo(\n () => getSignalScoreProp ?? (() => DEFAULT_SIGNAL_SCORE),\n [getSignalScoreProp],\n )\n\n // Build a map from filter category id → QueueItem field for targeted filtering.\n // Known category ids are mapped explicitly; unknown categories fall back to a\n // broad search across display fields so consumer-defined filters still work.\n const filterFieldMap = React.useMemo<\n Record<string, (item: QueueItem, value: string) => boolean>\n >(() => {\n const map: Record<string, (item: QueueItem, value: string) => boolean> = {}\n for (const cat of resolvedFilterCategories) {\n switch (cat.id) {\n case \"category\":\n case \"signalType\":\n map[cat.id] = (item, v) => item.tag1.toLowerCase() === v.toLowerCase()\n break\n case \"account\":\n map[cat.id] = (item, v) => item.company.toLowerCase() === v.toLowerCase()\n break\n default:\n // Fallback: check all display fields\n map[cat.id] = (item, v) => {\n const lv = v.toLowerCase()\n return (\n item.tag1.toLowerCase() === lv ||\n item.company.toLowerCase() === lv ||\n item.title.toLowerCase().includes(lv) ||\n item.details.toLowerCase().includes(lv)\n )\n }\n }\n }\n return map\n }, [resolvedFilterCategories])\n\n // Filter items for list view based on toolbar filters\n const filteredItems = React.useMemo(() => {\n const activeFilters = Object.entries(inboxFilters).filter(\n ([, value]) => value && value !== \"all\"\n )\n if (activeFilters.length === 0) return items\n return items.filter((item) =>\n activeFilters.every(([key, value]) => {\n const matcher = filterFieldMap[key]\n return matcher ? matcher(item, value) : true\n })\n )\n }, [items, inboxFilters, filterFieldMap])\n\n // Resolve quick filter tabs once — used by both the split view filter and\n // the tab bar render. Each tab's `matchValue` (falling back to `label`) is\n // compared against `item.tag1` so consumer labels can differ from data values.\n type QuickFilterTab = { id: string; label: string; matchValue?: string; count?: number }\n const resolvedQuickFilterTabs = React.useMemo<QuickFilterTab[]>(() => {\n if (quickFilterTabs) return quickFilterTabs\n // Derive default tabs from the actual item tag1 values\n const uniqueTags = [...new Set(items.map((i) => i.tag1))]\n return uniqueTags.map((tag) => ({\n id: tag.toLowerCase().replace(/\\s+/g, \"-\"),\n label: tag,\n }))\n }, [quickFilterTabs, items])\n\n // Compute per-tab counts once so they can be displayed in the tab bar\n const quickFilterTabCounts = React.useMemo(() => {\n const counts: Record<string, number> = {}\n for (const tab of resolvedQuickFilterTabs) {\n const match = (tab.matchValue ?? tab.label).toLowerCase()\n counts[tab.id] = items.filter((i) => i.tag1.toLowerCase() === match).length\n }\n return counts\n }, [resolvedQuickFilterTabs, items])\n\n // Filter items for split view based on quick filter tabs and search\n const splitViewItems = React.useMemo(() => {\n let filtered = items\n // Apply quick filter tab\n if (activeQuickFilter !== \"all\") {\n const activeTab = resolvedQuickFilterTabs.find((t) => t.id === activeQuickFilter)\n if (activeTab) {\n const match = (activeTab.matchValue ?? activeTab.label).toLowerCase()\n filtered = filtered.filter(\n (item) => item.tag1.toLowerCase() === match\n )\n }\n }\n // Apply search input\n if (splitViewSearch.trim()) {\n const q = splitViewSearch.trim().toLowerCase()\n filtered = filtered.filter(\n (item) =>\n item.tag1.toLowerCase().includes(q) ||\n item.company.toLowerCase().includes(q) ||\n item.title.toLowerCase().includes(q)\n )\n }\n return filtered\n }, [items, activeQuickFilter, resolvedQuickFilterTabs, splitViewSearch])\n\n // Grouped items for list view — one group per severity tier so items\n // are labelled correctly (e.g. \"Urgent\", \"High\", \"Active\").\n const inboxGroups = React.useMemo<GroupedListGroup<QueueItem>[]>(() => {\n const urgent = filteredItems.filter((i) => i.statusColor === \"red\")\n const high = filteredItems.filter((i) => i.statusColor === \"orange\")\n const active = filteredItems.filter((i) => i.statusColor !== \"red\" && i.statusColor !== \"orange\")\n return [\n { key: \"urgent\", label: \"Urgent\", items: urgent },\n { key: \"high\", label: \"High\", items: high },\n { key: \"active\", label: \"Active\", items: active },\n ].filter((g) => g.items.length > 0)\n }, [filteredItems])\n\n const renderInboxRow = React.useCallback(\n (item: QueueItem) => (\n <>\n <span className={`h-2 w-2 shrink-0 rounded-full ${dotColorClass(item.statusColor)}`} />\n <span className=\"w-[80px] shrink-0 font-mono text-xs text-muted-foreground/80\">{item.id}</span>\n <span className=\"shrink-0 rounded-md border border-border bg-muted px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground whitespace-nowrap\">{item.tag1}</span>\n <span className=\"min-w-0 flex-1 truncate text-sm font-semibold text-foreground\">{item.title}</span>\n <span className=\"w-[120px] shrink-0 truncate text-xs font-medium text-foreground\">{item.company}</span>\n <span className=\"w-[80px] shrink-0 text-right text-xs text-muted-foreground\">{item.time}</span>\n </>\n ),\n [],\n )\n\n const handleInboxItemSelect = React.useCallback(\n (item: QueueItem) => {\n setSelectedTask(item)\n if (onItemSelect) {\n onItemSelect(item)\n } else if (inboxViewMode === \"list\") {\n setPreviousViewMode(\"list\")\n setInboxViewMode(\"detail\")\n }\n },\n [inboxViewMode, onItemSelect],\n )\n\n const handleBackFromDetail = React.useCallback(() => {\n setInboxViewMode(previousViewMode)\n }, [previousViewMode])\n\n const handleViewModeChange = React.useCallback((id: string) => {\n const mode = id as \"inbox\" | \"list\" | \"detail\"\n if (mode !== \"detail\") {\n setPreviousViewMode(mode)\n }\n setInboxViewMode(mode)\n }, [])\n\n React.useEffect(() => {\n const mql = window.matchMedia(\"(max-width: 768px)\")\n function handleChange(e: MediaQueryListEvent | MediaQueryList) {\n if (e.matches && inboxViewMode === \"inbox\") {\n setPreviousViewMode(\"inbox\")\n setInboxViewMode(\"detail\")\n }\n }\n handleChange(mql)\n mql.addEventListener(\"change\", handleChange)\n return () => mql.removeEventListener(\"change\", handleChange)\n }, [inboxViewMode])\n\n const detailViewProps: DetailViewProps = {\n item: selectedTask,\n sections,\n getSignalScore,\n buildSuggestedActions,\n buildSourceItems,\n getTimelineEvents,\n accountContacts: buildAccountContacts?.(selectedTask) ?? accountContacts,\n emailSignature,\n iconMap,\n onOpenEntityPanel,\n onOpenRecentActivity,\n onSuggestedActionFeedback,\n onScoreFeedback,\n onSignalApprove,\n getSignalApprovalState,\n signalLabels,\n hideApproveButton,\n signalBriefCopy,\n briefStyleVariant,\n sectionLayout,\n renderDetailExtra,\n renderBeforeScore,\n renderAfterScore,\n renderPrimaryAction,\n renderCommentArea,\n lastActivityTime,\n timelineSystemEventsConfig,\n attentionCount,\n renderTitleExtra,\n renderTitleActionRow,\n renderTitleSubtext,\n accountDetailsButtonLabel,\n getAccountDetailsButtonAriaLabel,\n onOpenSignalBucket,\n }\n\n return (\n <div className=\"flex h-full w-full flex-col\">\n {/* Toolbar */}\n <div className=\"flex items-center justify-between border-b border-border bg-background px-4 py-3 shrink-0\">\n <div className=\"flex items-center gap-3\">\n {inboxViewMode === \"detail\" ? (\n <button\n type=\"button\"\n onClick={handleBackFromDetail}\n className=\"flex items-center gap-2 text-sm font-medium text-muted-foreground hover:text-foreground transition-colors\"\n >\n <ArrowLeft className=\"h-4 w-4\" />\n Back\n </button>\n ) : null}\n <h2 className=\"text-lg font-semibold text-foreground\">Inbox</h2>\n <Badge variant=\"secondary\" className=\"bg-muted text-muted-foreground hover:bg-muted font-medium text-[11px] px-2 py-0.5 rounded-md\">\n {items.length}\n </Badge>\n </div>\n <div className=\"flex items-center gap-3\">\n <ViewModeToggle\n modes={[\n { id: \"inbox\", icon: <Columns2 className=\"h-3.5 w-3.5\" />, label: \"Split View\" },\n { id: \"list\", icon: <LayoutList className=\"h-3.5 w-3.5\" />, label: \"List View\" },\n { id: \"detail\", icon: <Square className=\"h-3.5 w-3.5\" />, label: \"Detail View\" },\n ]}\n activeMode={inboxViewMode}\n onModeChange={handleViewModeChange}\n />\n {headerActions}\n </div>\n </div>\n\n {/* View modes */}\n {inboxViewMode === \"detail\" ? (\n <div className=\"flex h-full flex-1 flex-col overflow-hidden bg-background\">\n <div className=\"flex-1 overflow-y-auto\">\n <DetailView {...detailViewProps} />\n </div>\n </div>\n ) : inboxViewMode === \"list\" ? (\n <div className=\"flex-1 overflow-y-auto bg-background\">\n <InboxToolbar\n assignee={inboxAssignee}\n onAssigneeChange={setInboxAssignee}\n filterCategories={resolvedFilterCategories}\n selectedFilters={inboxFilters}\n onFilterChange={(catId, val) =>\n setInboxFilters((prev) => ({ ...prev, [catId]: val }))\n }\n onClearFilters={() => setInboxFilters({})}\n />\n <GroupedListView<QueueItem>\n groups={inboxGroups}\n renderRow={renderInboxRow}\n getItemKey={(item) => item.id}\n selectedKey={selectedTask.id}\n onItemClick={handleInboxItemSelect}\n emptyMessage=\"No inbox items\"\n />\n </div>\n ) : (\n /* Split view */\n <div className=\"flex h-full min-h-0 w-full flex-1\">\n <div className=\"flex h-full min-w-[380px] w-[380px] flex-col border-r border-border bg-background shadow-sm z-10\">\n <div className=\"flex flex-col gap-4 border-b border-border p-4 shrink-0\">\n {!hideToolbarActions && (\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-1\">\n <Button variant=\"outline\" size=\"icon\" className=\"h-8 w-8 text-muted-foreground\"><Eye className=\"w-4 h-4\" /></Button>\n <Button variant=\"outline\" size=\"icon\" className=\"h-8 w-8 text-muted-foreground\"><FileText className=\"w-4 h-4\" /></Button>\n <Button variant=\"outline\" size=\"icon\" className=\"h-8 w-8 text-muted-foreground\"><Clock className=\"w-4 h-4\" /></Button>\n <Button variant=\"outline\" size=\"icon\" className=\"h-8 w-8 text-muted-foreground\"><CheckSquare className=\"w-4 h-4\" /></Button>\n </div>\n <Button size=\"sm\" className=\"h-8 px-4 bg-foreground text-background hover:bg-foreground/90 text-xs font-semibold gap-1.5 rounded-md\">\n <Plus className=\"w-4 h-4\" /> Add Task\n </Button>\n </div>\n )}\n <div className=\"flex items-center gap-2\">\n <div className=\"relative flex-1\">\n <Filter className=\"absolute left-2.5 top-1.5 w-4 h-4 text-muted-foreground\" />\n <Input\n className=\"h-8 pl-8 text-xs bg-background border-border rounded-md shadow-none\"\n placeholder=\"Filter by category...\"\n value={splitViewSearch}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) => setSplitViewSearch(e.target.value)}\n />\n </div>\n {sortOptions && sortOptions.length > 0 && (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button variant=\"outline\" size=\"sm\" className=\"h-8 text-xs font-medium rounded-md shadow-none gap-1.5\">\n <ArrowUpDown className=\"w-3.5 h-3.5\" />\n {sortOptions.find(o => o.id === activeSortId)?.label ?? 'Sort'}\n <ChevronDown className=\"w-3 h-3\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuRadioGroup value={activeSortId ?? ''} onValueChange={(val) => onSortChange?.(val)}>\n {sortOptions.map(option => (\n <DropdownMenuRadioItem key={option.id} value={option.id}>\n {option.label}\n </DropdownMenuRadioItem>\n ))}\n </DropdownMenuRadioGroup>\n </DropdownMenuContent>\n </DropdownMenu>\n )}\n {!hideAccountsButton && (\n <Button variant=\"outline\" size=\"sm\" className=\"h-8 text-xs font-medium rounded-md shadow-none\">\n <Building className=\"w-3.5 h-3.5 mr-1.5\" /> {accountDetailsLabel ?? \"Accounts\"}\n </Button>\n )}\n </div>\n <div className=\"flex items-center gap-1.5 overflow-x-auto pb-1 mt-1 scrollbar-hide\">\n <Button\n size=\"sm\"\n variant={activeQuickFilter === \"all\" ? \"default\" : \"outline\"}\n className={`h-7 rounded-full px-3.5 text-[11px] font-semibold shadow-none ${\n activeQuickFilter === \"all\"\n ? \"bg-foreground text-background hover:bg-foreground/90\"\n : \"bg-transparent border-border text-muted-foreground hover:text-foreground\"\n }`}\n onClick={() => setActiveQuickFilter(\"all\")}\n >\n All\n </Button>\n {resolvedQuickFilterTabs.map((tab) => {\n const count = tab.count ?? quickFilterTabCounts[tab.id]\n return (\n <Button\n key={tab.id}\n size=\"sm\"\n variant={activeQuickFilter === tab.id ? \"default\" : \"outline\"}\n className={`h-7 rounded-full px-3.5 text-[11px] font-medium shadow-none ${\n activeQuickFilter === tab.id\n ? \"bg-foreground text-background hover:bg-foreground/90\"\n : \"bg-transparent border-border text-muted-foreground hover:text-foreground\"\n }`}\n onClick={() => setActiveQuickFilter(tab.id)}\n >\n {tab.label}{count != null && count > 0 ? ` (${count})` : \"\"}\n </Button>\n )\n })}\n </div>\n </div>\n\n <div className=\"flex-1 overflow-y-auto\">\n {splitViewItems.map((item) => (\n <div\n key={item.id}\n onClick={() => { setSelectedTask(item); onItemSelect?.(item) }}\n className={`cursor-pointer border-b border-border p-4 transition-colors group relative border-l-2 ${\n selectedTask.id === item.id\n ? \"bg-muted/30 border-l-brand-purple\"\n : \"bg-transparent border-l-transparent hover:bg-muted/10\"\n }`}\n >\n <div className=\"mb-1.5 flex items-center gap-2\">\n <span className=\"min-w-0 truncate text-[13px] font-semibold text-foreground leading-tight\">{item.title}</span>\n {selectedTask.id !== item.id && item.tag1 && (\n <span className=\"shrink-0 rounded-md border border-border bg-muted/60 px-2 py-0.5 text-[10px] font-medium text-muted-foreground\">\n {item.tag1}\n </span>\n )}\n <span className=\"ml-auto shrink-0 text-[10px] font-medium text-muted-foreground/80\">{item.time}</span>\n </div>\n <div className=\"flex items-start gap-2 mt-2\">\n <span className={`w-1.5 h-1.5 rounded-full shrink-0 mt-1.5 ${dotColorClass(item.statusColor)}`} />\n <span className=\"text-xs text-muted-foreground leading-tight\">{item.details}</span>\n </div>\n {buildEntityChips && (() => {\n const chips = buildEntityChips(item)\n if (!chips.length) return null\n return (\n <div className=\"flex items-center gap-1.5 mt-2 flex-wrap\">\n {chips.map((chip) => (\n <button\n key={chip.id}\n type=\"button\"\n onClick={(e) => { e.stopPropagation(); chip.onClick?.() }}\n className=\"inline-flex items-center gap-1 rounded-md border border-border/60 bg-muted/30 px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground transition-colors hover:bg-muted/50 hover:text-foreground\"\n >\n <span className=\"flex h-3.5 w-3.5 items-center justify-center rounded bg-muted-foreground/10 text-[8px] font-semibold\">{chip.avatarLetter}</span>\n {chip.label}\n </button>\n ))}\n </div>\n )\n })()}\n {!hideHoverActions && (\n <div className={`absolute right-4 bottom-4 flex items-center gap-1.5 bg-background shadow-sm rounded-md px-1 py-0.5 border border-border ${\n selectedTask.id === item.id ? \"opacity-100\" : \"opacity-0 group-hover:opacity-100 transition-opacity\"\n }`}>\n <Button variant=\"ghost\" size=\"icon\" className=\"h-6 w-6 rounded text-muted-foreground hover:text-foreground\"><CheckSquare className=\"w-3.5 h-3.5\" /></Button>\n <Button variant=\"ghost\" size=\"icon\" className=\"h-6 w-6 rounded text-muted-foreground hover:text-foreground\"><Clock className=\"w-3.5 h-3.5\" /></Button>\n </div>\n )}\n </div>\n ))}\n <div className=\"p-4\">\n <Button variant=\"outline\" size=\"sm\" className=\"h-8 text-xs font-semibold rounded-md shadow-none\">See more</Button>\n </div>\n </div>\n </div>\n\n <div className=\"flex h-full flex-1 flex-col overflow-hidden bg-background\">\n <div className=\"flex-1 overflow-y-auto\">\n <DetailView {...detailViewProps} />\n </div>\n </div>\n </div>\n )}\n </div>\n )\n}\n"],"mappings":"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","names":["_a"]}
|
package/package.json
CHANGED
|
@@ -37,21 +37,6 @@ describe("CommentComposer", () => {
|
|
|
37
37
|
expect(screen.getByText("Comment")).toBeDefined();
|
|
38
38
|
});
|
|
39
39
|
|
|
40
|
-
it("uses compact case-panel sizing only when requested", () => {
|
|
41
|
-
render(<CommentComposer density="case-panel" onPost={() => {}} />);
|
|
42
|
-
const input = screen.getByRole("textbox") as HTMLTextAreaElement;
|
|
43
|
-
expect(Number(input.rows)).toBe(1);
|
|
44
|
-
expect(input.className).toContain("!min-h-12");
|
|
45
|
-
|
|
46
|
-
fireEvent.focus(input);
|
|
47
|
-
expect(Number(input.rows)).toBe(1);
|
|
48
|
-
expect(input.className).toContain("!min-h-[76px]");
|
|
49
|
-
|
|
50
|
-
fireEvent.change(input, { target: { value: "draft" } });
|
|
51
|
-
expect(Number(input.rows)).toBe(3);
|
|
52
|
-
expect(input.className).toContain("!min-h-28");
|
|
53
|
-
});
|
|
54
|
-
|
|
55
40
|
it("collapses back to single-row structure on Cancel", () => {
|
|
56
41
|
render(<CommentComposer onPost={() => {}} />);
|
|
57
42
|
const input = screen.getByRole("textbox") as HTMLTextAreaElement;
|
|
@@ -20,7 +20,7 @@ describe("RichTextToolbar", () => {
|
|
|
20
20
|
it("font button has aria-haspopup", () => {
|
|
21
21
|
render(<RichTextToolbar />);
|
|
22
22
|
const fontBtn = screen.getByLabelText("Font family");
|
|
23
|
-
expect(fontBtn.getAttribute("aria-haspopup")).toBe("
|
|
23
|
+
expect(fontBtn.getAttribute("aria-haspopup")).toBe("menu");
|
|
24
24
|
});
|
|
25
25
|
|
|
26
26
|
it("renders all toolbar buttons with data-slot='rich-text-toolbar-button'", () => {
|
|
@@ -54,6 +54,33 @@ describe("RichTextToolbar", () => {
|
|
|
54
54
|
expect(handler).toHaveBeenCalledWith("font");
|
|
55
55
|
});
|
|
56
56
|
|
|
57
|
+
it("opens a font menu and emits the selected font family when options are provided", () => {
|
|
58
|
+
const handler = vi.fn();
|
|
59
|
+
render(
|
|
60
|
+
<RichTextToolbar
|
|
61
|
+
selectedFontFamily="Arial, Helvetica, sans-serif"
|
|
62
|
+
onFontFamilyChange={handler}
|
|
63
|
+
fontOptions={[
|
|
64
|
+
{ label: "Sans Serif", value: "Arial, Helvetica, sans-serif" },
|
|
65
|
+
{ label: "Serif", value: "Georgia, serif" },
|
|
66
|
+
]}
|
|
67
|
+
/>,
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
fireEvent.click(screen.getByLabelText("Font family"));
|
|
71
|
+
expect(screen.getByRole("menu", { name: "Font family" })).toBeDefined();
|
|
72
|
+
fireEvent.click(screen.getByRole("menuitemradio", { name: "Serif" }));
|
|
73
|
+
|
|
74
|
+
expect(handler).toHaveBeenCalledWith("Georgia, serif");
|
|
75
|
+
expect(screen.queryByRole("menu", { name: "Font family" })).toBeNull();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("keeps the font button backward compatible when no menu options are supplied", () => {
|
|
79
|
+
render(<RichTextToolbar />);
|
|
80
|
+
fireEvent.click(screen.getByLabelText("Font family"));
|
|
81
|
+
expect(screen.queryByRole("menu", { name: "Font family" })).toBeNull();
|
|
82
|
+
});
|
|
83
|
+
|
|
57
84
|
it("fires onAction with 'delete' when Delete button clicked", () => {
|
|
58
85
|
const handler = vi.fn();
|
|
59
86
|
render(<RichTextToolbar onAction={handler} />);
|
|
@@ -25,8 +25,6 @@ export interface CommentComposerProps {
|
|
|
25
25
|
/** Current operator (for the avatar). */
|
|
26
26
|
author?: { name?: string; email?: string; avatarUrl?: string | null }
|
|
27
27
|
placeholder?: string
|
|
28
|
-
/** Compact spacing for the case-panel timeline surface. Defaults preserve the standard composer sizing. */
|
|
29
|
-
density?: "default" | "case-panel"
|
|
30
28
|
/** Hint shown in the footer; defaults to the internal-note reassurance. */
|
|
31
29
|
hint?: string
|
|
32
30
|
className?: string
|
|
@@ -36,7 +34,6 @@ function CommentComposer({
|
|
|
36
34
|
onPost,
|
|
37
35
|
author,
|
|
38
36
|
placeholder = "Add a comment or internal note…",
|
|
39
|
-
density = "default",
|
|
40
37
|
hint = "Internal note: only your team sees this",
|
|
41
38
|
className,
|
|
42
39
|
}: CommentComposerProps) {
|
|
@@ -44,8 +41,6 @@ function CommentComposer({
|
|
|
44
41
|
const [focused, setFocused] = React.useState(false)
|
|
45
42
|
const open = focused || text.length > 0
|
|
46
43
|
const canPost = text.trim().length > 0
|
|
47
|
-
const hasDraft = text.length > 0
|
|
48
|
-
const compact = density === "case-panel"
|
|
49
44
|
|
|
50
45
|
const post = () => {
|
|
51
46
|
const value = text.trim()
|
|
@@ -84,19 +79,10 @@ function CommentComposer({
|
|
|
84
79
|
onChange={(e) => setText(e.target.value)}
|
|
85
80
|
onFocus={() => setFocused(true)}
|
|
86
81
|
placeholder={placeholder}
|
|
87
|
-
rows={
|
|
82
|
+
rows={open ? 4 : 1}
|
|
88
83
|
className={cn(
|
|
89
|
-
"resize-none rounded-none border-0 bg-transparent px-5 text-[15px] leading-6 shadow-none outline-none placeholder:text-muted-foreground/60 focus-visible:ring-0 focus-visible:ring-offset-0",
|
|
90
|
-
|
|
91
|
-
compact
|
|
92
|
-
? hasDraft
|
|
93
|
-
? "!min-h-28"
|
|
94
|
-
: open
|
|
95
|
-
? "!min-h-[76px]"
|
|
96
|
-
: "!min-h-12"
|
|
97
|
-
: open
|
|
98
|
-
? "min-h-32"
|
|
99
|
-
: "min-h-14"
|
|
84
|
+
"resize-none rounded-none border-0 bg-transparent px-5 py-4 text-[15px] leading-6 shadow-none outline-none placeholder:text-muted-foreground/60 focus-visible:ring-0 focus-visible:ring-offset-0",
|
|
85
|
+
open ? "min-h-32" : "min-h-14"
|
|
100
86
|
)}
|
|
101
87
|
onKeyDown={(e) => {
|
|
102
88
|
if ((e.metaKey || e.ctrlKey) && e.key === "Enter") {
|
|
@@ -107,7 +93,7 @@ function CommentComposer({
|
|
|
107
93
|
/>
|
|
108
94
|
|
|
109
95
|
{open ? (
|
|
110
|
-
<div className=
|
|
96
|
+
<div className="flex items-center justify-between gap-3 border-t border-border bg-muted/10 px-5 py-4">
|
|
111
97
|
<span className="inline-flex items-center gap-2 text-sm text-muted-foreground">
|
|
112
98
|
<Lock size={16} strokeWidth={1.75} /> {hint}
|
|
113
99
|
</span>
|