@handled-ai/design-system 0.18.58 → 0.19.0-rc.1

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.
Files changed (64) hide show
  1. package/dist/components/badge.d.ts +1 -1
  2. package/dist/components/button.d.ts +1 -1
  3. package/dist/components/case-panel-activity-timeline.d.ts +2 -0
  4. package/dist/components/case-panel-activity-timeline.js +22 -1
  5. package/dist/components/case-panel-activity-timeline.js.map +1 -1
  6. package/dist/components/comment-composer.d.ts +29 -0
  7. package/dist/components/comment-composer.js +102 -0
  8. package/dist/components/comment-composer.js.map +1 -0
  9. package/dist/components/conversation-panel.d.ts +95 -0
  10. package/dist/components/conversation-panel.js +636 -0
  11. package/dist/components/conversation-panel.js.map +1 -0
  12. package/dist/components/detail-view.js +1 -1
  13. package/dist/components/detail-view.js.map +1 -1
  14. package/dist/components/owner-chips.d.ts +59 -0
  15. package/dist/components/owner-chips.js +256 -0
  16. package/dist/components/owner-chips.js.map +1 -0
  17. package/dist/components/pill.d.ts +1 -1
  18. package/dist/components/score-why-chips.d.ts +1 -1
  19. package/dist/components/signal-priority-popover.d.ts +1 -1
  20. package/dist/components/signal-priority-popover.js +16 -7
  21. package/dist/components/signal-priority-popover.js.map +1 -1
  22. package/dist/components/tabs.d.ts +1 -1
  23. package/dist/components/timeline-activity.d.ts +7 -0
  24. package/dist/components/timeline-activity.js +22 -1
  25. package/dist/components/timeline-activity.js.map +1 -1
  26. package/dist/components/virtualized-data-table.js +4 -4
  27. package/dist/components/virtualized-data-table.js.map +1 -1
  28. package/dist/index.d.ts +4 -1
  29. package/dist/index.js +3 -0
  30. package/dist/index.js.map +1 -1
  31. package/dist/internal/safe-html.d.ts +11 -0
  32. package/dist/internal/safe-html.js +222 -0
  33. package/dist/internal/safe-html.js.map +1 -0
  34. package/dist/prototype/index.d.ts +1 -1
  35. package/dist/prototype/prototype-accounts-view.d.ts +1 -1
  36. package/dist/prototype/prototype-admin-view.d.ts +1 -1
  37. package/dist/prototype/prototype-config.d.ts +1 -1
  38. package/dist/prototype/prototype-inbox-view.d.ts +1 -1
  39. package/dist/prototype/prototype-inbox-view.js +2 -0
  40. package/dist/prototype/prototype-inbox-view.js.map +1 -1
  41. package/dist/prototype/prototype-insights-view.d.ts +1 -1
  42. package/dist/prototype/prototype-shell.d.ts +1 -1
  43. package/dist/{signal-priority-popover-QJngMAj7.d.ts → signal-priority-popover-CZitE9xq.d.ts} +11 -2
  44. package/package.json +1 -1
  45. package/src/components/__tests__/comment-composer.test.tsx +57 -0
  46. package/src/components/__tests__/conversation-panel.test.tsx +157 -0
  47. package/src/components/__tests__/owner-chips.test.tsx +100 -0
  48. package/src/components/__tests__/signal-priority-popover.test.tsx +41 -4
  49. package/src/components/__tests__/timeline-activity.test.tsx +55 -0
  50. package/src/components/__tests__/virtualized-data-table-resize.test.tsx +18 -0
  51. package/src/components/case-panel-activity-timeline.tsx +20 -0
  52. package/src/components/comment-composer.tsx +119 -0
  53. package/src/components/conversation-panel.tsx +790 -0
  54. package/src/components/detail-view.tsx +3 -1
  55. package/src/components/owner-chips.tsx +335 -0
  56. package/src/components/signal-priority-popover.tsx +19 -6
  57. package/src/components/timeline-activity.tsx +37 -3
  58. package/src/components/virtualized-data-table.tsx +4 -4
  59. package/src/index.ts +4 -1
  60. package/src/internal/__tests__/safe-html.test.ts +53 -0
  61. package/src/internal/safe-html.ts +284 -0
  62. package/src/prototype/__tests__/detail-view-score-why.test.tsx +34 -0
  63. package/src/prototype/prototype-config.ts +5 -1
  64. package/src/prototype/prototype-inbox-view.tsx +2 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/conversation-panel.tsx"],"sourcesContent":["\"use client\"\n\n/**\n * conversation-panel.tsx — in-case email-thread reader + reply, for the case\n * panel (\"Email response detected\" hub).\n *\n * v1 scope (WIT-853 / WIT-802): INLINE thread reading + reply only.\n * - A collapsible hub header with a pulse badge whose color reflects state:\n * responded (a reply needs you) / awaiting (sent, waiting) / viewing (read-only).\n * - A list of thread rows; clicking one opens a Gmail-style reader inline,\n * right under the row, with collapsible messages + quoted-history toggle.\n * - Reply / Reply-all composer with a signature toggle and two send paths:\n * Preview→Send (in-app) and \"Open draft in Gmail\" (deep link).\n * - A \"playbook stopped\" banner when the customer's reply halted the sequence,\n * and a read-only notice when the operator is not a thread participant.\n *\n * The bottom-right floating dock / side-by-side compare is intentionally OUT of\n * scope here and tracked separately (WIT-855).\n *\n * Presentational: all data + side effects come from the consumer. Email bodies\n * (`bodyHtml`, `quoted.html`) are sanitized before rendering.\n */\n\nimport * as React from \"react\"\nimport {\n ChevronDown,\n ChevronUp,\n CornerUpLeft,\n CheckCheck,\n MailOpen,\n Reply,\n ReplyAll,\n Eye,\n Send,\n Lock,\n Pause,\n GitMerge,\n Check,\n X,\n} from \"lucide-react\"\n\nimport { cn } from \"../lib/utils\"\nimport { getInitials } from \"../lib/user-display\"\nimport { BRAND_ICONS } from \"../lib/icons\"\nimport { htmlToTextSnippet, sanitizeHtml } from \"../internal/safe-html\"\nimport { Avatar, AvatarFallback, AvatarImage } from \"./avatar\"\nimport { Button } from \"./button\"\nimport { Switch } from \"./switch\"\nimport { Textarea } from \"./textarea\"\nimport { RichTextToolbar } from \"./rich-text-toolbar\"\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogDescription,\n DialogFooter,\n} from \"./dialog\"\n\n/* ── Types ───────────────────────────────────────────────────────────────── */\n\nexport interface ConvParticipant {\n name: string\n email: string\n avatarUrl?: string | null\n role?: string\n}\n\nexport interface ConvMessage {\n id: string\n direction: \"inbound\" | \"outbound\"\n from: ConvParticipant\n to: ConvParticipant\n /** Absolute timestamp label, e.g. \"Jun 1, 2026, 9:12 AM\". */\n date: string\n /** Relative label, e.g. \"2 days ago\". */\n ago?: string\n receipt?: { kind: \"new\" | \"read\" | \"opened\" | \"sent\"; label: string }\n /** HTML body (preferred). Sanitized by the component before rendering. */\n bodyHtml?: string\n /** Plain-text fallback when `bodyHtml` is absent. */\n body?: string\n /** Quoted prior message, collapsed behind a toggle. Sanitized before rendering. */\n quoted?: { attr: string; html: string }\n}\n\nexport type ConvStatus = \"responded\" | \"awaiting\" | \"viewing\"\n\nexport interface ConversationThread {\n threadId: string\n subject: string\n status: ConvStatus\n /** Relative label for the most recent activity. */\n lastWhen?: string\n contact: ConvParticipant\n cc?: ConvParticipant[]\n /** Set when this thread's reply halted a playbook (terminal). */\n paused?: { playbook: string } | null\n /** false => operator is not a participant; reply disabled (read-only). */\n canReply?: boolean\n messages: ConvMessage[]\n /** Prefilled reply draft body. */\n draft?: string\n /** Signature text appended to replies (plain text). */\n signature?: string\n}\n\nexport interface ConversationReplyPayload {\n threadId: string\n body: string\n includeSignature: boolean\n replyAll: boolean\n}\n\nexport interface ConversationPanelProps {\n threads: ConversationThread[]\n /** Current operator: drives \"to me\" + the reply avatar. */\n me?: ConvParticipant\n /** Deployment brand, used in the paused-playbook copy. */\n tenantName?: string\n onSendReply?: (payload: ConversationReplyPayload) => void | Promise<void>\n onCreateGmailDraft?: (payload: ConversationReplyPayload) => void\n onOpenInGmail?: (threadId: string) => void\n /** Inline-open this thread initially (defaults to the first responded one). */\n defaultOpenThreadId?: string\n className?: string\n}\n\n/* ── Shared helpers ──────────────────────────────────────────────────────── */\n\n/** Gmail-like reading-pane typography (mirrors timeline-activity's email mode). */\nconst PROSE = cn(\n \"text-sm leading-[1.62] text-foreground/90 break-words\",\n \"[&_p]:my-2 [&_p:first-child]:mt-0 [&_p:last-child]:mb-0\",\n \"[&_a]:text-[#1a73e8] [&_a]:underline-offset-2 hover:[&_a]:underline\",\n \"[&_ul]:my-2 [&_ul]:list-disc [&_ul]:pl-5 [&_ol]:my-2 [&_ol]:list-decimal [&_ol]:pl-5\",\n \"[&_img]:max-w-full [&_img]:h-auto\"\n)\n\nfunction escapeHtml(s: string): string {\n return s.replace(/&/g, \"&amp;\").replace(/</g, \"&lt;\").replace(/>/g, \"&gt;\")\n}\n\n/** Plain-text -> simple paragraph HTML for the Preview / sent-message body. */\nfunction textToHtml(text: string): string {\n return text\n .split(/\\n{2,}/)\n .map((p) => p.trim())\n .filter(Boolean)\n .map((p) => `<p>${escapeHtml(p).replace(/\\n/g, \"<br>\")}</p>`)\n .join(\"\")\n}\n\nfunction GmailMark({ size = 14 }: { size?: number }) {\n return (\n // eslint-disable-next-line @next/next/no-img-element\n <img\n src={BRAND_ICONS.gmail.icon}\n alt=\"Gmail\"\n width={size}\n height={size}\n style={{ width: size, height: size, objectFit: \"contain\", display: \"block\" }}\n />\n )\n}\n\nfunction PersonAvatar({ person, size = \"sm\" }: { person: ConvParticipant; size?: \"sm\" | \"default\" }) {\n return (\n <Avatar size={size}>\n {person.avatarUrl ? <AvatarImage src={person.avatarUrl} alt={person.name} /> : null}\n <AvatarFallback className=\"bg-muted text-muted-foreground text-[10px] font-medium uppercase\">\n {getInitials({ name: person.name, email: person.email })}\n </AvatarFallback>\n </Avatar>\n )\n}\n\nfunction firstName(name: string): string {\n return name.split(\" \")[0] || name\n}\n\nconst STATUS_PILL: Record<ConvStatus, { label: string; cls: string }> = {\n responded: { label: \"New reply\", cls: \"bg-status-active-bg text-status-active-fg border-status-active-border\" },\n awaiting: { label: \"Awaiting\", cls: \"bg-status-pending-bg text-status-pending-fg border-status-pending-border\" },\n viewing: { label: \"Viewing\", cls: \"bg-muted text-muted-foreground border-border\" },\n}\n\nconst STATUS_DOT: Record<ConvStatus, string> = {\n responded: \"bg-status-active-fg\",\n awaiting: \"bg-status-pending-fg\",\n viewing: \"bg-muted-foreground/50\",\n}\n\nfunction effectiveStatus(t: ConversationThread): ConvStatus {\n return t.canReply === false ? \"viewing\" : t.status\n}\n\n/* ── One message (collapsible) ──────────────────────────────────────────── */\n\nfunction MessageView({\n message,\n expanded,\n onToggle,\n me,\n}: {\n message: ConvMessage\n expanded: boolean\n onToggle: () => void\n me?: ConvParticipant\n}) {\n const [quoteOpen, setQuoteOpen] = React.useState(false)\n const snippet =\n message.body?.split(\"\\n\").find(Boolean) ??\n (message.bodyHtml ? htmlToTextSnippet(message.bodyHtml, 140) : \"\")\n\n if (!expanded) {\n return (\n <button\n type=\"button\"\n data-slot=\"conv-message-collapsed\"\n onClick={onToggle}\n className=\"flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left hover:bg-muted/40\"\n >\n <PersonAvatar person={message.from} />\n <span className=\"text-muted-foreground min-w-0 flex-1 truncate text-[13px]\">\n <b className=\"text-foreground\">{firstName(message.from.name)}</b> · {snippet}\n </span>\n <span className=\"text-muted-foreground/60 shrink-0 text-xs\">{message.ago ?? message.date}</span>\n <ChevronDown size={13} className=\"text-muted-foreground shrink-0\" />\n </button>\n )\n }\n\n const toLabel = me && message.to.email === me.email ? \"me\" : firstName(message.to.name)\n\n return (\n <div data-slot=\"conv-message\" className=\"rounded-md border border-border bg-background\">\n <button\n type=\"button\"\n onClick={onToggle}\n className=\"flex w-full items-start gap-2 px-3 py-2 text-left\"\n >\n <PersonAvatar person={message.from} size=\"default\" />\n <span className=\"min-w-0 flex-1\">\n <span className=\"flex flex-wrap items-baseline gap-x-1.5\">\n <span className=\"text-[13px] font-semibold\">{message.from.name}</span>\n <span className=\"text-muted-foreground/60 truncate text-xs\">&lt;{message.from.email}&gt;</span>\n </span>\n <span className=\"text-muted-foreground block text-xs\">\n to <b>{toLabel}</b>\n </span>\n </span>\n <span className=\"flex shrink-0 items-center gap-2\">\n {message.receipt ? (\n <span className=\"text-muted-foreground inline-flex items-center gap-1 text-[11px]\">\n {message.receipt.kind === \"new\" ? (\n <CornerUpLeft size={11} />\n ) : message.receipt.kind === \"read\" ? (\n <CheckCheck size={11} />\n ) : (\n <MailOpen size={11} />\n )}\n {message.receipt.label}\n </span>\n ) : null}\n <span className=\"text-muted-foreground/60 text-xs\">{message.date}</span>\n <ChevronUp size={13} className=\"text-muted-foreground\" />\n </span>\n </button>\n\n <div className=\"px-3 pb-3\">\n {message.bodyHtml ? (\n <div className={PROSE} dangerouslySetInnerHTML={{ __html: sanitizeHtml(message.bodyHtml) }} />\n ) : (\n <div className={cn(PROSE, \"whitespace-pre-line\")}>{message.body}</div>\n )}\n\n {message.quoted ? (\n <div className=\"mt-2\">\n <button\n type=\"button\"\n onClick={() => setQuoteOpen((v) => !v)}\n className=\"text-muted-foreground hover:bg-muted rounded px-1.5 text-xs leading-5\"\n title={quoteOpen ? \"Hide quoted text\" : \"Show quoted text\"}\n >\n •••\n </button>\n {quoteOpen ? (\n <div className=\"border-border text-muted-foreground mt-1 border-l-2 pl-3 text-[13px]\">\n <p className=\"mb-1\" dangerouslySetInnerHTML={{ __html: sanitizeHtml(message.quoted.attr) }} />\n <div className={PROSE} dangerouslySetInnerHTML={{ __html: sanitizeHtml(message.quoted.html) }} />\n </div>\n ) : null}\n </div>\n ) : null}\n </div>\n </div>\n )\n}\n\n/* ── Reply composer ─────────────────────────────────────────────────────── */\n\nfunction ReplyComposer({\n thread,\n me,\n replyAll,\n tenantName,\n onClose,\n onSend,\n onDraft,\n}: {\n thread: ConversationThread\n me?: ConvParticipant\n replyAll: boolean\n tenantName?: string\n onClose: () => void\n onSend: (body: string, includeSignature: boolean) => void | Promise<void>\n onDraft: (body: string, includeSignature: boolean) => void\n}) {\n const [body, setBody] = React.useState(thread.draft ?? \"\")\n const [sig, setSig] = React.useState(true)\n const [preview, setPreview] = React.useState(false)\n const [sending, setSending] = React.useState(false)\n const [sendError, setSendError] = React.useState<string | null>(null)\n const ccList = replyAll ? thread.cc ?? [] : []\n const subject = /^re:/i.test(thread.subject) ? thread.subject : `Re: ${thread.subject}`\n\n const previewHtml = textToHtml(body) + (sig && thread.signature ? textToHtml(thread.signature) : \"\")\n\n const handleSend = async () => {\n setSending(true)\n setSendError(null)\n try {\n await onSend(body, sig)\n setPreview(false)\n } catch (error) {\n setSendError(error instanceof Error ? error.message : \"Could not send this reply. Please try again.\")\n } finally {\n setSending(false)\n }\n }\n\n return (\n <div data-slot=\"conv-reply\" className=\"border-border bg-muted/20 rounded-md border p-3\">\n <div className=\"mb-2 flex items-center gap-2\">\n {me ? <PersonAvatar person={me} /> : null}\n <span className=\"flex-1 text-[13px] font-medium\">\n {replyAll ? (\n <>\n Reply all{\" \"}\n <span className=\"text-muted-foreground font-normal\">· {1 + ccList.length} recipients</span>\n </>\n ) : (\n <>\n Reply to <b>{firstName(thread.contact.name)}</b>\n </>\n )}\n </span>\n <span className=\"text-muted-foreground inline-flex items-center gap-1 text-[11px]\">\n <GitMerge size={11} /> Same thread\n </span>\n <button type=\"button\" onClick={onClose} title=\"Discard reply\" className=\"text-muted-foreground hover:text-foreground\">\n <X size={15} />\n </button>\n </div>\n\n <div className=\"border-border mb-2 space-y-1 border-b pb-2 text-[13px]\">\n <div className=\"flex items-center gap-1.5\">\n <span className=\"text-muted-foreground w-12 shrink-0 text-[11px] font-medium\">To</span>\n <span className=\"font-medium\">{thread.contact.name}</span>\n <span className=\"text-muted-foreground/60 truncate text-xs\">{thread.contact.email}</span>\n </div>\n {replyAll && ccList.length ? (\n <div className=\"flex items-start gap-1.5\">\n <span className=\"text-muted-foreground w-12 shrink-0 text-[11px] font-medium\">Cc</span>\n <span className=\"text-muted-foreground text-xs\">\n {ccList.map((c) => c.name).join(\", \")}\n </span>\n </div>\n ) : null}\n <div className=\"flex items-center gap-1.5\">\n <span className=\"text-muted-foreground w-12 shrink-0 text-[11px] font-medium\">Subject</span>\n <span className=\"truncate\">{subject}</span>\n <span className=\"text-muted-foreground/60 ml-auto inline-flex items-center gap-1 text-[11px]\">\n <Lock size={10} /> on thread\n </span>\n </div>\n </div>\n\n <Textarea\n value={body}\n onChange={(e) => setBody(e.target.value)}\n placeholder=\"Write your reply…\"\n className=\"min-h-28 resize-y text-sm\"\n onKeyDown={(e) => {\n if ((e.metaKey || e.ctrlKey) && e.key === \"Enter\") {\n e.preventDefault()\n setPreview(true)\n }\n }}\n />\n\n {sig && thread.signature ? (\n <div className=\"text-muted-foreground mt-2 whitespace-pre-line border-l-2 border-border pl-3 text-[13px]\">\n <span className=\"text-muted-foreground/60 mr-1\">--</span>\n {thread.signature}\n </div>\n ) : null}\n\n <div className=\"mt-2 flex flex-wrap items-center gap-2\">\n <RichTextToolbar />\n <label className=\"text-muted-foreground ml-auto inline-flex cursor-pointer items-center gap-1.5 text-[12px]\">\n <Switch checked={sig} onCheckedChange={setSig} aria-label=\"Toggle signature\" />\n Signature\n </label>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" disabled={sending} onClick={() => setPreview(true)}>\n <Eye size={14} /> Preview\n </Button>\n <Button type=\"button\" size=\"sm\" disabled={sending} onClick={() => setPreview(true)}>\n <Send size={14} /> Send\n </Button>\n </div>\n\n <Dialog open={preview} onOpenChange={(open) => { if (!sending) setPreview(open) }}>\n <DialogContent className=\"max-w-xl\">\n <DialogHeader>\n <DialogTitle className=\"flex items-center gap-1.5 text-[15px]\">\n <Eye size={15} /> Preview: this is exactly what sends\n </DialogTitle>\n <DialogDescription>\n Stays on the {subject.replace(/^Re:\\s*/i, \"\")} thread. Gmail keeps it threaded.\n </DialogDescription>\n </DialogHeader>\n <div className=\"border-border space-y-1 rounded-md border p-3 text-[13px]\">\n <div>\n <span className=\"text-muted-foreground\">To </span>\n <b>{thread.contact.name}</b>{\" \"}\n <span className=\"text-muted-foreground/60\">&lt;{thread.contact.email}&gt;</span>\n </div>\n {replyAll && ccList.length ? (\n <div className=\"text-muted-foreground\">Cc {ccList.map((c) => c.name).join(\", \")}</div>\n ) : null}\n <div>\n <span className=\"text-muted-foreground\">Subject </span>\n {subject}\n </div>\n </div>\n <div className={cn(PROSE, \"max-h-72 overflow-auto\")} dangerouslySetInnerHTML={{ __html: previewHtml }} />\n {sendError ? (\n <p role=\"alert\" className=\"text-destructive text-sm\">\n {sendError}\n </p>\n ) : null}\n <DialogFooter className=\"sm:justify-between\">\n <button\n type=\"button\"\n disabled={sending}\n onClick={() => {\n setPreview(false)\n onDraft(body, sig)\n }}\n className=\"text-muted-foreground hover:text-foreground inline-flex items-center gap-1.5 text-[13px] disabled:pointer-events-none disabled:opacity-50\"\n >\n <GmailMark size={14} /> Open draft in Gmail\n </button>\n <span className=\"flex items-center gap-2\">\n <Button type=\"button\" variant=\"outline\" size=\"sm\" disabled={sending} onClick={() => setPreview(false)}>\n Keep editing\n </Button>\n <Button\n type=\"button\"\n size=\"sm\"\n disabled={sending}\n onClick={handleSend}\n >\n <Send size={14} /> {sending ? \"Sending...\" : \"Send now\"}\n </Button>\n </span>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n\n {tenantName ? (\n <p className=\"text-muted-foreground/70 mt-2 text-[11px]\">Sends via Gmail · playbooks stay stopped.</p>\n ) : null}\n </div>\n )\n}\n\n/* ── Thread body (messages + footer/composer/done states) ───────────────── */\n\ntype ThreadMode = \"idle\" | \"replying\" | \"sent\" | \"draft\"\n\nfunction ThreadBody({\n thread,\n me,\n tenantName,\n onSendReply,\n onCreateGmailDraft,\n onOpenInGmail,\n}: {\n thread: ConversationThread\n me?: ConvParticipant\n tenantName?: string\n onSendReply?: (p: ConversationReplyPayload) => void | Promise<void>\n onCreateGmailDraft?: (p: ConversationReplyPayload) => void\n onOpenInGmail?: (threadId: string) => void\n}) {\n const canReply = thread.canReply !== false\n const hasCc = !!(thread.cc && thread.cc.length)\n const [mode, setMode] = React.useState<ThreadMode>(\"idle\")\n const [replyAll, setReplyAll] = React.useState(false)\n const [expanded, setExpanded] = React.useState<Record<string, boolean>>(() => {\n const o: Record<string, boolean> = {}\n thread.messages.forEach((m, i) => {\n o[m.id] = i === thread.messages.length - 1\n })\n return o\n })\n\n const toggle = (id: string) => setExpanded((e) => ({ ...e, [id]: !e[id] }))\n\n return (\n <div data-slot=\"conv-thread-body\" className=\"space-y-2\">\n {canReply && thread.paused ? (\n <div className=\"border-status-pending-border bg-status-pending-bg text-status-pending-fg flex items-start gap-2 rounded-md border p-2.5 text-[12px]\">\n <Pause size={13} className=\"mt-0.5 shrink-0\" />\n <span>\n <b>Follow-up actions stopped.</b> Your {thread.paused.playbook} next steps won’t send\n automatically while this conversation is live. Continue it in {tenantName ?? \"the app\"} or Gmail.\n </span>\n </div>\n ) : null}\n\n <div className=\"space-y-1.5\">\n {thread.messages.map((m) => (\n <MessageView key={m.id} message={m} expanded={!!expanded[m.id]} onToggle={() => toggle(m.id)} me={me} />\n ))}\n </div>\n\n {!canReply ? (\n <div className=\"border-border bg-muted/30 text-muted-foreground flex items-start gap-2 rounded-md border p-2.5 text-[12px]\">\n <Eye size={14} className=\"mt-0.5 shrink-0\" />\n <span>\n <b>Viewing only.</b> You’re not a participant on this thread, so replying is disabled here.\n </span>\n </div>\n ) : null}\n\n {canReply && mode === \"idle\" ? (\n <div className=\"flex flex-wrap items-center gap-2\">\n <Button type=\"button\" size=\"sm\" onClick={() => { setReplyAll(false); setMode(\"replying\") }}>\n <Reply size={15} /> Reply\n </Button>\n {hasCc ? (\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => { setReplyAll(true); setMode(\"replying\") }}>\n <ReplyAll size={14} /> Reply all\n </Button>\n ) : null}\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => onOpenInGmail?.(thread.threadId)}>\n <GmailMark size={14} /> Open in Gmail\n </Button>\n <span className=\"text-muted-foreground/70 ml-auto inline-flex items-center gap-1 text-[11px]\">\n <GitMerge size={12} /> Stays on this thread\n </span>\n </div>\n ) : null}\n\n {canReply && mode === \"replying\" ? (\n <ReplyComposer\n thread={thread}\n me={me}\n replyAll={replyAll}\n tenantName={tenantName}\n onClose={() => setMode(\"idle\")}\n onSend={async (body, includeSignature) => {\n await onSendReply?.({ threadId: thread.threadId, body, includeSignature, replyAll })\n setMode(\"sent\")\n }}\n onDraft={(body, includeSignature) => {\n onCreateGmailDraft?.({ threadId: thread.threadId, body, includeSignature, replyAll })\n setMode(\"draft\")\n }}\n />\n ) : null}\n\n {canReply && mode === \"sent\" ? (\n <div className=\"border-status-active-border bg-status-active-bg flex items-center gap-2 rounded-md border p-3 text-[13px]\">\n <Check size={16} className=\"text-status-active-fg shrink-0\" />\n <span className=\"flex-1\">\n <b>{replyAll ? \"Reply all sent\" : \"Reply sent\"}</b> · added to the thread. Delivered to{\" \"}\n <b>{thread.contact.name}</b>. This action stays <b>Pending</b>; playbooks remain stopped.\n </span>\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => setMode(\"idle\")}>\n Done\n </Button>\n </div>\n ) : null}\n\n {canReply && mode === \"draft\" ? (\n <div className=\"border-border bg-muted/30 flex items-center gap-2 rounded-md border p-3 text-[13px]\">\n <GmailMark size={16} />\n <span className=\"flex-1\">\n <b>Draft saved to Gmail.</b> Waiting on the <b>Re: {thread.subject}</b> thread; open it there to finish. Nothing was sent.\n </span>\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => onOpenInGmail?.(thread.threadId)}>\n <GmailMark size={14} /> Open in Gmail\n </Button>\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => setMode(\"idle\")}>\n Done\n </Button>\n </div>\n ) : null}\n </div>\n )\n}\n\n/* ── A thread row + its inline reader ───────────────────────────────────── */\n\nfunction ThreadRow({\n thread,\n open,\n onToggleOpen,\n me,\n tenantName,\n onSendReply,\n onCreateGmailDraft,\n onOpenInGmail,\n}: {\n thread: ConversationThread\n open: boolean\n onToggleOpen: () => void\n} & Pick<ConversationPanelProps, \"me\" | \"tenantName\" | \"onSendReply\" | \"onCreateGmailDraft\" | \"onOpenInGmail\">) {\n const status = effectiveStatus(thread)\n const last = thread.messages[thread.messages.length - 1]\n const who = last?.direction === \"inbound\" ? firstName(last.from.name) : \"You\"\n const lastSnippet =\n last?.body?.split(\"\\n\").find(Boolean) ??\n (last?.bodyHtml ? htmlToTextSnippet(last.bodyHtml, 120) : \"\")\n const pill = STATUS_PILL[status]\n\n return (\n <div data-slot=\"conv-thread\" data-open={open ? \"true\" : undefined} className=\"border-border border-b last:border-b-0\">\n <button\n type=\"button\"\n onClick={onToggleOpen}\n aria-expanded={open}\n className=\"flex w-full items-center gap-2.5 px-3 py-2.5 text-left hover:bg-muted/30\"\n >\n <span className={cn(\"size-2 shrink-0 rounded-full\", STATUS_DOT[status])} aria-hidden />\n <span className=\"min-w-0 flex-1\">\n <span className=\"flex items-center gap-2\">\n <span className=\"truncate text-[13px] font-semibold\">{thread.subject}</span>\n <span className={cn(\"shrink-0 rounded border px-1.5 text-[10px] font-medium leading-4\", pill.cls)}>\n {pill.label}\n </span>\n </span>\n <span className=\"text-muted-foreground block truncate text-xs\">\n <b className=\"text-foreground/80\">{thread.contact.name}</b> · {who}: {lastSnippet}\n </span>\n </span>\n <span className=\"text-muted-foreground/60 shrink-0 text-xs\">{thread.lastWhen}</span>\n {open ? (\n <ChevronUp size={15} className=\"text-muted-foreground shrink-0\" />\n ) : (\n <ChevronDown size={15} className=\"text-muted-foreground shrink-0\" />\n )}\n </button>\n\n {open ? (\n <div className=\"px-3 pb-3\">\n <ThreadBody\n thread={thread}\n me={me}\n tenantName={tenantName}\n onSendReply={onSendReply}\n onCreateGmailDraft={onCreateGmailDraft}\n onOpenInGmail={onOpenInGmail}\n />\n </div>\n ) : null}\n </div>\n )\n}\n\n/* ── The hub ─────────────────────────────────────────────────────────────── */\n\nfunction ConversationPanel({\n threads,\n me,\n tenantName,\n onSendReply,\n onCreateGmailDraft,\n onOpenInGmail,\n defaultOpenThreadId,\n className,\n}: ConversationPanelProps) {\n const responded = threads.filter((t) => t.status === \"responded\" && t.canReply !== false).length\n const awaiting = threads.filter((t) => effectiveStatus(t) === \"awaiting\").length\n const anyPaused = threads.some((t) => t.paused)\n\n const [hubOpen, setHubOpen] = React.useState(true)\n const [openId, setOpenId] = React.useState<string | null>(() => {\n if (defaultOpenThreadId) return defaultOpenThreadId\n const firstResponded = threads.find((t) => t.status === \"responded\" && t.canReply !== false)\n return firstResponded ? firstResponded.threadId : null\n })\n\n if (!threads.length) return null\n\n // Header badge state: a responded reply leads; else an awaiting state; else neutral.\n const badge =\n responded > 0\n ? { label: \"Email response detected\", dot: \"bg-status-active-fg\", ring: \"bg-status-active-fg/30\" }\n : awaiting > 0\n ? { label: \"Awaiting response\", dot: \"bg-status-pending-fg\", ring: \"bg-status-pending-fg/30\" }\n : { label: \"Conversations\", dot: \"bg-muted-foreground/50\", ring: \"bg-muted-foreground/20\" }\n\n const headTitle =\n responded > 0\n ? `${responded} ${responded === 1 ? \"reply needs\" : \"replies need\"} your response`\n : awaiting > 0\n ? `Awaiting response on ${awaiting} ${awaiting === 1 ? \"thread\" : \"threads\"}`\n : `${threads.length} email ${threads.length === 1 ? \"thread\" : \"threads\"}`\n\n return (\n <section\n data-slot=\"conversation-panel\"\n data-responded={responded > 0 ? \"true\" : undefined}\n className={cn(\"border-border bg-background overflow-hidden rounded-xl border\", className)}\n >\n <button\n type=\"button\"\n onClick={() => setHubOpen((v) => !v)}\n aria-expanded={hubOpen}\n className=\"flex w-full items-center gap-3 px-3 py-2.5 text-left\"\n >\n <span\n data-slot=\"conversation-badge\"\n className={cn(\n \"inline-flex items-center gap-1.5 rounded-full px-2 py-0.5 text-[11px] font-semibold\",\n responded > 0\n ? \"bg-status-active-bg text-status-active-fg\"\n : awaiting > 0\n ? \"bg-status-pending-bg text-status-pending-fg\"\n : \"bg-muted text-muted-foreground\"\n )}\n >\n <span className=\"relative inline-flex size-2\">\n <span className={cn(\"absolute inline-flex h-full w-full animate-ping rounded-full opacity-75\", badge.ring)} />\n <span className={cn(\"relative inline-flex size-2 rounded-full\", badge.dot)} />\n </span>\n {badge.label}\n </span>\n <span className=\"min-w-0 flex-1\">\n <span className=\"block truncate text-[13px] font-semibold\">{headTitle}</span>\n <span className=\"text-muted-foreground block truncate text-xs\">\n {threads.length} {threads.length === 1 ? \"thread\" : \"threads\"} on this action\n {anyPaused ? <> · <b>playbook stopped</b></> : null}\n </span>\n </span>\n {hubOpen ? (\n <ChevronUp size={16} className=\"text-muted-foreground shrink-0\" />\n ) : (\n <ChevronDown size={16} className=\"text-muted-foreground shrink-0\" />\n )}\n </button>\n\n {hubOpen ? (\n <div className=\"border-border border-t\">\n {threads.map((t) => (\n <ThreadRow\n key={t.threadId}\n thread={t}\n open={openId === t.threadId}\n onToggleOpen={() => setOpenId((cur) => (cur === t.threadId ? null : t.threadId))}\n me={me}\n tenantName={tenantName}\n onSendReply={onSendReply}\n onCreateGmailDraft={onCreateGmailDraft}\n onOpenInGmail={onOpenInGmail}\n />\n ))}\n </div>\n ) : null}\n </section>\n )\n}\n\nexport { ConversationPanel }\n"],"mappings":"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","names":[]}
@@ -115,7 +115,7 @@ function DetailViewThread({
115
115
  " actions"
116
116
  ] })
117
117
  ] }),
118
- /* @__PURE__ */ jsx("div", { className: "rounded-xl border border-border bg-card overflow-hidden shadow-sm", children })
118
+ /* @__PURE__ */ jsx("div", { className: "rounded-xl border border-border bg-card overflow-hidden shadow-[0_1px_2px_rgba(0,0,0,0.03)]", children })
119
119
  ] });
120
120
  }
121
121
  function ThreadMessage({
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/detail-view.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { ArrowLeft, ChevronDown, ChevronUp, MessageSquare } from \"lucide-react\"\nimport { HoverCard as HoverCardPrimitive } from \"radix-ui\"\nimport { cn } from \"../lib/utils\"\nimport { Button } from \"./button\"\n\n\nexport function DetailViewHeader({\n title,\n breadcrumbs,\n badges,\n onBack,\n}: {\n title: string\n breadcrumbs: React.ReactNode\n badges: React.ReactNode\n onBack?: () => void\n}) {\n return (\n <div className=\"flex flex-col gap-4 mb-6\">\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n {onBack && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-6 px-2 text-muted-foreground hover:text-foreground -ml-2\"\n onClick={onBack}\n >\n <ArrowLeft className=\"w-4 h-4 mr-1\" />\n Back\n </Button>\n )}\n <span className=\"text-muted-foreground/50\">&middot;</span>\n {breadcrumbs}\n </div>\n <h1 className=\"text-2xl font-bold tracking-tight text-foreground\">{title}</h1>\n <div className=\"flex flex-wrap items-center gap-2\">{badges}</div>\n </div>\n )\n}\n\nexport function DetailViewSummary({\n title = \"Here's what I found\",\n children,\n sources,\n actions,\n}: {\n title?: string\n children: React.ReactNode\n sources?: React.ReactNode\n actions?: React.ReactNode\n}) {\n const [showSources, setShowSources] = React.useState(false)\n\n return (\n <div className=\"relative pl-4 mb-8\">\n {/* Blue left border */}\n <div className=\"absolute left-0 top-1 bottom-1 w-1 bg-brand-purple rounded-full\" />\n \n <div className=\"flex items-start justify-between mb-4 gap-3 flex-wrap\">\n <h2 className=\"text-base font-semibold text-foreground\">{title}</h2>\n {actions}\n </div>\n\n <div className=\"space-y-3 text-sm text-foreground/90\">\n {children}\n </div>\n\n {sources && (\n <div className=\"mt-4\">\n <button\n type=\"button\"\n onClick={() => setShowSources(!showSources)}\n className=\"flex items-center gap-1.5 text-[11px] font-bold text-muted-foreground/70 hover:text-foreground transition-colors uppercase tracking-wider\"\n >\n Sources\n <ChevronDown className={cn(\"w-3.5 h-3.5 transition-transform duration-200\", showSources && \"rotate-180\")} />\n </button>\n {showSources && <div className=\"mt-3\">{sources}</div>}\n </div>\n )}\n </div>\n )\n}\n\nexport type SourceDef = {\n id: number | string\n summary: string\n meta: string\n}\n\nexport function Citation({\n number,\n source,\n}: {\n number: number | string\n source?: SourceDef\n}) {\n const badge = (\n <span className=\"inline-flex items-center justify-center w-4 h-4 rounded-full bg-muted border border-border text-[9px] font-medium text-muted-foreground ml-1 align-middle cursor-pointer select-none hover:bg-foreground hover:text-background hover:border-foreground transition-all duration-150\">\n {number}\n </span>\n )\n\n if (!source) return badge\n\n return (\n <HoverCardPrimitive.Root openDelay={200} closeDelay={100}>\n <HoverCardPrimitive.Trigger asChild>{badge}</HoverCardPrimitive.Trigger>\n <HoverCardPrimitive.Portal>\n <HoverCardPrimitive.Content\n side=\"top\"\n align=\"start\"\n sideOffset={6}\n className=\"z-50 w-72 rounded-md border bg-popover p-0 text-popover-foreground shadow-md outline-hidden data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2\"\n >\n <div className=\"p-3 cursor-pointer hover:bg-muted/40 rounded-md transition-colors\">\n <div className=\"text-xs text-foreground leading-relaxed\">{source.summary}</div>\n <div className=\"text-[11px] text-muted-foreground/60 mt-1.5\">{source.meta}</div>\n </div>\n </HoverCardPrimitive.Content>\n </HoverCardPrimitive.Portal>\n </HoverCardPrimitive.Root>\n )\n}\n\nexport function SourceList({ sources }: { sources: SourceDef[] }) {\n return (\n <div className=\"space-y-0.5\">\n {sources.map((source) => (\n <SourceItem key={source.id} source={source} />\n ))}\n </div>\n )\n}\n\nfunction SourceItem({ source }: { source: SourceDef }) {\n return (\n <div className=\"group flex items-start gap-3 cursor-pointer py-2.5 px-2 -mx-2 rounded-lg text-sm transition-colors hover:bg-muted/40\">\n <span className=\"flex-shrink-0 mt-0.5 w-5 text-right text-xs font-medium text-muted-foreground/50 group-hover:text-foreground/70 transition-colors\">\n {source.id}\n </span>\n <div className=\"text-muted-foreground leading-relaxed min-w-0\">\n <span>{source.summary}</span>\n <span className=\"mx-1.5 text-muted-foreground/30\">|</span>\n <span className=\"text-xs text-muted-foreground/50\">{source.meta}</span>\n </div>\n </div>\n )\n}\n\nexport function DetailViewThread({\n title,\n actionCount,\n children,\n}: {\n title: string\n actionCount?: number\n children: React.ReactNode\n}) {\n return (\n <div className=\"mt-8 border-t border-border pt-8\">\n <div className=\"flex items-center justify-between mb-4\">\n <h3 className=\"text-xs font-bold text-muted-foreground uppercase tracking-wider\">{title}</h3>\n {actionCount !== undefined && (\n <span className=\"text-sm text-muted-foreground\">{actionCount} actions</span>\n )}\n </div>\n <div className=\"rounded-xl border border-border bg-card overflow-hidden shadow-sm\">\n {children}\n </div>\n </div>\n )\n}\n\nexport function ThreadMessage({\n icon,\n subject,\n time,\n messageCount,\n threadLink,\n sender,\n senderTime,\n children,\n isExpanded = true,\n}: {\n icon?: React.ReactNode\n subject: string\n time: string\n messageCount?: number\n threadLink?: string\n sender?: string\n senderTime?: string\n children: React.ReactNode\n isExpanded?: boolean\n}) {\n return (\n <div className=\"flex flex-col\">\n {/* Header */}\n <div className=\"flex items-center justify-between p-4 border-b border-border bg-card hover:bg-muted/30 cursor-pointer\">\n <div className=\"flex items-center gap-3\">\n {icon || <MessageSquare className=\"w-4 h-4 text-muted-foreground\" />}\n <span className=\"font-semibold text-sm\">{subject}</span>\n </div>\n <div className=\"flex items-center gap-3 text-xs text-muted-foreground\">\n {time}\n <ChevronUp className=\"w-4 h-4\" />\n </div>\n </div>\n \n {/* Context info */}\n {messageCount !== undefined && (\n <div className=\"px-4 py-2 bg-muted/20 border-b border-border text-xs text-muted-foreground flex items-center gap-2\">\n {messageCount} messages in this thread &middot;\n {threadLink && <a href={threadLink} className=\"underline hover:text-foreground\">View thread</a>}\n </div>\n )}\n\n {/* Message Body */}\n {isExpanded && (\n <div className=\"p-4\">\n {sender && (\n <div className=\"flex items-center justify-between mb-2\">\n <span className=\"font-semibold text-sm\">{sender}</span>\n {senderTime && <span className=\"text-xs text-muted-foreground\">{senderTime}</span>}\n </div>\n )}\n <div className=\"text-sm text-foreground/90 space-y-4 leading-relaxed\">\n {children}\n </div>\n </div>\n )}\n </div>\n )\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/components/detail-view.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { ArrowLeft, ChevronDown, ChevronUp, MessageSquare } from \"lucide-react\"\nimport { HoverCard as HoverCardPrimitive } from \"radix-ui\"\nimport { cn } from \"../lib/utils\"\nimport { Button } from \"./button\"\n\n\nexport function DetailViewHeader({\n title,\n breadcrumbs,\n badges,\n onBack,\n}: {\n title: string\n breadcrumbs: React.ReactNode\n badges: React.ReactNode\n onBack?: () => void\n}) {\n return (\n <div className=\"flex flex-col gap-4 mb-6\">\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n {onBack && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-6 px-2 text-muted-foreground hover:text-foreground -ml-2\"\n onClick={onBack}\n >\n <ArrowLeft className=\"w-4 h-4 mr-1\" />\n Back\n </Button>\n )}\n <span className=\"text-muted-foreground/50\">&middot;</span>\n {breadcrumbs}\n </div>\n <h1 className=\"text-2xl font-bold tracking-tight text-foreground\">{title}</h1>\n <div className=\"flex flex-wrap items-center gap-2\">{badges}</div>\n </div>\n )\n}\n\nexport function DetailViewSummary({\n title = \"Here's what I found\",\n children,\n sources,\n actions,\n}: {\n title?: string\n children: React.ReactNode\n sources?: React.ReactNode\n actions?: React.ReactNode\n}) {\n const [showSources, setShowSources] = React.useState(false)\n\n return (\n <div className=\"relative pl-4 mb-8\">\n {/* Blue left border */}\n <div className=\"absolute left-0 top-1 bottom-1 w-1 bg-brand-purple rounded-full\" />\n \n <div className=\"flex items-start justify-between mb-4 gap-3 flex-wrap\">\n <h2 className=\"text-base font-semibold text-foreground\">{title}</h2>\n {actions}\n </div>\n\n <div className=\"space-y-3 text-sm text-foreground/90\">\n {children}\n </div>\n\n {sources && (\n <div className=\"mt-4\">\n <button\n type=\"button\"\n onClick={() => setShowSources(!showSources)}\n className=\"flex items-center gap-1.5 text-[11px] font-bold text-muted-foreground/70 hover:text-foreground transition-colors uppercase tracking-wider\"\n >\n Sources\n <ChevronDown className={cn(\"w-3.5 h-3.5 transition-transform duration-200\", showSources && \"rotate-180\")} />\n </button>\n {showSources && <div className=\"mt-3\">{sources}</div>}\n </div>\n )}\n </div>\n )\n}\n\nexport type SourceDef = {\n id: number | string\n summary: string\n meta: string\n}\n\nexport function Citation({\n number,\n source,\n}: {\n number: number | string\n source?: SourceDef\n}) {\n const badge = (\n <span className=\"inline-flex items-center justify-center w-4 h-4 rounded-full bg-muted border border-border text-[9px] font-medium text-muted-foreground ml-1 align-middle cursor-pointer select-none hover:bg-foreground hover:text-background hover:border-foreground transition-all duration-150\">\n {number}\n </span>\n )\n\n if (!source) return badge\n\n return (\n <HoverCardPrimitive.Root openDelay={200} closeDelay={100}>\n <HoverCardPrimitive.Trigger asChild>{badge}</HoverCardPrimitive.Trigger>\n <HoverCardPrimitive.Portal>\n <HoverCardPrimitive.Content\n side=\"top\"\n align=\"start\"\n sideOffset={6}\n className=\"z-50 w-72 rounded-md border bg-popover p-0 text-popover-foreground shadow-md outline-hidden data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2\"\n >\n <div className=\"p-3 cursor-pointer hover:bg-muted/40 rounded-md transition-colors\">\n <div className=\"text-xs text-foreground leading-relaxed\">{source.summary}</div>\n <div className=\"text-[11px] text-muted-foreground/60 mt-1.5\">{source.meta}</div>\n </div>\n </HoverCardPrimitive.Content>\n </HoverCardPrimitive.Portal>\n </HoverCardPrimitive.Root>\n )\n}\n\nexport function SourceList({ sources }: { sources: SourceDef[] }) {\n return (\n <div className=\"space-y-0.5\">\n {sources.map((source) => (\n <SourceItem key={source.id} source={source} />\n ))}\n </div>\n )\n}\n\nfunction SourceItem({ source }: { source: SourceDef }) {\n return (\n <div className=\"group flex items-start gap-3 cursor-pointer py-2.5 px-2 -mx-2 rounded-lg text-sm transition-colors hover:bg-muted/40\">\n <span className=\"flex-shrink-0 mt-0.5 w-5 text-right text-xs font-medium text-muted-foreground/50 group-hover:text-foreground/70 transition-colors\">\n {source.id}\n </span>\n <div className=\"text-muted-foreground leading-relaxed min-w-0\">\n <span>{source.summary}</span>\n <span className=\"mx-1.5 text-muted-foreground/30\">|</span>\n <span className=\"text-xs text-muted-foreground/50\">{source.meta}</span>\n </div>\n </div>\n )\n}\n\nexport function DetailViewThread({\n title,\n actionCount,\n children,\n}: {\n title: string\n actionCount?: number\n children: React.ReactNode\n}) {\n return (\n <div className=\"mt-8 border-t border-border pt-8\">\n <div className=\"flex items-center justify-between mb-4\">\n <h3 className=\"text-xs font-bold text-muted-foreground uppercase tracking-wider\">{title}</h3>\n {actionCount !== undefined && (\n <span className=\"text-sm text-muted-foreground\">{actionCount} actions</span>\n )}\n </div>\n {/* Minimal case-panel shadow (WIT-854): kept subtle but present, ~45%\n lighter than shadow-sm. */}\n <div className=\"rounded-xl border border-border bg-card overflow-hidden shadow-[0_1px_2px_rgba(0,0,0,0.03)]\">\n {children}\n </div>\n </div>\n )\n}\n\nexport function ThreadMessage({\n icon,\n subject,\n time,\n messageCount,\n threadLink,\n sender,\n senderTime,\n children,\n isExpanded = true,\n}: {\n icon?: React.ReactNode\n subject: string\n time: string\n messageCount?: number\n threadLink?: string\n sender?: string\n senderTime?: string\n children: React.ReactNode\n isExpanded?: boolean\n}) {\n return (\n <div className=\"flex flex-col\">\n {/* Header */}\n <div className=\"flex items-center justify-between p-4 border-b border-border bg-card hover:bg-muted/30 cursor-pointer\">\n <div className=\"flex items-center gap-3\">\n {icon || <MessageSquare className=\"w-4 h-4 text-muted-foreground\" />}\n <span className=\"font-semibold text-sm\">{subject}</span>\n </div>\n <div className=\"flex items-center gap-3 text-xs text-muted-foreground\">\n {time}\n <ChevronUp className=\"w-4 h-4\" />\n </div>\n </div>\n \n {/* Context info */}\n {messageCount !== undefined && (\n <div className=\"px-4 py-2 bg-muted/20 border-b border-border text-xs text-muted-foreground flex items-center gap-2\">\n {messageCount} messages in this thread &middot;\n {threadLink && <a href={threadLink} className=\"underline hover:text-foreground\">View thread</a>}\n </div>\n )}\n\n {/* Message Body */}\n {isExpanded && (\n <div className=\"p-4\">\n {sender && (\n <div className=\"flex items-center justify-between mb-2\">\n <span className=\"font-semibold text-sm\">{sender}</span>\n {senderTime && <span className=\"text-xs text-muted-foreground\">{senderTime}</span>}\n </div>\n )}\n <div className=\"text-sm text-foreground/90 space-y-4 leading-relaxed\">\n {children}\n </div>\n </div>\n )}\n </div>\n )\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,59 @@
1
+ import * as React from 'react';
2
+
3
+ /**
4
+ * owner-chips.tsx — disambiguates the two ownership concepts operators kept
5
+ * confusing on the case panel:
6
+ *
7
+ * 1. SIGNAL OWNER — Handled's OWN assignment. Who owns working this
8
+ * signal/action inside Handled. Editable here (assign / reassign /
9
+ * unassign). Replaces the ambiguous bare "Unassigned" chip.
10
+ *
11
+ * 2. ACCOUNT OWNER(S) — read-through from Salesforce. An account can carry
12
+ * more than one (AE + RM). Informational + links out to Salesforce; never
13
+ * assigned from inside Handled. Leads with the Salesforce mark.
14
+ *
15
+ * Single account owner -> a static, non-interactive chip (no dropdown).
16
+ * Multiple account owners -> stacked avatars + a ×N badge and a menu that
17
+ * lists each owner with a link out. This keeps a single owner cheap and quiet
18
+ * while still surfacing the full set when there is more than one.
19
+ *
20
+ * Presentational only: data + handlers come from the consumer (the app).
21
+ */
22
+
23
+ interface OwnerPerson {
24
+ /** Stable id (profile id for signal owner; SF user id for account owner). */
25
+ id?: string;
26
+ name: string;
27
+ email?: string;
28
+ /** e.g. "Relationship Manager", "Account Executive". */
29
+ role?: string;
30
+ /** Avatar image; falls back to initials when absent. */
31
+ avatarUrl?: string | null;
32
+ /** External link (Salesforce) for an account owner. */
33
+ href?: string;
34
+ }
35
+ interface SignalOwnerChipProps {
36
+ /** Current Handled assignee, or null when unassigned. */
37
+ owner: OwnerPerson | null;
38
+ /** Operators the case can be assigned to (preloaded — no fetch on open). */
39
+ assignableOwners?: OwnerPerson[];
40
+ onAssign?: (owner: OwnerPerson) => void;
41
+ onUnassign?: () => void;
42
+ /** Read-only: render a static chip without the assignment menu. */
43
+ disabled?: boolean;
44
+ className?: string;
45
+ }
46
+ declare function SignalOwnerChip({ owner, assignableOwners, onAssign, onUnassign, disabled, className, }: SignalOwnerChipProps): React.JSX.Element;
47
+ interface AccountOwnerChipProps {
48
+ /** Salesforce account owners (RM, AE, …). Empty -> renders nothing. */
49
+ owners: OwnerPerson[];
50
+ className?: string;
51
+ }
52
+ declare function AccountOwnerChip({ owners, className }: AccountOwnerChipProps): React.JSX.Element | null;
53
+ interface OwnerChipsProps extends SignalOwnerChipProps {
54
+ /** Salesforce account owners (read-through). */
55
+ accountOwners?: OwnerPerson[];
56
+ }
57
+ declare function OwnerChips({ accountOwners, className, ...signal }: OwnerChipsProps): React.JSX.Element;
58
+
59
+ export { AccountOwnerChip, type AccountOwnerChipProps, OwnerChips, type OwnerChipsProps, type OwnerPerson, SignalOwnerChip, type SignalOwnerChipProps };
@@ -0,0 +1,256 @@
1
+ "use client"
2
+
3
+ "use client";
4
+ var __defProp = Object.defineProperty;
5
+ var __defProps = Object.defineProperties;
6
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
7
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
10
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
11
+ var __spreadValues = (a, b) => {
12
+ for (var prop in b || (b = {}))
13
+ if (__hasOwnProp.call(b, prop))
14
+ __defNormalProp(a, prop, b[prop]);
15
+ if (__getOwnPropSymbols)
16
+ for (var prop of __getOwnPropSymbols(b)) {
17
+ if (__propIsEnum.call(b, prop))
18
+ __defNormalProp(a, prop, b[prop]);
19
+ }
20
+ return a;
21
+ };
22
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
23
+ var __objRest = (source, exclude) => {
24
+ var target = {};
25
+ for (var prop in source)
26
+ if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
27
+ target[prop] = source[prop];
28
+ if (source != null && __getOwnPropSymbols)
29
+ for (var prop of __getOwnPropSymbols(source)) {
30
+ if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
31
+ target[prop] = source[prop];
32
+ }
33
+ return target;
34
+ };
35
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
36
+ import * as React from "react";
37
+ import {
38
+ ChevronDown,
39
+ ChevronUp,
40
+ Check,
41
+ UserPlus,
42
+ UserMinus,
43
+ Info,
44
+ ArrowUpRight
45
+ } from "lucide-react";
46
+ import { cn } from "../lib/utils.js";
47
+ import { getInitials } from "../lib/user-display.js";
48
+ import { BRAND_ICONS } from "../lib/icons.js";
49
+ import { Avatar, AvatarFallback, AvatarImage } from "./avatar.js";
50
+ import {
51
+ DropdownMenu,
52
+ DropdownMenuTrigger,
53
+ DropdownMenuContent,
54
+ DropdownMenuItem,
55
+ DropdownMenuLabel,
56
+ DropdownMenuSeparator
57
+ } from "./dropdown-menu.js";
58
+ function SalesforceMark({ size = 14 }) {
59
+ return (
60
+ // eslint-disable-next-line @next/next/no-img-element
61
+ /* @__PURE__ */ jsx(
62
+ "img",
63
+ {
64
+ src: BRAND_ICONS.salesforce,
65
+ alt: "Salesforce",
66
+ width: size,
67
+ height: size,
68
+ style: { width: size, height: size, objectFit: "contain", display: "block" }
69
+ }
70
+ )
71
+ );
72
+ }
73
+ function OwnerAvatar({ person, size = "sm" }) {
74
+ return /* @__PURE__ */ jsxs(Avatar, { size, className: "ring-background ring-2", children: [
75
+ person.avatarUrl ? /* @__PURE__ */ jsx(AvatarImage, { src: person.avatarUrl, alt: person.name }) : null,
76
+ /* @__PURE__ */ jsx(AvatarFallback, { className: "bg-muted text-muted-foreground text-[10px] font-medium uppercase", children: getInitials({ name: person.name, email: person.email }) })
77
+ ] });
78
+ }
79
+ const chipBase = "inline-flex h-8 items-center gap-1.5 rounded-lg border border-border bg-background px-2.5 text-[13px] shadow-[0_1px_1px_rgba(0,0,0,0.03)]";
80
+ function SignalOwnerChip({
81
+ owner,
82
+ assignableOwners = [],
83
+ onAssign,
84
+ onUnassign,
85
+ disabled,
86
+ className
87
+ }) {
88
+ const [open, setOpen] = React.useState(false);
89
+ const value = /* @__PURE__ */ jsxs(Fragment, { children: [
90
+ owner ? /* @__PURE__ */ jsx(OwnerAvatar, { person: owner }) : /* @__PURE__ */ jsx("span", { className: "text-muted-foreground inline-flex size-[18px] items-center justify-center", children: /* @__PURE__ */ jsx(UserPlus, { size: 13 }) }),
91
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground text-[11px] font-medium tracking-wide uppercase", children: "Signal owner" }),
92
+ /* @__PURE__ */ jsx("span", { className: "bg-border/70 mx-0.5 h-3.5 w-px", "aria-hidden": true }),
93
+ /* @__PURE__ */ jsx("span", { className: cn("font-medium", owner ? "text-foreground" : "text-muted-foreground"), children: owner ? owner.name.split(" ")[0] : "Unassigned" })
94
+ ] });
95
+ if (disabled || !onAssign && !onUnassign) {
96
+ return /* @__PURE__ */ jsx(
97
+ "span",
98
+ {
99
+ "data-slot": "signal-owner-chip",
100
+ "data-empty": owner ? void 0 : "true",
101
+ className: cn(chipBase, className),
102
+ title: "Who owns this signal inside Handled",
103
+ children: value
104
+ }
105
+ );
106
+ }
107
+ return /* @__PURE__ */ jsxs(DropdownMenu, { open, onOpenChange: setOpen, children: [
108
+ /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
109
+ "button",
110
+ {
111
+ type: "button",
112
+ "data-slot": "signal-owner-chip",
113
+ "data-empty": owner ? void 0 : "true",
114
+ className: cn(chipBase, "hover:bg-muted cursor-pointer transition-colors", className),
115
+ title: "Who owns this signal inside Handled",
116
+ children: [
117
+ value,
118
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground ml-0.5", children: open ? /* @__PURE__ */ jsx(ChevronUp, { size: 13 }) : /* @__PURE__ */ jsx(ChevronDown, { size: 13 }) })
119
+ ]
120
+ }
121
+ ) }),
122
+ /* @__PURE__ */ jsxs(DropdownMenuContent, { align: "start", className: "w-64", children: [
123
+ /* @__PURE__ */ jsxs(DropdownMenuLabel, { className: "flex flex-col gap-0.5", children: [
124
+ /* @__PURE__ */ jsx("span", { className: "text-[13px] font-semibold", children: "Assign signal owner" }),
125
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground text-[11px] font-normal", children: "Who works this inside Handled, separate from the Salesforce account owner." })
126
+ ] }),
127
+ /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
128
+ assignableOwners.map((o) => {
129
+ var _a;
130
+ const active = !!owner && (owner.id ? owner.id === o.id : owner.name === o.name);
131
+ return /* @__PURE__ */ jsxs(
132
+ DropdownMenuItem,
133
+ {
134
+ onSelect: () => onAssign == null ? void 0 : onAssign(o),
135
+ className: "gap-2",
136
+ children: [
137
+ /* @__PURE__ */ jsx(OwnerAvatar, { person: o, size: "default" }),
138
+ /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-col", children: [
139
+ /* @__PURE__ */ jsx("span", { className: "truncate text-[13px] font-medium", children: o.name }),
140
+ o.role ? /* @__PURE__ */ jsx("span", { className: "text-muted-foreground truncate text-[11px]", children: o.role }) : null
141
+ ] }),
142
+ active ? /* @__PURE__ */ jsx(Check, { size: 14, className: "text-foreground ml-auto" }) : null
143
+ ]
144
+ },
145
+ (_a = o.id) != null ? _a : o.name
146
+ );
147
+ }),
148
+ owner && onUnassign ? /* @__PURE__ */ jsxs(Fragment, { children: [
149
+ /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
150
+ /* @__PURE__ */ jsxs(DropdownMenuItem, { onSelect: () => onUnassign(), className: "text-muted-foreground gap-2", children: [
151
+ /* @__PURE__ */ jsx(UserMinus, { size: 13 }),
152
+ " Unassign"
153
+ ] })
154
+ ] }) : null
155
+ ] })
156
+ ] });
157
+ }
158
+ function AccountOwnerChip({ owners, className }) {
159
+ const [open, setOpen] = React.useState(false);
160
+ if (!owners.length) return null;
161
+ const multi = owners.length > 1;
162
+ if (!multi) {
163
+ const only = owners[0];
164
+ const body = /* @__PURE__ */ jsxs(Fragment, { children: [
165
+ /* @__PURE__ */ jsx("span", { className: "inline-flex shrink-0 items-center", children: /* @__PURE__ */ jsx(SalesforceMark, {}) }),
166
+ /* @__PURE__ */ jsx(OwnerAvatar, { person: only }),
167
+ /* @__PURE__ */ jsx("span", { className: "text-foreground font-medium", children: only.name.split(" ")[0] })
168
+ ] });
169
+ return only.href ? /* @__PURE__ */ jsxs(
170
+ "a",
171
+ {
172
+ href: only.href,
173
+ target: "_blank",
174
+ rel: "noopener noreferrer",
175
+ "data-slot": "account-owner-chip",
176
+ className: cn(chipBase, "hover:bg-muted transition-colors", className),
177
+ title: `Account owner in Salesforce \u2014 ${only.name}`,
178
+ children: [
179
+ body,
180
+ /* @__PURE__ */ jsx(ArrowUpRight, { size: 12, className: "text-muted-foreground ml-0.5" })
181
+ ]
182
+ }
183
+ ) : /* @__PURE__ */ jsx(
184
+ "span",
185
+ {
186
+ "data-slot": "account-owner-chip",
187
+ className: cn(chipBase, className),
188
+ title: `Account owner in Salesforce \u2014 ${only.name}`,
189
+ children: body
190
+ }
191
+ );
192
+ }
193
+ return /* @__PURE__ */ jsxs(DropdownMenu, { open, onOpenChange: setOpen, children: [
194
+ /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
195
+ "button",
196
+ {
197
+ type: "button",
198
+ "data-slot": "account-owner-chip",
199
+ "data-multi": "true",
200
+ className: cn(chipBase, "hover:bg-muted cursor-pointer transition-colors", className),
201
+ title: "Account owners in Salesforce",
202
+ children: [
203
+ /* @__PURE__ */ jsx("span", { className: "inline-flex shrink-0 items-center", children: /* @__PURE__ */ jsx(SalesforceMark, {}) }),
204
+ /* @__PURE__ */ jsx("span", { className: "flex -space-x-2", children: owners.map((o) => {
205
+ var _a;
206
+ return /* @__PURE__ */ jsx(OwnerAvatar, { person: o }, (_a = o.id) != null ? _a : o.name);
207
+ }) }),
208
+ /* @__PURE__ */ jsx("span", { className: "text-foreground font-medium", children: "Account owners" }),
209
+ /* @__PURE__ */ jsxs("span", { className: "bg-muted text-muted-foreground rounded px-1 text-[11px] font-semibold tabular-nums", children: [
210
+ "\xD7",
211
+ owners.length
212
+ ] }),
213
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground ml-0.5", children: open ? /* @__PURE__ */ jsx(ChevronUp, { size: 13 }) : /* @__PURE__ */ jsx(ChevronDown, { size: 13 }) })
214
+ ]
215
+ }
216
+ ) }),
217
+ /* @__PURE__ */ jsxs(DropdownMenuContent, { align: "start", className: "w-72", children: [
218
+ /* @__PURE__ */ jsxs(DropdownMenuLabel, { className: "flex items-center gap-1.5 text-[13px]", children: [
219
+ /* @__PURE__ */ jsx(SalesforceMark, {}),
220
+ owners.length,
221
+ " account owners"
222
+ ] }),
223
+ /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
224
+ owners.map((o) => {
225
+ var _a;
226
+ const row = /* @__PURE__ */ jsxs(Fragment, { children: [
227
+ /* @__PURE__ */ jsx(OwnerAvatar, { person: o, size: "default" }),
228
+ /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-col", children: [
229
+ /* @__PURE__ */ jsx("span", { className: "truncate text-[13px] font-medium", children: o.name }),
230
+ o.role ? /* @__PURE__ */ jsx("span", { className: "text-muted-foreground truncate text-[11px]", children: o.role }) : null
231
+ ] }),
232
+ o.href ? /* @__PURE__ */ jsx(ArrowUpRight, { size: 13, className: "text-muted-foreground ml-auto" }) : null
233
+ ] });
234
+ return /* @__PURE__ */ jsx(DropdownMenuItem, { asChild: !!o.href, className: "gap-2", children: o.href ? /* @__PURE__ */ jsx("a", { href: o.href, target: "_blank", rel: "noopener noreferrer", title: `Open ${o.name} in Salesforce`, children: row }) : /* @__PURE__ */ jsx("span", { children: row }) }, (_a = o.id) != null ? _a : o.name);
235
+ }),
236
+ /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
237
+ /* @__PURE__ */ jsxs("div", { className: "text-muted-foreground flex items-center gap-1.5 px-2 py-1.5 text-[11px]", children: [
238
+ /* @__PURE__ */ jsx(Info, { size: 12 }),
239
+ " Synced from Salesforce. Manage owners there."
240
+ ] })
241
+ ] })
242
+ ] });
243
+ }
244
+ function OwnerChips(_a) {
245
+ var _b = _a, { accountOwners = [], className } = _b, signal = __objRest(_b, ["accountOwners", "className"]);
246
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
247
+ /* @__PURE__ */ jsx(SignalOwnerChip, __spreadProps(__spreadValues({}, signal), { className })),
248
+ /* @__PURE__ */ jsx(AccountOwnerChip, { owners: accountOwners, className })
249
+ ] });
250
+ }
251
+ export {
252
+ AccountOwnerChip,
253
+ OwnerChips,
254
+ SignalOwnerChip
255
+ };
256
+ //# sourceMappingURL=owner-chips.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/owner-chips.tsx"],"sourcesContent":["\"use client\"\n\n/**\n * owner-chips.tsx — disambiguates the two ownership concepts operators kept\n * confusing on the case panel:\n *\n * 1. SIGNAL OWNER — Handled's OWN assignment. Who owns working this\n * signal/action inside Handled. Editable here (assign / reassign /\n * unassign). Replaces the ambiguous bare \"Unassigned\" chip.\n *\n * 2. ACCOUNT OWNER(S) — read-through from Salesforce. An account can carry\n * more than one (AE + RM). Informational + links out to Salesforce; never\n * assigned from inside Handled. Leads with the Salesforce mark.\n *\n * Single account owner -> a static, non-interactive chip (no dropdown).\n * Multiple account owners -> stacked avatars + a ×N badge and a menu that\n * lists each owner with a link out. This keeps a single owner cheap and quiet\n * while still surfacing the full set when there is more than one.\n *\n * Presentational only: data + handlers come from the consumer (the app).\n */\n\nimport * as React from \"react\"\nimport {\n ChevronDown,\n ChevronUp,\n Check,\n UserPlus,\n UserMinus,\n Info,\n ArrowUpRight,\n} from \"lucide-react\"\n\nimport { cn } from \"../lib/utils\"\nimport { getInitials } from \"../lib/user-display\"\nimport { BRAND_ICONS } from \"../lib/icons\"\nimport { Avatar, AvatarFallback, AvatarImage } from \"./avatar\"\nimport {\n DropdownMenu,\n DropdownMenuTrigger,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n} from \"./dropdown-menu\"\n\nexport interface OwnerPerson {\n /** Stable id (profile id for signal owner; SF user id for account owner). */\n id?: string\n name: string\n email?: string\n /** e.g. \"Relationship Manager\", \"Account Executive\". */\n role?: string\n /** Avatar image; falls back to initials when absent. */\n avatarUrl?: string | null\n /** External link (Salesforce) for an account owner. */\n href?: string\n}\n\n/* ── shared bits ─────────────────────────────────────────────────────────── */\n\nfunction SalesforceMark({ size = 14 }: { size?: number }) {\n return (\n // eslint-disable-next-line @next/next/no-img-element\n <img\n src={BRAND_ICONS.salesforce}\n alt=\"Salesforce\"\n width={size}\n height={size}\n style={{ width: size, height: size, objectFit: \"contain\", display: \"block\" }}\n />\n )\n}\n\nfunction OwnerAvatar({ person, size = \"sm\" }: { person: OwnerPerson; size?: \"sm\" | \"default\" }) {\n return (\n <Avatar size={size} className=\"ring-background ring-2\">\n {person.avatarUrl ? <AvatarImage src={person.avatarUrl} alt={person.name} /> : null}\n <AvatarFallback className=\"bg-muted text-muted-foreground text-[10px] font-medium uppercase\">\n {getInitials({ name: person.name, email: person.email })}\n </AvatarFallback>\n </Avatar>\n )\n}\n\nconst chipBase =\n \"inline-flex h-8 items-center gap-1.5 rounded-lg border border-border bg-background px-2.5 text-[13px] \" +\n \"shadow-[0_1px_1px_rgba(0,0,0,0.03)]\"\n\n/* ── Signal owner (Handled assignment, editable) ─────────────────────────── */\n\nexport interface SignalOwnerChipProps {\n /** Current Handled assignee, or null when unassigned. */\n owner: OwnerPerson | null\n /** Operators the case can be assigned to (preloaded — no fetch on open). */\n assignableOwners?: OwnerPerson[]\n onAssign?: (owner: OwnerPerson) => void\n onUnassign?: () => void\n /** Read-only: render a static chip without the assignment menu. */\n disabled?: boolean\n className?: string\n}\n\nfunction SignalOwnerChip({\n owner,\n assignableOwners = [],\n onAssign,\n onUnassign,\n disabled,\n className,\n}: SignalOwnerChipProps) {\n const [open, setOpen] = React.useState(false)\n\n const value = (\n <>\n {owner ? (\n <OwnerAvatar person={owner} />\n ) : (\n <span className=\"text-muted-foreground inline-flex size-[18px] items-center justify-center\">\n <UserPlus size={13} />\n </span>\n )}\n <span className=\"text-muted-foreground text-[11px] font-medium tracking-wide uppercase\">\n Signal owner\n </span>\n <span className=\"bg-border/70 mx-0.5 h-3.5 w-px\" aria-hidden />\n <span className={cn(\"font-medium\", owner ? \"text-foreground\" : \"text-muted-foreground\")}>\n {owner ? owner.name.split(\" \")[0] : \"Unassigned\"}\n </span>\n </>\n )\n\n // Read-only or nothing to assign to: static chip.\n if (disabled || (!onAssign && !onUnassign)) {\n return (\n <span\n data-slot=\"signal-owner-chip\"\n data-empty={owner ? undefined : \"true\"}\n className={cn(chipBase, className)}\n title=\"Who owns this signal inside Handled\"\n >\n {value}\n </span>\n )\n }\n\n return (\n <DropdownMenu open={open} onOpenChange={setOpen}>\n <DropdownMenuTrigger asChild>\n <button\n type=\"button\"\n data-slot=\"signal-owner-chip\"\n data-empty={owner ? undefined : \"true\"}\n className={cn(chipBase, \"hover:bg-muted cursor-pointer transition-colors\", className)}\n title=\"Who owns this signal inside Handled\"\n >\n {value}\n <span className=\"text-muted-foreground ml-0.5\">\n {open ? <ChevronUp size={13} /> : <ChevronDown size={13} />}\n </span>\n </button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\" className=\"w-64\">\n <DropdownMenuLabel className=\"flex flex-col gap-0.5\">\n <span className=\"text-[13px] font-semibold\">Assign signal owner</span>\n <span className=\"text-muted-foreground text-[11px] font-normal\">\n Who works this inside Handled, separate from the Salesforce account owner.\n </span>\n </DropdownMenuLabel>\n <DropdownMenuSeparator />\n {assignableOwners.map((o) => {\n const active = !!owner && (owner.id ? owner.id === o.id : owner.name === o.name)\n return (\n <DropdownMenuItem\n key={o.id ?? o.name}\n onSelect={() => onAssign?.(o)}\n className=\"gap-2\"\n >\n <OwnerAvatar person={o} size=\"default\" />\n <span className=\"flex min-w-0 flex-col\">\n <span className=\"truncate text-[13px] font-medium\">{o.name}</span>\n {o.role ? (\n <span className=\"text-muted-foreground truncate text-[11px]\">{o.role}</span>\n ) : null}\n </span>\n {active ? <Check size={14} className=\"text-foreground ml-auto\" /> : null}\n </DropdownMenuItem>\n )\n })}\n {owner && onUnassign ? (\n <>\n <DropdownMenuSeparator />\n <DropdownMenuItem onSelect={() => onUnassign()} className=\"text-muted-foreground gap-2\">\n <UserMinus size={13} /> Unassign\n </DropdownMenuItem>\n </>\n ) : null}\n </DropdownMenuContent>\n </DropdownMenu>\n )\n}\n\n/* ── Account owner(s) (Salesforce, read-through) ─────────────────────────── */\n\nexport interface AccountOwnerChipProps {\n /** Salesforce account owners (RM, AE, …). Empty -> renders nothing. */\n owners: OwnerPerson[]\n className?: string\n}\n\nfunction AccountOwnerChip({ owners, className }: AccountOwnerChipProps) {\n const [open, setOpen] = React.useState(false)\n if (!owners.length) return null\n const multi = owners.length > 1\n\n // Single owner: a quiet, static chip — no dropdown (per product intent).\n if (!multi) {\n const only = owners[0]\n const body = (\n <>\n <span className=\"inline-flex shrink-0 items-center\">\n <SalesforceMark />\n </span>\n <OwnerAvatar person={only} />\n <span className=\"text-foreground font-medium\">{only.name.split(\" \")[0]}</span>\n </>\n )\n return only.href ? (\n <a\n href={only.href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n data-slot=\"account-owner-chip\"\n className={cn(chipBase, \"hover:bg-muted transition-colors\", className)}\n title={`Account owner in Salesforce — ${only.name}`}\n >\n {body}\n <ArrowUpRight size={12} className=\"text-muted-foreground ml-0.5\" />\n </a>\n ) : (\n <span\n data-slot=\"account-owner-chip\"\n className={cn(chipBase, className)}\n title={`Account owner in Salesforce — ${only.name}`}\n >\n {body}\n </span>\n )\n }\n\n // Multiple owners: stacked avatars + ×N + a menu listing each.\n return (\n <DropdownMenu open={open} onOpenChange={setOpen}>\n <DropdownMenuTrigger asChild>\n <button\n type=\"button\"\n data-slot=\"account-owner-chip\"\n data-multi=\"true\"\n className={cn(chipBase, \"hover:bg-muted cursor-pointer transition-colors\", className)}\n title=\"Account owners in Salesforce\"\n >\n <span className=\"inline-flex shrink-0 items-center\">\n <SalesforceMark />\n </span>\n <span className=\"flex -space-x-2\">\n {owners.map((o) => (\n <OwnerAvatar key={o.id ?? o.name} person={o} />\n ))}\n </span>\n <span className=\"text-foreground font-medium\">Account owners</span>\n <span className=\"bg-muted text-muted-foreground rounded px-1 text-[11px] font-semibold tabular-nums\">\n ×{owners.length}\n </span>\n <span className=\"text-muted-foreground ml-0.5\">\n {open ? <ChevronUp size={13} /> : <ChevronDown size={13} />}\n </span>\n </button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\" className=\"w-72\">\n <DropdownMenuLabel className=\"flex items-center gap-1.5 text-[13px]\">\n <SalesforceMark />\n {owners.length} account owners\n </DropdownMenuLabel>\n <DropdownMenuSeparator />\n {owners.map((o) => {\n const row = (\n <>\n <OwnerAvatar person={o} size=\"default\" />\n <span className=\"flex min-w-0 flex-col\">\n <span className=\"truncate text-[13px] font-medium\">{o.name}</span>\n {o.role ? (\n <span className=\"text-muted-foreground truncate text-[11px]\">{o.role}</span>\n ) : null}\n </span>\n {o.href ? <ArrowUpRight size={13} className=\"text-muted-foreground ml-auto\" /> : null}\n </>\n )\n return (\n <DropdownMenuItem key={o.id ?? o.name} asChild={!!o.href} className=\"gap-2\">\n {o.href ? (\n <a href={o.href} target=\"_blank\" rel=\"noopener noreferrer\" title={`Open ${o.name} in Salesforce`}>\n {row}\n </a>\n ) : (\n <span>{row}</span>\n )}\n </DropdownMenuItem>\n )\n })}\n <DropdownMenuSeparator />\n <div className=\"text-muted-foreground flex items-center gap-1.5 px-2 py-1.5 text-[11px]\">\n <Info size={12} /> Synced from Salesforce. Manage owners there.\n </div>\n </DropdownMenuContent>\n </DropdownMenu>\n )\n}\n\n/* ── Convenience composite ──────────────────────────────────────────────── */\n\nexport interface OwnerChipsProps extends SignalOwnerChipProps {\n /** Salesforce account owners (read-through). */\n accountOwners?: OwnerPerson[]\n}\n\nfunction OwnerChips({ accountOwners = [], className, ...signal }: OwnerChipsProps) {\n return (\n <>\n <SignalOwnerChip {...signal} className={className} />\n <AccountOwnerChip owners={accountOwners} className={className} />\n </>\n )\n}\n\nexport { SignalOwnerChip, AccountOwnerChip, OwnerChips }\n"],"mappings":"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","names":[]}
@@ -12,7 +12,7 @@ import { VariantProps } from 'class-variance-authority';
12
12
  */
13
13
  type PillStatus = "success" | "warning" | "error" | "neutral" | "info";
14
14
  declare const pillVariants: (props?: ({
15
- variant?: "error" | "default" | "secondary" | "destructive" | "outline" | "ghost" | "neutral" | "info" | "success" | "warning" | null | undefined;
15
+ variant?: "default" | "secondary" | "destructive" | "outline" | "ghost" | "error" | "neutral" | "info" | "success" | "warning" | null | undefined;
16
16
  } & class_variance_authority_types.ClassProp) | undefined) => string;
17
17
  interface PillProps extends React.ComponentProps<"span">, VariantProps<typeof pillVariants> {
18
18
  }
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { Q as QueueItem, l as SignalScoreData, o as SignalScoreUrgencyLabel } from '../signal-priority-popover-QJngMAj7.js';
2
+ import { Q as QueueItem, m as SignalScoreData, p as SignalScoreUrgencyLabel } from '../signal-priority-popover-CZitE9xq.js';
3
3
  import './feedback-primitives.js';
4
4
  import './quick-action-sidebar-nav.js';
5
5
  import './quick-action-modal.js';
@@ -1,6 +1,6 @@
1
1
  import 'react';
2
2
  import './feedback-primitives.js';
3
- export { P as PriorityFactor, S as SignalPriorityPopover, k as SignalPriorityPopoverProps } from '../signal-priority-popover-QJngMAj7.js';
3
+ export { P as PriorityFactor, S as SignalPriorityPopover, k as SignalPriorityPopoverProps, l as SignalPriorityScoreDisplay } from '../signal-priority-popover-CZitE9xq.js';
4
4
  import './quick-action-sidebar-nav.js';
5
5
  import './quick-action-modal.js';
6
6
  import './score-breakdown.js';
@@ -161,7 +161,9 @@ function SignalPriorityPopover({
161
161
  className,
162
162
  initialFactorFeedback,
163
163
  onFactorFeedback,
164
- initialPriorityFeedback
164
+ initialPriorityFeedback,
165
+ scoreDisplay = "number",
166
+ formulaLabel = "Priority factors"
165
167
  }) {
166
168
  const urgencyLabel = providedLabel != null ? providedLabel : getUrgencyLevel(score);
167
169
  const scoreRange = getUrgencyRange(urgencyLabel);
@@ -202,17 +204,24 @@ function SignalPriorityPopover({
202
204
  className: "z-50 w-[420px] rounded-lg border border-border bg-background shadow-lg p-0",
203
205
  "data-testid": "priority-popover-content",
204
206
  children: [
205
- /* @__PURE__ */ jsxs("div", { className: "p-4 pb-3", children: [
207
+ /* @__PURE__ */ jsxs("div", { className: "p-4 pb-3", "data-testid": "priority-popover-header", children: [
206
208
  /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3", children: [
207
209
  /* @__PURE__ */ jsxs("p", { className: "text-sm font-semibold text-foreground", children: [
208
210
  "Why this is ",
209
211
  urgencyLabel.toLowerCase(),
210
212
  " priority"
211
213
  ] }),
212
- /* @__PURE__ */ jsxs("span", { className: "text-2xl font-bold tabular-nums text-foreground", children: [
213
- score,
214
- /* @__PURE__ */ jsx("span", { className: "text-sm font-normal text-muted-foreground", children: "/100" })
215
- ] })
214
+ scoreDisplay === "number" && /* @__PURE__ */ jsxs(
215
+ "span",
216
+ {
217
+ className: "text-2xl font-bold tabular-nums text-foreground",
218
+ "data-testid": "priority-overall-score",
219
+ children: [
220
+ score,
221
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-normal text-muted-foreground", children: "/100" })
222
+ ]
223
+ }
224
+ )
216
225
  ] }),
217
226
  /* @__PURE__ */ jsxs("div", { className: "mt-1 flex items-center gap-1.5 text-[11px] text-muted-foreground", children: [
218
227
  /* @__PURE__ */ jsx(
@@ -235,7 +244,7 @@ function SignalPriorityPopover({
235
244
  /* @__PURE__ */ jsx("span", { className: "text-[10px] font-bold uppercase tracking-wider text-muted-foreground", children: "Contributing factors" }),
236
245
  /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1 text-[10px] text-muted-foreground", children: [
237
246
  /* @__PURE__ */ jsx(Info, { className: "h-3 w-3" }),
238
- "Score = weighted sum"
247
+ formulaLabel
239
248
  ] })
240
249
  ] }),
241
250
  /* @__PURE__ */ jsx("div", { className: "divide-y divide-border/40", children: factors.map((factor) => /* @__PURE__ */ jsx(