@handled-ai/design-system 0.20.31 → 0.20.33
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/owner-chips.js +1 -1
- package/dist/components/owner-chips.js.map +1 -1
- package/dist/components/score-why-chips.d.ts +1 -1
- package/dist/components/signal-priority-popover.d.ts +1 -1
- package/dist/components/signal-priority-popover.js +14 -4
- package/dist/components/signal-priority-popover.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/prototype/index.d.ts +1 -1
- package/dist/prototype/prototype-accounts-view.d.ts +1 -1
- package/dist/prototype/prototype-admin-view.d.ts +1 -1
- package/dist/prototype/prototype-config.d.ts +1 -1
- package/dist/prototype/prototype-inbox-view.d.ts +1 -1
- package/dist/prototype/prototype-insights-view.d.ts +1 -1
- package/dist/prototype/prototype-shell.d.ts +1 -1
- package/dist/{signal-priority-popover-DIUVhipw.d.ts → signal-priority-popover-BmG4WAxT.d.ts} +17 -1
- package/package.json +1 -1
- package/src/components/__tests__/owner-chips.test.tsx +10 -0
- package/src/components/__tests__/signal-priority-popover.test.tsx +72 -0
- package/src/components/owner-chips.tsx +1 -1
- package/src/components/signal-priority-popover.tsx +50 -21
|
@@ -71,7 +71,7 @@ function SalesforceMark({ size = 13 }) {
|
|
|
71
71
|
);
|
|
72
72
|
}
|
|
73
73
|
function OwnerAvatar({ person, size = "sm" }) {
|
|
74
|
-
return /* @__PURE__ */ jsxs(Avatar, { size, className: "ring-background ring-
|
|
74
|
+
return /* @__PURE__ */ jsxs(Avatar, { size, className: "ring-background ring-1", children: [
|
|
75
75
|
person.avatarUrl ? /* @__PURE__ */ jsx(AvatarImage, { src: person.avatarUrl, alt: person.name }) : null,
|
|
76
76
|
/* @__PURE__ */ jsx(AvatarFallback, { className: "bg-muted text-muted-foreground text-[10px] font-medium uppercase", children: getInitials({ name: person.name, email: person.email }) })
|
|
77
77
|
] });
|
|
@@ -1 +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 * Account owner chips are read-only dropdowns. A single owner still opens a\n * small Salesforce-sourced details menu; multiple owners show stacked avatars\n * and a ×N badge before listing each owner with optional Salesforce links.\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 = 13 }: { 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\n// Component-owned compact sizing. Set directly here (not via consumer\n// className) because cn()/tailwind-merge can strip conflicting overrides.\nconst chipBase =\n \"inline-flex h-7 items-center gap-1 rounded-md border border-border bg-background px-2 text-[12px] \" +\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-4 items-center justify-center\">\n <UserPlus size={12} />\n </span>\n )}\n <span className=\"text-muted-foreground text-[10px] font-medium tracking-wide uppercase\">\n Signal owner\n </span>\n <span className=\"bg-border/70 mx-0.5 h-3 w-px\" aria-hidden />\n <span className={cn(\"font-medium\", owner ? \"text-foreground\" : \"text-muted-foreground\")}>\n {owner ? owner.name : \"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={12} /> : <ChevronDown size={12} />}\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 AccountOwnerSummary({ person }: { person: OwnerPerson }) {\n return (\n <span className=\"flex min-w-0 flex-col\">\n <span className=\"truncate text-[13px] font-medium\">{person.name}</span>\n {person.role ? (\n <span className=\"text-muted-foreground truncate text-[11px]\">{person.role}</span>\n ) : null}\n {person.email ? (\n <span className=\"text-muted-foreground truncate text-[11px]\">{person.email}</span>\n ) : null}\n </span>\n )\n}\n\nfunction AccountOwnerSalesforceLink({ person }: { person: OwnerPerson }) {\n if (!person.href) return null\n\n return (\n <DropdownMenuItem asChild className=\"p-0\">\n <a\n href={person.href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n data-account-owner-salesforce-link=\"true\"\n className=\"text-foreground focus:bg-accent focus:text-accent-foreground flex items-center gap-1.5 rounded-sm px-2 py-1.5 text-[12px] font-medium outline-hidden transition-colors\"\n title={`Open ${person.name} in Salesforce`}\n >\n <ArrowUpRight size={12} className=\"text-muted-foreground\" />\n Open in Salesforce\n </a>\n </DropdownMenuItem>\n )\n}\n\nfunction AccountOwnerRow({ person }: { person: OwnerPerson }) {\n return (\n <>\n <DropdownMenuItem\n disabled\n data-owner-row=\"true\"\n className=\"items-start gap-2 py-1.5 data-[disabled]:opacity-100\"\n >\n <OwnerAvatar person={person} size=\"default\" />\n <AccountOwnerSummary person={person} />\n </DropdownMenuItem>\n <AccountOwnerSalesforceLink person={person} />\n </>\n )\n}\n\nfunction SalesforceReadOnlyHelper() {\n return (\n <div role=\"presentation\" className=\"text-muted-foreground flex items-start gap-1.5 px-2 py-1.5 text-[11px]\">\n <Info size={12} className=\"mt-0.5 shrink-0\" />\n <span>Read-only from Salesforce. Manage owners in Salesforce.</span>\n </div>\n )\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 if (!multi) {\n const only = owners[0]\n\n return (\n <DropdownMenu open={open} onOpenChange={setOpen}>\n <DropdownMenuTrigger asChild>\n <button\n type=\"button\"\n data-slot=\"account-owner-chip\"\n className={cn(chipBase, \"hover:bg-muted cursor-pointer transition-colors\", className)}\n title={`Account owner in Salesforce — ${only.name}`}\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}</span>\n <span className=\"text-muted-foreground ml-0.5\">\n {open ? <ChevronUp size={12} /> : <ChevronDown size={12} />}\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 Account owner\n </DropdownMenuLabel>\n <DropdownMenuSeparator />\n <AccountOwnerRow person={only} />\n <DropdownMenuSeparator />\n <SalesforceReadOnlyHelper />\n </DropdownMenuContent>\n </DropdownMenu>\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-[10px] font-semibold tabular-nums\">\n ×{owners.length}\n </span>\n <span className=\"text-muted-foreground ml-0.5\">\n {open ? <ChevronUp size={12} /> : <ChevronDown size={12} />}\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 <AccountOwnerRow key={o.id ?? o.name} person={o} />\n ))}\n <DropdownMenuSeparator />\n <SalesforceReadOnlyHelper />\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":[]}
|
|
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 * Account owner chips are read-only dropdowns. A single owner still opens a\n * small Salesforce-sourced details menu; multiple owners show stacked avatars\n * and a ×N badge before listing each owner with optional Salesforce links.\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 = 13 }: { 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-1\">\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\n// Component-owned compact sizing. Set directly here (not via consumer\n// className) because cn()/tailwind-merge can strip conflicting overrides.\nconst chipBase =\n \"inline-flex h-7 items-center gap-1 rounded-md border border-border bg-background px-2 text-[12px] \" +\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-4 items-center justify-center\">\n <UserPlus size={12} />\n </span>\n )}\n <span className=\"text-muted-foreground text-[10px] font-medium tracking-wide uppercase\">\n Signal owner\n </span>\n <span className=\"bg-border/70 mx-0.5 h-3 w-px\" aria-hidden />\n <span className={cn(\"font-medium\", owner ? \"text-foreground\" : \"text-muted-foreground\")}>\n {owner ? owner.name : \"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={12} /> : <ChevronDown size={12} />}\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 AccountOwnerSummary({ person }: { person: OwnerPerson }) {\n return (\n <span className=\"flex min-w-0 flex-col\">\n <span className=\"truncate text-[13px] font-medium\">{person.name}</span>\n {person.role ? (\n <span className=\"text-muted-foreground truncate text-[11px]\">{person.role}</span>\n ) : null}\n {person.email ? (\n <span className=\"text-muted-foreground truncate text-[11px]\">{person.email}</span>\n ) : null}\n </span>\n )\n}\n\nfunction AccountOwnerSalesforceLink({ person }: { person: OwnerPerson }) {\n if (!person.href) return null\n\n return (\n <DropdownMenuItem asChild className=\"p-0\">\n <a\n href={person.href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n data-account-owner-salesforce-link=\"true\"\n className=\"text-foreground focus:bg-accent focus:text-accent-foreground flex items-center gap-1.5 rounded-sm px-2 py-1.5 text-[12px] font-medium outline-hidden transition-colors\"\n title={`Open ${person.name} in Salesforce`}\n >\n <ArrowUpRight size={12} className=\"text-muted-foreground\" />\n Open in Salesforce\n </a>\n </DropdownMenuItem>\n )\n}\n\nfunction AccountOwnerRow({ person }: { person: OwnerPerson }) {\n return (\n <>\n <DropdownMenuItem\n disabled\n data-owner-row=\"true\"\n className=\"items-start gap-2 py-1.5 data-[disabled]:opacity-100\"\n >\n <OwnerAvatar person={person} size=\"default\" />\n <AccountOwnerSummary person={person} />\n </DropdownMenuItem>\n <AccountOwnerSalesforceLink person={person} />\n </>\n )\n}\n\nfunction SalesforceReadOnlyHelper() {\n return (\n <div role=\"presentation\" className=\"text-muted-foreground flex items-start gap-1.5 px-2 py-1.5 text-[11px]\">\n <Info size={12} className=\"mt-0.5 shrink-0\" />\n <span>Read-only from Salesforce. Manage owners in Salesforce.</span>\n </div>\n )\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 if (!multi) {\n const only = owners[0]\n\n return (\n <DropdownMenu open={open} onOpenChange={setOpen}>\n <DropdownMenuTrigger asChild>\n <button\n type=\"button\"\n data-slot=\"account-owner-chip\"\n className={cn(chipBase, \"hover:bg-muted cursor-pointer transition-colors\", className)}\n title={`Account owner in Salesforce — ${only.name}`}\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}</span>\n <span className=\"text-muted-foreground ml-0.5\">\n {open ? <ChevronUp size={12} /> : <ChevronDown size={12} />}\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 Account owner\n </DropdownMenuLabel>\n <DropdownMenuSeparator />\n <AccountOwnerRow person={only} />\n <DropdownMenuSeparator />\n <SalesforceReadOnlyHelper />\n </DropdownMenuContent>\n </DropdownMenu>\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-[10px] font-semibold tabular-nums\">\n ×{owners.length}\n </span>\n <span className=\"text-muted-foreground ml-0.5\">\n {open ? <ChevronUp size={12} /> : <ChevronDown size={12} />}\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 <AccountOwnerRow key={o.id ?? o.name} person={o} />\n ))}\n <DropdownMenuSeparator />\n <SalesforceReadOnlyHelper />\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":[]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { Q as QueueItem, m as SignalScoreData, p as SignalScoreUrgencyLabel } from '../signal-priority-popover-
|
|
2
|
+
import { Q as QueueItem, m as SignalScoreData, p as SignalScoreUrgencyLabel } from '../signal-priority-popover-BmG4WAxT.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, l as SignalPriorityScoreDisplay } from '../signal-priority-popover-
|
|
3
|
+
export { P as PriorityFactor, S as SignalPriorityPopover, k as SignalPriorityPopoverProps, l as SignalPriorityScoreDisplay } from '../signal-priority-popover-BmG4WAxT.js';
|
|
4
4
|
import './quick-action-sidebar-nav.js';
|
|
5
5
|
import './quick-action-modal.js';
|
|
6
6
|
import './score-breakdown.js';
|
|
@@ -163,18 +163,29 @@ function SignalPriorityPopover({
|
|
|
163
163
|
onFactorFeedback,
|
|
164
164
|
initialPriorityFeedback,
|
|
165
165
|
scoreDisplay = "number",
|
|
166
|
-
formulaLabel = "Priority factors"
|
|
166
|
+
formulaLabel = "Priority factors",
|
|
167
|
+
renderTrigger,
|
|
168
|
+
open: controlledOpen,
|
|
169
|
+
onOpenChange
|
|
167
170
|
}) {
|
|
168
171
|
const urgencyLabel = providedLabel != null ? providedLabel : getUrgencyLevel(score);
|
|
169
172
|
const scoreRange = getUrgencyRange(urgencyLabel);
|
|
170
|
-
const [
|
|
173
|
+
const [uncontrolledOpen, setUncontrolledOpen] = React.useState(false);
|
|
174
|
+
const open = controlledOpen != null ? controlledOpen : uncontrolledOpen;
|
|
175
|
+
const setOpen = React.useCallback(
|
|
176
|
+
(next) => {
|
|
177
|
+
if (controlledOpen === void 0) setUncontrolledOpen(next);
|
|
178
|
+
onOpenChange == null ? void 0 : onOpenChange(next);
|
|
179
|
+
},
|
|
180
|
+
[controlledOpen, onOpenChange]
|
|
181
|
+
);
|
|
171
182
|
const [feedback, setFeedback] = React.useState(null);
|
|
172
183
|
const triggerDefault = URGENCY_TRIGGER_DEFAULT[urgencyLabel];
|
|
173
184
|
const triggerHover = URGENCY_TRIGGER_HOVER[urgencyLabel];
|
|
174
185
|
const triggerOpen = URGENCY_TRIGGER_OPEN[urgencyLabel];
|
|
175
186
|
const footerFeedbackKey = `priority-${score}-${urgencyLabel}`;
|
|
176
187
|
return /* @__PURE__ */ jsxs(PopoverPrimitive.Root, { open, onOpenChange: setOpen, children: [
|
|
177
|
-
/* @__PURE__ */ jsx(PopoverPrimitive.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs(
|
|
188
|
+
/* @__PURE__ */ jsx(PopoverPrimitive.Trigger, { asChild: true, "data-testid": "priority-popover-trigger", children: renderTrigger ? renderTrigger({ urgencyLabel, open }) : /* @__PURE__ */ jsxs(
|
|
178
189
|
"button",
|
|
179
190
|
{
|
|
180
191
|
type: "button",
|
|
@@ -186,7 +197,6 @@ function SignalPriorityPopover({
|
|
|
186
197
|
open && "outline-2 outline-foreground outline-offset-2",
|
|
187
198
|
className
|
|
188
199
|
),
|
|
189
|
-
"data-testid": "priority-popover-trigger",
|
|
190
200
|
children: [
|
|
191
201
|
urgencyLabel,
|
|
192
202
|
" Priority",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/signal-priority-popover.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { Popover as PopoverPrimitive } from \"radix-ui\"\nimport type { LucideIcon } from \"lucide-react\"\nimport {\n Radar,\n Wallet,\n Link2,\n MessageSquare,\n TrendingDown,\n ArrowUpRight,\n ArrowDownRight,\n Clock,\n Activity,\n Minus,\n ChevronDown,\n ChevronUp,\n Info,\n} from \"lucide-react\"\nimport { cn } from \"../lib/utils\"\nimport { FeedbackFooter, InlineFeedbackControl } from \"./feedback-primitives\"\nimport type { FeedbackChipTree, FeedbackSubmitData, PersistedFeedbackData } from \"./feedback-primitives\"\nimport type { SignalScoreUrgencyLabel } from \"../prototype/prototype-config\"\nimport { SCORE_TONE_CLASSES, URGENCY_SCORE_CLASSES, getUrgencyLevel, getUrgencyRange } from \"./score-semantics\"\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * A single contributing factor in the priority popover.\n */\nexport interface PriorityFactor {\n key: string\n label: string\n /** Lucide icon name (e.g. \"radar\", \"wallet\", \"link-2\", \"message-square\"). */\n icon: string\n /** Drives icon background tint. */\n tone: \"alert\" | \"warn\" | \"info\"\n /** Explicit semantic label - NOT inferred from score+weight. */\n direction: \"raises\" | \"lowers\" | \"neutral\"\n /** Optional display label for the direction text. Keeps semantic direction icon/color unchanged. */\n directionLabel?: string\n /** 0-100 */\n score: number\n /** Optional display label rendered instead of the numeric score cell. */\n displayValueLabel?: string\n /** Evidence text (e.g. \"$3.4M moved in 8h - current treasury balance $0.00\"). */\n rationale: string\n}\n\nexport type SignalPriorityScoreDisplay = \"label\" | \"number\"\n\nexport interface SignalPriorityPopoverProps {\n score: number\n /** Controls whether the overall score number is shown in the popover header. @default \"number\" */\n scoreDisplay?: SignalPriorityScoreDisplay\n /** Short formula/context label shown beside the contributing factors heading. @default \"Priority factors\" */\n formulaLabel?: string\n urgencyLabel?: SignalScoreUrgencyLabel\n /** Synthesis sentence displayed in the popover head. */\n urgencyExplanation?: string\n factors: PriorityFactor[]\n /** e.g. \"Updated 4m ago - model v3.2\" */\n metaText?: string\n /** Negative feedback issue tree. */\n feedbackChips?: FeedbackChipTree[]\n onFeedbackSubmit?: (data: FeedbackSubmitData) => void\n className?: string\n /** Persisted factor-level feedback (keyed by factor key). */\n initialFactorFeedback?: Record<string, { type: \"up\" | \"down\"; detail: string; ownershipLabel?: string }>\n /** Callback when user submits factor-level feedback. */\n onFactorFeedback?: (factorKey: string, type: \"up\" | \"down\" | null, detail?: string) => void\n /** Persisted priority-level feedback for the footer. */\n initialPriorityFeedback?: PersistedFeedbackData | null\n}\n\n// ---------------------------------------------------------------------------\n// Static class maps (required for Tailwind v4 source scanning)\n// ---------------------------------------------------------------------------\n\nconst URGENCY_TRIGGER_DEFAULT: Record<SignalScoreUrgencyLabel, string> = {\n Urgent: URGENCY_SCORE_CLASSES.Urgent.trigger,\n High: URGENCY_SCORE_CLASSES.High.trigger,\n Medium: URGENCY_SCORE_CLASSES.Medium.trigger,\n Low: URGENCY_SCORE_CLASSES.Low.trigger,\n}\n\nconst URGENCY_TRIGGER_HOVER: Record<SignalScoreUrgencyLabel, string> = {\n Urgent: URGENCY_SCORE_CLASSES.Urgent.hover,\n High: URGENCY_SCORE_CLASSES.High.hover,\n Medium: URGENCY_SCORE_CLASSES.Medium.hover,\n Low: URGENCY_SCORE_CLASSES.Low.hover,\n}\n\nconst URGENCY_TRIGGER_OPEN: Record<SignalScoreUrgencyLabel, string> = {\n Urgent: URGENCY_SCORE_CLASSES.Urgent.open,\n High: URGENCY_SCORE_CLASSES.High.open,\n Medium: URGENCY_SCORE_CLASSES.Medium.open,\n Low: URGENCY_SCORE_CLASSES.Low.open,\n}\n\n/** Re-use shared tone classes from score-semantics. */\nconst TONE_ICON_CLASSES: Record<PriorityFactor[\"tone\"], string> = SCORE_TONE_CLASSES as Record<PriorityFactor[\"tone\"], string>\n\nconst DIRECTION_CLASSES: Record<PriorityFactor[\"direction\"], string> = {\n raises: \"text-red-600\",\n lowers: \"text-emerald-600\",\n neutral: \"text-muted-foreground\",\n}\n\n// ---------------------------------------------------------------------------\n// Icon lookup\n// ---------------------------------------------------------------------------\n\nconst FACTOR_ICONS: Record<string, LucideIcon> = {\n radar: Radar,\n wallet: Wallet,\n \"link-2\": Link2,\n \"message-square\": MessageSquare,\n \"trending-down\": TrendingDown,\n \"arrow-up-right\": ArrowUpRight,\n clock: Clock,\n activity: Activity,\n}\n\n// ---------------------------------------------------------------------------\n// Urgency dot color (static map)\n// ---------------------------------------------------------------------------\n\nconst URGENCY_DOT_CLASSES: Record<SignalScoreUrgencyLabel, string> = {\n Urgent: URGENCY_SCORE_CLASSES.Urgent.dot,\n High: URGENCY_SCORE_CLASSES.High.dot,\n Medium: URGENCY_SCORE_CLASSES.Medium.dot,\n Low: URGENCY_SCORE_CLASSES.Low.dot,\n}\n\n// ---------------------------------------------------------------------------\n// Default feedback chips\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_PRIORITY_FEEDBACK_CHIPS: FeedbackChipTree[] = [\n { label: \"Wrong factor weighting\" },\n { label: \"Missing context\" },\n { label: \"Inaccurate data\" },\n { label: \"Stale\" },\n { label: \"Other\" },\n]\n\n// ---------------------------------------------------------------------------\n// Direction icon component\n// ---------------------------------------------------------------------------\n\nfunction DirectionIcon({ direction }: { direction: PriorityFactor[\"direction\"] }) {\n const cls = \"h-2.5 w-2.5\"\n switch (direction) {\n case \"raises\":\n return <ArrowUpRight className={cls} />\n case \"lowers\":\n return <ArrowDownRight className={cls} />\n case \"neutral\":\n return <Minus className={cls} />\n }\n}\n\n// ---------------------------------------------------------------------------\n// PriorityFactorRow\n// ---------------------------------------------------------------------------\n\ninterface PriorityFactorRowProps {\n factor: PriorityFactor\n initialFeedback?: { type: \"up\" | \"down\"; detail: string; ownershipLabel?: string }\n onFactorFeedback?: (factorKey: string, type: \"up\" | \"down\" | null, detail?: string) => void\n}\n\nfunction PriorityFactorRow({ factor, initialFeedback, onFactorFeedback }: PriorityFactorRowProps) {\n const IconComponent = FACTOR_ICONS[factor.icon] ?? Activity\n const toneClasses = TONE_ICON_CLASSES[factor.tone]\n const directionClasses = DIRECTION_CLASSES[factor.direction]\n const directionLabel = factor.directionLabel ?? (\n factor.direction === \"raises\"\n ? \"Raises\"\n : factor.direction === \"lowers\"\n ? \"Lowers\"\n : \"Neutral\"\n )\n\n return (\n <div\n className=\"grid grid-cols-[20px_1fr_auto] gap-x-3 gap-y-1 px-4 py-3\"\n data-testid={`factor-row-${factor.key}`}\n >\n {/* Icon */}\n <div\n className={cn(\n \"flex h-5 w-5 items-center justify-center rounded\",\n toneClasses,\n )}\n >\n <IconComponent className=\"h-3 w-3\" />\n </div>\n\n {/* Label + direction tag */}\n <div className=\"flex items-center gap-2\">\n <span className=\"text-[13px] font-semibold text-foreground\">\n {factor.label}\n </span>\n <span\n className={cn(\n \"inline-flex items-center gap-0.5 text-[10px] font-medium\",\n directionClasses,\n )}\n >\n <DirectionIcon direction={factor.direction} />\n {directionLabel}\n </span>\n </div>\n\n {/* Score number / display label */}\n <div className=\"flex items-center text-right\">\n {factor.displayValueLabel ? (\n <span className=\"text-xs font-semibold text-foreground\">{factor.displayValueLabel}</span>\n ) : (\n <>\n <span className=\"text-sm font-bold tabular-nums\">{factor.score}</span>\n <span className=\"text-xs font-normal text-muted-foreground\">/100</span>\n </>\n )}\n </div>\n\n {/* empty grid cell under icon column */}\n <div />\n\n {/* Rationale */}\n <p className=\"text-xs leading-relaxed text-muted-foreground\">\n {factor.rationale}\n </p>\n\n {/* empty grid cell under score column */}\n <div />\n\n {/* empty grid cell under icon column */}\n <div />\n\n {/* Score track */}\n <div className=\"mt-1 h-0.5 rounded-full bg-muted\">\n <div\n className=\"h-full rounded-full bg-foreground/20\"\n style={{ width: `${Math.min(100, Math.max(0, factor.score))}%` }}\n />\n </div>\n\n {/* empty grid cell under score column */}\n <div />\n\n {/* Factor-level feedback row (spans icon + content columns) */}\n {onFactorFeedback && (\n <>\n <div />\n <div className=\"col-span-2 mt-1\">\n <InlineFeedbackControl\n feedbackKey={factor.key}\n initialFeedback={initialFeedback}\n onFeedback={onFactorFeedback}\n testIdPrefix=\"factor\"\n />\n </div>\n </>\n )}\n </div>\n )\n}\n\n// ---------------------------------------------------------------------------\n// SignalPriorityPopover\n// ---------------------------------------------------------------------------\n\nexport function SignalPriorityPopover({\n score,\n urgencyLabel: providedLabel,\n urgencyExplanation,\n factors,\n metaText,\n feedbackChips,\n onFeedbackSubmit,\n className,\n initialFactorFeedback,\n onFactorFeedback,\n initialPriorityFeedback,\n scoreDisplay = \"number\",\n formulaLabel = \"Priority factors\",\n}: SignalPriorityPopoverProps) {\n const urgencyLabel = providedLabel ?? getUrgencyLevel(score)\n const scoreRange = getUrgencyRange(urgencyLabel)\n\n const [open, setOpen] = React.useState(false)\n const [feedback, setFeedback] = React.useState<\"positive\" | \"negative\" | null>(null)\n\n const triggerDefault = URGENCY_TRIGGER_DEFAULT[urgencyLabel]\n const triggerHover = URGENCY_TRIGGER_HOVER[urgencyLabel]\n const triggerOpen = URGENCY_TRIGGER_OPEN[urgencyLabel]\n\n // Derive a stable feedbackKey for the footer from score + urgencyLabel\n const footerFeedbackKey = `priority-${score}-${urgencyLabel}`\n\n return (\n <PopoverPrimitive.Root open={open} onOpenChange={setOpen}>\n <PopoverPrimitive.Trigger asChild>\n <button\n type=\"button\"\n className={cn(\n \"inline-flex items-center gap-1 rounded-md border px-2.5 py-1 text-xs font-semibold transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n triggerDefault,\n triggerHover,\n open && triggerOpen,\n open && \"outline-2 outline-foreground outline-offset-2\",\n className,\n )}\n data-testid=\"priority-popover-trigger\"\n >\n {urgencyLabel} Priority\n {open ? (\n <ChevronUp className=\"h-3 w-3\" />\n ) : (\n <ChevronDown className=\"h-3 w-3\" />\n )}\n </button>\n </PopoverPrimitive.Trigger>\n\n <PopoverPrimitive.Portal>\n <PopoverPrimitive.Content\n side=\"bottom\"\n align=\"start\"\n sideOffset={8}\n onOpenAutoFocus={(e) => e.preventDefault()}\n className=\"z-50 w-[420px] rounded-lg border border-border bg-background shadow-lg p-0\"\n data-testid=\"priority-popover-content\"\n >\n {/* Head section */}\n <div className=\"p-4 pb-3\" data-testid=\"priority-popover-header\">\n <div className=\"flex items-start justify-between gap-3\">\n <p className=\"text-sm font-semibold text-foreground\">\n Why this is {urgencyLabel.toLowerCase()} priority\n </p>\n {scoreDisplay === \"number\" && (\n <span\n className=\"text-2xl font-bold tabular-nums text-foreground\"\n data-testid=\"priority-overall-score\"\n >\n {score}\n <span className=\"text-sm font-normal text-muted-foreground\">/100</span>\n </span>\n )}\n </div>\n\n {/* Band indicator */}\n <div className=\"mt-1 flex items-center gap-1.5 text-[11px] text-muted-foreground\">\n <span\n className={cn(\n \"inline-block h-2 w-2 rounded-full\",\n URGENCY_DOT_CLASSES[urgencyLabel],\n )}\n />\n {urgencyLabel} range: {scoreRange}\n </div>\n\n {/* Synthesis sentence */}\n {urgencyExplanation && (\n <p className=\"mt-2 text-xs leading-relaxed text-muted-foreground\">\n {urgencyExplanation}\n </p>\n )}\n </div>\n\n {/* Section label */}\n {factors.length > 0 && (\n <>\n <div className=\"flex items-center justify-between border-t border-border px-4 py-2\">\n <span className=\"text-[10px] font-bold uppercase tracking-wider text-muted-foreground\">\n Contributing factors\n </span>\n <span className=\"flex items-center gap-1 text-[10px] text-muted-foreground\">\n <Info className=\"h-3 w-3\" />\n {formulaLabel}\n </span>\n </div>\n\n {/* Factor rows */}\n <div className=\"divide-y divide-border/40\">\n {factors.map((factor) => (\n <PriorityFactorRow\n key={factor.key}\n factor={factor}\n initialFeedback={initialFactorFeedback?.[factor.key]}\n onFactorFeedback={onFactorFeedback}\n />\n ))}\n </div>\n </>\n )}\n\n {/* Feedback footer */}\n {onFeedbackSubmit && (\n <div className=\"border-t border-border\">\n <FeedbackFooter\n feedback={feedback}\n onFeedbackChange={setFeedback}\n onSubmit={onFeedbackSubmit}\n metaText={metaText}\n negativeChips={feedbackChips ?? DEFAULT_PRIORITY_FEEDBACK_CHIPS}\n positivePrompt=\"Thanks. Anything to keep about this score?\"\n className=\"px-4 py-3\"\n initialFeedback={initialPriorityFeedback}\n feedbackKey={footerFeedbackKey}\n />\n </div>\n )}\n </PopoverPrimitive.Content>\n </PopoverPrimitive.Portal>\n </PopoverPrimitive.Root>\n )\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/signal-priority-popover.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { Popover as PopoverPrimitive } from \"radix-ui\"\nimport type { LucideIcon } from \"lucide-react\"\nimport {\n Radar,\n Wallet,\n Link2,\n MessageSquare,\n TrendingDown,\n ArrowUpRight,\n ArrowDownRight,\n Clock,\n Activity,\n Minus,\n ChevronDown,\n ChevronUp,\n Info,\n} from \"lucide-react\"\nimport { cn } from \"../lib/utils\"\nimport { FeedbackFooter, InlineFeedbackControl } from \"./feedback-primitives\"\nimport type { FeedbackChipTree, FeedbackSubmitData, PersistedFeedbackData } from \"./feedback-primitives\"\nimport type { SignalScoreUrgencyLabel } from \"../prototype/prototype-config\"\nimport { SCORE_TONE_CLASSES, URGENCY_SCORE_CLASSES, getUrgencyLevel, getUrgencyRange } from \"./score-semantics\"\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * A single contributing factor in the priority popover.\n */\nexport interface PriorityFactor {\n key: string\n label: string\n /** Lucide icon name (e.g. \"radar\", \"wallet\", \"link-2\", \"message-square\"). */\n icon: string\n /** Drives icon background tint. */\n tone: \"alert\" | \"warn\" | \"info\"\n /** Explicit semantic label - NOT inferred from score+weight. */\n direction: \"raises\" | \"lowers\" | \"neutral\"\n /** Optional display label for the direction text. Keeps semantic direction icon/color unchanged. */\n directionLabel?: string\n /** 0-100 */\n score: number\n /** Optional display label rendered instead of the numeric score cell. */\n displayValueLabel?: string\n /** Evidence text (e.g. \"$3.4M moved in 8h - current treasury balance $0.00\"). */\n rationale: string\n}\n\nexport type SignalPriorityScoreDisplay = \"label\" | \"number\"\n\nexport interface SignalPriorityPopoverProps {\n score: number\n /** Controls whether the overall score number is shown in the popover header. @default \"number\" */\n scoreDisplay?: SignalPriorityScoreDisplay\n /** Short formula/context label shown beside the contributing factors heading. @default \"Priority factors\" */\n formulaLabel?: string\n urgencyLabel?: SignalScoreUrgencyLabel\n /** Synthesis sentence displayed in the popover head. */\n urgencyExplanation?: string\n factors: PriorityFactor[]\n /** e.g. \"Updated 4m ago - model v3.2\" */\n metaText?: string\n /** Negative feedback issue tree. */\n feedbackChips?: FeedbackChipTree[]\n onFeedbackSubmit?: (data: FeedbackSubmitData) => void\n className?: string\n /** Persisted factor-level feedback (keyed by factor key). */\n initialFactorFeedback?: Record<string, { type: \"up\" | \"down\"; detail: string; ownershipLabel?: string }>\n /** Callback when user submits factor-level feedback. */\n onFactorFeedback?: (factorKey: string, type: \"up\" | \"down\" | null, detail?: string) => void\n /** Persisted priority-level feedback for the footer. */\n initialPriorityFeedback?: PersistedFeedbackData | null\n /**\n * Optional custom trigger element. When provided, it is rendered inside\n * `<PopoverPrimitive.Trigger asChild>` instead of the default button, so it\n * must be a single `React.ReactElement` capable of receiving props/ref (not a\n * generic `ReactNode`). Receives the resolved `urgencyLabel` and the current\n * `open` state. Provide an accessible name on the element (e.g.\n * `aria-label=\"View {urgencyLabel} urgency detail\"`).\n */\n renderTrigger?: (args: { urgencyLabel: SignalScoreUrgencyLabel; open: boolean }) => React.ReactElement\n /** Controlled open state. When defined, the popover is controlled; otherwise it falls back to internal state. */\n open?: boolean\n /** Called when the open state should change. Passed straight through to the Radix root. */\n onOpenChange?: (open: boolean) => void\n}\n\n// ---------------------------------------------------------------------------\n// Static class maps (required for Tailwind v4 source scanning)\n// ---------------------------------------------------------------------------\n\nconst URGENCY_TRIGGER_DEFAULT: Record<SignalScoreUrgencyLabel, string> = {\n Urgent: URGENCY_SCORE_CLASSES.Urgent.trigger,\n High: URGENCY_SCORE_CLASSES.High.trigger,\n Medium: URGENCY_SCORE_CLASSES.Medium.trigger,\n Low: URGENCY_SCORE_CLASSES.Low.trigger,\n}\n\nconst URGENCY_TRIGGER_HOVER: Record<SignalScoreUrgencyLabel, string> = {\n Urgent: URGENCY_SCORE_CLASSES.Urgent.hover,\n High: URGENCY_SCORE_CLASSES.High.hover,\n Medium: URGENCY_SCORE_CLASSES.Medium.hover,\n Low: URGENCY_SCORE_CLASSES.Low.hover,\n}\n\nconst URGENCY_TRIGGER_OPEN: Record<SignalScoreUrgencyLabel, string> = {\n Urgent: URGENCY_SCORE_CLASSES.Urgent.open,\n High: URGENCY_SCORE_CLASSES.High.open,\n Medium: URGENCY_SCORE_CLASSES.Medium.open,\n Low: URGENCY_SCORE_CLASSES.Low.open,\n}\n\n/** Re-use shared tone classes from score-semantics. */\nconst TONE_ICON_CLASSES: Record<PriorityFactor[\"tone\"], string> = SCORE_TONE_CLASSES as Record<PriorityFactor[\"tone\"], string>\n\nconst DIRECTION_CLASSES: Record<PriorityFactor[\"direction\"], string> = {\n raises: \"text-red-600\",\n lowers: \"text-emerald-600\",\n neutral: \"text-muted-foreground\",\n}\n\n// ---------------------------------------------------------------------------\n// Icon lookup\n// ---------------------------------------------------------------------------\n\nconst FACTOR_ICONS: Record<string, LucideIcon> = {\n radar: Radar,\n wallet: Wallet,\n \"link-2\": Link2,\n \"message-square\": MessageSquare,\n \"trending-down\": TrendingDown,\n \"arrow-up-right\": ArrowUpRight,\n clock: Clock,\n activity: Activity,\n}\n\n// ---------------------------------------------------------------------------\n// Urgency dot color (static map)\n// ---------------------------------------------------------------------------\n\nconst URGENCY_DOT_CLASSES: Record<SignalScoreUrgencyLabel, string> = {\n Urgent: URGENCY_SCORE_CLASSES.Urgent.dot,\n High: URGENCY_SCORE_CLASSES.High.dot,\n Medium: URGENCY_SCORE_CLASSES.Medium.dot,\n Low: URGENCY_SCORE_CLASSES.Low.dot,\n}\n\n// ---------------------------------------------------------------------------\n// Default feedback chips\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_PRIORITY_FEEDBACK_CHIPS: FeedbackChipTree[] = [\n { label: \"Wrong factor weighting\" },\n { label: \"Missing context\" },\n { label: \"Inaccurate data\" },\n { label: \"Stale\" },\n { label: \"Other\" },\n]\n\n// ---------------------------------------------------------------------------\n// Direction icon component\n// ---------------------------------------------------------------------------\n\nfunction DirectionIcon({ direction }: { direction: PriorityFactor[\"direction\"] }) {\n const cls = \"h-2.5 w-2.5\"\n switch (direction) {\n case \"raises\":\n return <ArrowUpRight className={cls} />\n case \"lowers\":\n return <ArrowDownRight className={cls} />\n case \"neutral\":\n return <Minus className={cls} />\n }\n}\n\n// ---------------------------------------------------------------------------\n// PriorityFactorRow\n// ---------------------------------------------------------------------------\n\ninterface PriorityFactorRowProps {\n factor: PriorityFactor\n initialFeedback?: { type: \"up\" | \"down\"; detail: string; ownershipLabel?: string }\n onFactorFeedback?: (factorKey: string, type: \"up\" | \"down\" | null, detail?: string) => void\n}\n\nfunction PriorityFactorRow({ factor, initialFeedback, onFactorFeedback }: PriorityFactorRowProps) {\n const IconComponent = FACTOR_ICONS[factor.icon] ?? Activity\n const toneClasses = TONE_ICON_CLASSES[factor.tone]\n const directionClasses = DIRECTION_CLASSES[factor.direction]\n const directionLabel = factor.directionLabel ?? (\n factor.direction === \"raises\"\n ? \"Raises\"\n : factor.direction === \"lowers\"\n ? \"Lowers\"\n : \"Neutral\"\n )\n\n return (\n <div\n className=\"grid grid-cols-[20px_1fr_auto] gap-x-3 gap-y-1 px-4 py-3\"\n data-testid={`factor-row-${factor.key}`}\n >\n {/* Icon */}\n <div\n className={cn(\n \"flex h-5 w-5 items-center justify-center rounded\",\n toneClasses,\n )}\n >\n <IconComponent className=\"h-3 w-3\" />\n </div>\n\n {/* Label + direction tag */}\n <div className=\"flex items-center gap-2\">\n <span className=\"text-[13px] font-semibold text-foreground\">\n {factor.label}\n </span>\n <span\n className={cn(\n \"inline-flex items-center gap-0.5 text-[10px] font-medium\",\n directionClasses,\n )}\n >\n <DirectionIcon direction={factor.direction} />\n {directionLabel}\n </span>\n </div>\n\n {/* Score number / display label */}\n <div className=\"flex items-center text-right\">\n {factor.displayValueLabel ? (\n <span className=\"text-xs font-semibold text-foreground\">{factor.displayValueLabel}</span>\n ) : (\n <>\n <span className=\"text-sm font-bold tabular-nums\">{factor.score}</span>\n <span className=\"text-xs font-normal text-muted-foreground\">/100</span>\n </>\n )}\n </div>\n\n {/* empty grid cell under icon column */}\n <div />\n\n {/* Rationale */}\n <p className=\"text-xs leading-relaxed text-muted-foreground\">\n {factor.rationale}\n </p>\n\n {/* empty grid cell under score column */}\n <div />\n\n {/* empty grid cell under icon column */}\n <div />\n\n {/* Score track */}\n <div className=\"mt-1 h-0.5 rounded-full bg-muted\">\n <div\n className=\"h-full rounded-full bg-foreground/20\"\n style={{ width: `${Math.min(100, Math.max(0, factor.score))}%` }}\n />\n </div>\n\n {/* empty grid cell under score column */}\n <div />\n\n {/* Factor-level feedback row (spans icon + content columns) */}\n {onFactorFeedback && (\n <>\n <div />\n <div className=\"col-span-2 mt-1\">\n <InlineFeedbackControl\n feedbackKey={factor.key}\n initialFeedback={initialFeedback}\n onFeedback={onFactorFeedback}\n testIdPrefix=\"factor\"\n />\n </div>\n </>\n )}\n </div>\n )\n}\n\n// ---------------------------------------------------------------------------\n// SignalPriorityPopover\n// ---------------------------------------------------------------------------\n\nexport function SignalPriorityPopover({\n score,\n urgencyLabel: providedLabel,\n urgencyExplanation,\n factors,\n metaText,\n feedbackChips,\n onFeedbackSubmit,\n className,\n initialFactorFeedback,\n onFactorFeedback,\n initialPriorityFeedback,\n scoreDisplay = \"number\",\n formulaLabel = \"Priority factors\",\n renderTrigger,\n open: controlledOpen,\n onOpenChange,\n}: SignalPriorityPopoverProps) {\n const urgencyLabel = providedLabel ?? getUrgencyLevel(score)\n const scoreRange = getUrgencyRange(urgencyLabel)\n\n // Controlled/uncontrolled open state: use the controlled prop when defined,\n // otherwise fall back to internal state.\n const [uncontrolledOpen, setUncontrolledOpen] = React.useState(false)\n const open = controlledOpen ?? uncontrolledOpen\n const setOpen = React.useCallback(\n (next: boolean) => {\n if (controlledOpen === undefined) setUncontrolledOpen(next)\n onOpenChange?.(next)\n },\n [controlledOpen, onOpenChange],\n )\n const [feedback, setFeedback] = React.useState<\"positive\" | \"negative\" | null>(null)\n\n const triggerDefault = URGENCY_TRIGGER_DEFAULT[urgencyLabel]\n const triggerHover = URGENCY_TRIGGER_HOVER[urgencyLabel]\n const triggerOpen = URGENCY_TRIGGER_OPEN[urgencyLabel]\n\n // Derive a stable feedbackKey for the footer from score + urgencyLabel\n const footerFeedbackKey = `priority-${score}-${urgencyLabel}`\n\n return (\n <PopoverPrimitive.Root open={open} onOpenChange={setOpen}>\n <PopoverPrimitive.Trigger asChild data-testid=\"priority-popover-trigger\">\n {renderTrigger ? (\n renderTrigger({ urgencyLabel, open })\n ) : (\n <button\n type=\"button\"\n className={cn(\n \"inline-flex items-center gap-1 rounded-md border px-2.5 py-1 text-xs font-semibold transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n triggerDefault,\n triggerHover,\n open && triggerOpen,\n open && \"outline-2 outline-foreground outline-offset-2\",\n className,\n )}\n >\n {urgencyLabel} Priority\n {open ? (\n <ChevronUp className=\"h-3 w-3\" />\n ) : (\n <ChevronDown className=\"h-3 w-3\" />\n )}\n </button>\n )}\n </PopoverPrimitive.Trigger>\n\n <PopoverPrimitive.Portal>\n <PopoverPrimitive.Content\n side=\"bottom\"\n align=\"start\"\n sideOffset={8}\n onOpenAutoFocus={(e) => e.preventDefault()}\n className=\"z-50 w-[420px] rounded-lg border border-border bg-background shadow-lg p-0\"\n data-testid=\"priority-popover-content\"\n >\n {/* Head section */}\n <div className=\"p-4 pb-3\" data-testid=\"priority-popover-header\">\n <div className=\"flex items-start justify-between gap-3\">\n <p className=\"text-sm font-semibold text-foreground\">\n Why this is {urgencyLabel.toLowerCase()} priority\n </p>\n {scoreDisplay === \"number\" && (\n <span\n className=\"text-2xl font-bold tabular-nums text-foreground\"\n data-testid=\"priority-overall-score\"\n >\n {score}\n <span className=\"text-sm font-normal text-muted-foreground\">/100</span>\n </span>\n )}\n </div>\n\n {/* Band indicator */}\n <div className=\"mt-1 flex items-center gap-1.5 text-[11px] text-muted-foreground\">\n <span\n className={cn(\n \"inline-block h-2 w-2 rounded-full\",\n URGENCY_DOT_CLASSES[urgencyLabel],\n )}\n />\n {urgencyLabel} range: {scoreRange}\n </div>\n\n {/* Synthesis sentence */}\n {urgencyExplanation && (\n <p className=\"mt-2 text-xs leading-relaxed text-muted-foreground\">\n {urgencyExplanation}\n </p>\n )}\n </div>\n\n {/* Section label */}\n {factors.length > 0 && (\n <>\n <div className=\"flex items-center justify-between border-t border-border px-4 py-2\">\n <span className=\"text-[10px] font-bold uppercase tracking-wider text-muted-foreground\">\n Contributing factors\n </span>\n <span className=\"flex items-center gap-1 text-[10px] text-muted-foreground\">\n <Info className=\"h-3 w-3\" />\n {formulaLabel}\n </span>\n </div>\n\n {/* Factor rows */}\n <div className=\"divide-y divide-border/40\">\n {factors.map((factor) => (\n <PriorityFactorRow\n key={factor.key}\n factor={factor}\n initialFeedback={initialFactorFeedback?.[factor.key]}\n onFactorFeedback={onFactorFeedback}\n />\n ))}\n </div>\n </>\n )}\n\n {/* Feedback footer */}\n {onFeedbackSubmit && (\n <div className=\"border-t border-border\">\n <FeedbackFooter\n feedback={feedback}\n onFeedbackChange={setFeedback}\n onSubmit={onFeedbackSubmit}\n metaText={metaText}\n negativeChips={feedbackChips ?? DEFAULT_PRIORITY_FEEDBACK_CHIPS}\n positivePrompt=\"Thanks. Anything to keep about this score?\"\n className=\"px-4 py-3\"\n initialFeedback={initialPriorityFeedback}\n feedbackKey={footerFeedbackKey}\n />\n </div>\n )}\n </PopoverPrimitive.Content>\n </PopoverPrimitive.Portal>\n </PopoverPrimitive.Root>\n )\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.ts
CHANGED
|
@@ -42,7 +42,7 @@ export { EmptyState, EmptyStateProps } from './components/empty-state.js';
|
|
|
42
42
|
export { ActivityItem, ConnectedApps, EntityActivityItem, EntityDetails, EntityMetadataField, EntityMetadataGrid, EntityPanel, EntityPanelBrandIcons, EntityPanelHeader, EntityPanelTabs, EntitySection, PanelMode, PotentialContacts, RecentActivity, SystemActivity, useEntityPanel } from './components/entity-panel.js';
|
|
43
43
|
export { RelatedRecordActionCard, RelatedRecordActionCardKind, RelatedRecordActionCardProps, RelatedRecordActionIcon } from './components/related-record-action-card.js';
|
|
44
44
|
export { FeedbackActions, FeedbackActionsProps, FeedbackChipGroup, FeedbackChipGroupProps, FeedbackChipTree, FeedbackFooter, FeedbackFooterProps, FeedbackInput, FeedbackInputProps, FeedbackSubmitData, InlineFeedbackControl, InlineFeedbackControlProps, PersistedFeedbackData } from './components/feedback-primitives.js';
|
|
45
|
-
export { A as AccountFilterTab, a as AccountsViewConfig, b as AdminTab, c as AdminViewConfig, B as BriefStyleVariant, E as EntityPanelConfig, d as EntityPanelSection, I as InboxDetailSections, e as InboxSortOption, f as InboxViewConfig, g as InsightsCustomTab, h as InsightsViewConfig, P as PriorityFactor, i as PrototypeBrandConfig, j as PrototypeConfig, Q as QueueItem, R as RenderTimelineHeaderControls, S as SignalPriorityPopover, k as SignalPriorityPopoverProps, l as SignalPriorityScoreDisplay, m as SignalScoreData, n as SignalScoreExplanationBucket, o as SignalScoreExplanationSignal, p as SignalScoreUrgencyLabel, T as TimelineSystemEventsConfig, W as WorkQueueViewConfig } from './signal-priority-popover-
|
|
45
|
+
export { A as AccountFilterTab, a as AccountsViewConfig, b as AdminTab, c as AdminViewConfig, B as BriefStyleVariant, E as EntityPanelConfig, d as EntityPanelSection, I as InboxDetailSections, e as InboxSortOption, f as InboxViewConfig, g as InsightsCustomTab, h as InsightsViewConfig, P as PriorityFactor, i as PrototypeBrandConfig, j as PrototypeConfig, Q as QueueItem, R as RenderTimelineHeaderControls, S as SignalPriorityPopover, k as SignalPriorityPopoverProps, l as SignalPriorityScoreDisplay, m as SignalScoreData, n as SignalScoreExplanationBucket, o as SignalScoreExplanationSignal, p as SignalScoreUrgencyLabel, T as TimelineSystemEventsConfig, W as WorkQueueViewConfig } from './signal-priority-popover-BmG4WAxT.js';
|
|
46
46
|
export { FilterChip, FilterChipProps } from './components/filter-chip.js';
|
|
47
47
|
export { InboxGroupHeader, InboxRow, InboxRowProps } from './components/inbox-row.js';
|
|
48
48
|
export { AssigneeFilter, InboxFilterCategory, InboxToolbar, InboxToolbarProps } from './components/inbox-toolbar.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { A as AccountFilterTab, a as AccountsViewConfig, b as AdminTab, c as AdminViewConfig, B as BriefStyleVariant, E as EntityPanelConfig, d as EntityPanelSection, I as InboxDetailSections, e as InboxSortOption, f as InboxViewConfig, g as InsightsCustomTab, h as InsightsViewConfig, i as PrototypeBrandConfig, j as PrototypeConfig, Q as QueueItem, R as RenderTimelineHeaderControls, m as SignalScoreData, n as SignalScoreExplanationBucket, o as SignalScoreExplanationSignal, p as SignalScoreUrgencyLabel, T as TimelineSystemEventsConfig, W as WorkQueueViewConfig } from '../signal-priority-popover-
|
|
1
|
+
export { A as AccountFilterTab, a as AccountsViewConfig, b as AdminTab, c as AdminViewConfig, B as BriefStyleVariant, E as EntityPanelConfig, d as EntityPanelSection, I as InboxDetailSections, e as InboxSortOption, f as InboxViewConfig, g as InsightsCustomTab, h as InsightsViewConfig, i as PrototypeBrandConfig, j as PrototypeConfig, Q as QueueItem, R as RenderTimelineHeaderControls, m as SignalScoreData, n as SignalScoreExplanationBucket, o as SignalScoreExplanationSignal, p as SignalScoreUrgencyLabel, T as TimelineSystemEventsConfig, W as WorkQueueViewConfig } from '../signal-priority-popover-BmG4WAxT.js';
|
|
2
2
|
export { PrototypeShell, PrototypeShellProps } from './prototype-shell.js';
|
|
3
3
|
export { DetailView, DetailViewProps, PrototypeInboxView, PrototypeInboxViewProps } from './prototype-inbox-view.js';
|
|
4
4
|
export { PrototypeInsightsView, PrototypeInsightsViewProps } from './prototype-insights-view.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { a as AccountsViewConfig } from '../signal-priority-popover-
|
|
2
|
+
import { a as AccountsViewConfig } from '../signal-priority-popover-BmG4WAxT.js';
|
|
3
3
|
import { DataRow } from '../components/data-table.js';
|
|
4
4
|
import '../components/feedback-primitives.js';
|
|
5
5
|
import '../components/quick-action-sidebar-nav.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { c as AdminViewConfig } from '../signal-priority-popover-
|
|
2
|
+
import { c as AdminViewConfig } from '../signal-priority-popover-BmG4WAxT.js';
|
|
3
3
|
import '../components/feedback-primitives.js';
|
|
4
4
|
import '../components/quick-action-sidebar-nav.js';
|
|
5
5
|
import '../components/quick-action-modal.js';
|
|
@@ -11,7 +11,7 @@ import '../charts/pipeline-overview.js';
|
|
|
11
11
|
import '../components/timeline-activity.js';
|
|
12
12
|
import '../components/signal-feedback-inline.js';
|
|
13
13
|
import 'lucide-react';
|
|
14
|
-
export { A as AccountFilterTab, a as AccountsViewConfig, b as AdminTab, c as AdminViewConfig, B as BriefStyleVariant, E as EntityPanelConfig, d as EntityPanelSection, I as InboxDetailSections, e as InboxSortOption, f as InboxViewConfig, g as InsightsCustomTab, h as InsightsViewConfig, i as PrototypeBrandConfig, j as PrototypeConfig, Q as QueueItem, R as RenderTimelineHeaderControls, m as SignalScoreData, n as SignalScoreExplanationBucket, o as SignalScoreExplanationSignal, p as SignalScoreUrgencyLabel, T as TimelineSystemEventsConfig, W as WorkQueueViewConfig } from '../signal-priority-popover-
|
|
14
|
+
export { A as AccountFilterTab, a as AccountsViewConfig, b as AdminTab, c as AdminViewConfig, B as BriefStyleVariant, E as EntityPanelConfig, d as EntityPanelSection, I as InboxDetailSections, e as InboxSortOption, f as InboxViewConfig, g as InsightsCustomTab, h as InsightsViewConfig, i as PrototypeBrandConfig, j as PrototypeConfig, Q as QueueItem, R as RenderTimelineHeaderControls, m as SignalScoreData, n as SignalScoreExplanationBucket, o as SignalScoreExplanationSignal, p as SignalScoreUrgencyLabel, T as TimelineSystemEventsConfig, W as WorkQueueViewConfig } from '../signal-priority-popover-BmG4WAxT.js';
|
|
15
15
|
import '../components/feedback-primitives.js';
|
|
16
16
|
import '../components/quick-action-modal.js';
|
|
17
17
|
import '../components/score-semantics.js';
|
|
@@ -3,7 +3,7 @@ import { OpportunityDraft, ApprovalState, OpportunityPreview } from '../componen
|
|
|
3
3
|
import { SourceDef } from '../components/detail-view.js';
|
|
4
4
|
import { SuggestedAction, SuggestedContact } from '../components/suggested-actions.js';
|
|
5
5
|
import { TimelineEvent } from '../components/timeline-activity.js';
|
|
6
|
-
import { Q as QueueItem, I as InboxDetailSections, m as SignalScoreData, f as InboxViewConfig, B as BriefStyleVariant, T as TimelineSystemEventsConfig, R as RenderTimelineHeaderControls } from '../signal-priority-popover-
|
|
6
|
+
import { Q as QueueItem, I as InboxDetailSections, m as SignalScoreData, f as InboxViewConfig, B as BriefStyleVariant, T as TimelineSystemEventsConfig, R as RenderTimelineHeaderControls } from '../signal-priority-popover-BmG4WAxT.js';
|
|
7
7
|
import '../components/feedback-primitives.js';
|
|
8
8
|
import '../components/quick-action-sidebar-nav.js';
|
|
9
9
|
import '../components/quick-action-modal.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { h as InsightsViewConfig } from '../signal-priority-popover-
|
|
2
|
+
import { h as InsightsViewConfig } from '../signal-priority-popover-BmG4WAxT.js';
|
|
3
3
|
import '../components/feedback-primitives.js';
|
|
4
4
|
import '../components/quick-action-sidebar-nav.js';
|
|
5
5
|
import '../components/quick-action-modal.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { j as PrototypeConfig } from '../signal-priority-popover-
|
|
2
|
+
import { j as PrototypeConfig } from '../signal-priority-popover-BmG4WAxT.js';
|
|
3
3
|
import '../components/feedback-primitives.js';
|
|
4
4
|
import '../components/quick-action-sidebar-nav.js';
|
|
5
5
|
import '../components/quick-action-modal.js';
|
package/dist/{signal-priority-popover-DIUVhipw.d.ts → signal-priority-popover-BmG4WAxT.d.ts}
RENAMED
|
@@ -470,7 +470,23 @@ interface SignalPriorityPopoverProps {
|
|
|
470
470
|
onFactorFeedback?: (factorKey: string, type: "up" | "down" | null, detail?: string) => void;
|
|
471
471
|
/** Persisted priority-level feedback for the footer. */
|
|
472
472
|
initialPriorityFeedback?: PersistedFeedbackData | null;
|
|
473
|
+
/**
|
|
474
|
+
* Optional custom trigger element. When provided, it is rendered inside
|
|
475
|
+
* `<PopoverPrimitive.Trigger asChild>` instead of the default button, so it
|
|
476
|
+
* must be a single `React.ReactElement` capable of receiving props/ref (not a
|
|
477
|
+
* generic `ReactNode`). Receives the resolved `urgencyLabel` and the current
|
|
478
|
+
* `open` state. Provide an accessible name on the element (e.g.
|
|
479
|
+
* `aria-label="View {urgencyLabel} urgency detail"`).
|
|
480
|
+
*/
|
|
481
|
+
renderTrigger?: (args: {
|
|
482
|
+
urgencyLabel: SignalScoreUrgencyLabel;
|
|
483
|
+
open: boolean;
|
|
484
|
+
}) => React.ReactElement;
|
|
485
|
+
/** Controlled open state. When defined, the popover is controlled; otherwise it falls back to internal state. */
|
|
486
|
+
open?: boolean;
|
|
487
|
+
/** Called when the open state should change. Passed straight through to the Radix root. */
|
|
488
|
+
onOpenChange?: (open: boolean) => void;
|
|
473
489
|
}
|
|
474
|
-
declare function SignalPriorityPopover({ score, urgencyLabel: providedLabel, urgencyExplanation, factors, metaText, feedbackChips, onFeedbackSubmit, className, initialFactorFeedback, onFactorFeedback, initialPriorityFeedback, scoreDisplay, formulaLabel, }: SignalPriorityPopoverProps): React.JSX.Element;
|
|
490
|
+
declare function SignalPriorityPopover({ score, urgencyLabel: providedLabel, urgencyExplanation, factors, metaText, feedbackChips, onFeedbackSubmit, className, initialFactorFeedback, onFactorFeedback, initialPriorityFeedback, scoreDisplay, formulaLabel, renderTrigger, open: controlledOpen, onOpenChange, }: SignalPriorityPopoverProps): React.JSX.Element;
|
|
475
491
|
|
|
476
492
|
export { type AccountFilterTab as A, type BriefStyleVariant as B, type EntityPanelConfig as E, type InboxDetailSections as I, type PriorityFactor as P, type QueueItem as Q, type RenderTimelineHeaderControls as R, SignalPriorityPopover as S, type TimelineSystemEventsConfig as T, type WorkQueueViewConfig as W, type AccountsViewConfig as a, type AdminTab as b, type AdminViewConfig as c, type EntityPanelSection as d, type InboxSortOption as e, type InboxViewConfig as f, type InsightsCustomTab as g, type InsightsViewConfig as h, type PrototypeBrandConfig as i, type PrototypeConfig as j, type SignalPriorityPopoverProps as k, type SignalPriorityScoreDisplay as l, type SignalScoreData as m, type SignalScoreExplanationBucket as n, type SignalScoreExplanationSignal as o, type SignalScoreUrgencyLabel as p };
|
package/package.json
CHANGED
|
@@ -109,6 +109,16 @@ describe("SignalOwnerChip", () => {
|
|
|
109
109
|
expect(cls).not.toContain("text-[13px]");
|
|
110
110
|
});
|
|
111
111
|
|
|
112
|
+
it("renders the owner avatar with a 1px ring (no thick ring overflow)", () => {
|
|
113
|
+
const { container } = render(<SignalOwnerChip owner={dana} />);
|
|
114
|
+
const avatar = container.querySelector('[data-slot="avatar"]')!;
|
|
115
|
+
const cls = avatar.className;
|
|
116
|
+
expect(cls).toContain("ring-background");
|
|
117
|
+
expect(cls).toContain("ring-1");
|
|
118
|
+
// The thick ring used to bleed past the chip border; it must be gone.
|
|
119
|
+
expect(cls).not.toContain("ring-2");
|
|
120
|
+
});
|
|
121
|
+
|
|
112
122
|
it("renders a static span (no button) when read-only / no handlers", () => {
|
|
113
123
|
const { container } = render(<SignalOwnerChip owner={dana} />);
|
|
114
124
|
const el = container.querySelector('[data-slot="signal-owner-chip"]');
|
|
@@ -378,4 +378,76 @@ describe("SignalPriorityPopover", () => {
|
|
|
378
378
|
expect(trigger.className).toContain("text-amber-800")
|
|
379
379
|
expect(trigger.textContent).toContain("Medium Priority")
|
|
380
380
|
})
|
|
381
|
+
|
|
382
|
+
// ─── Custom trigger (Task 0) ───────────────────────────────────────────────
|
|
383
|
+
|
|
384
|
+
it("renders the default button trigger unchanged when no custom trigger is passed", () => {
|
|
385
|
+
render(<SignalPriorityPopover {...defaultProps} />)
|
|
386
|
+
const trigger = screen.getByTestId("priority-popover-trigger")
|
|
387
|
+
// Default trigger is a <button> reading "{urgencyLabel} Priority"
|
|
388
|
+
expect(trigger.tagName).toBe("BUTTON")
|
|
389
|
+
expect(trigger.textContent).toContain("High Priority")
|
|
390
|
+
})
|
|
391
|
+
|
|
392
|
+
it("renders a custom trigger inside the popover trigger, merging the data-testid, and opens the panel", () => {
|
|
393
|
+
render(
|
|
394
|
+
<SignalPriorityPopover
|
|
395
|
+
{...defaultProps}
|
|
396
|
+
renderTrigger={({ urgencyLabel, open }) => (
|
|
397
|
+
<button
|
|
398
|
+
type="button"
|
|
399
|
+
aria-label={`View ${urgencyLabel} urgency detail`}
|
|
400
|
+
data-open={open}
|
|
401
|
+
>
|
|
402
|
+
bar-glyph
|
|
403
|
+
</button>
|
|
404
|
+
)}
|
|
405
|
+
/>,
|
|
406
|
+
)
|
|
407
|
+
|
|
408
|
+
// data-testid is merged onto the custom child element
|
|
409
|
+
const trigger = screen.getByTestId("priority-popover-trigger")
|
|
410
|
+
expect(trigger.tagName).toBe("BUTTON")
|
|
411
|
+
expect(trigger.textContent).toBe("bar-glyph")
|
|
412
|
+
expect(trigger.getAttribute("aria-label")).toBe("View High urgency detail")
|
|
413
|
+
// No default "High Priority" label rendered
|
|
414
|
+
expect(trigger.textContent).not.toContain("High Priority")
|
|
415
|
+
|
|
416
|
+
// Opens the panel
|
|
417
|
+
expect(screen.queryByTestId("priority-popover-content")).toBeNull()
|
|
418
|
+
fireEvent.click(trigger)
|
|
419
|
+
expect(screen.getByTestId("priority-popover-content")).toBeTruthy()
|
|
420
|
+
// open state is forwarded to the render function
|
|
421
|
+
expect(trigger.getAttribute("data-open")).toBe("true")
|
|
422
|
+
})
|
|
423
|
+
|
|
424
|
+
// ─── Controlled open state (Task 0) ─────────────────────────────────────────
|
|
425
|
+
|
|
426
|
+
it("respects the controlled open prop for visibility", () => {
|
|
427
|
+
const { rerender } = render(
|
|
428
|
+
<SignalPriorityPopover {...defaultProps} open={false} onOpenChange={vi.fn()} />,
|
|
429
|
+
)
|
|
430
|
+
expect(screen.queryByTestId("priority-popover-content")).toBeNull()
|
|
431
|
+
|
|
432
|
+
rerender(<SignalPriorityPopover {...defaultProps} open={true} onOpenChange={vi.fn()} />)
|
|
433
|
+
expect(screen.getByTestId("priority-popover-content")).toBeTruthy()
|
|
434
|
+
})
|
|
435
|
+
|
|
436
|
+
it("calls onOpenChange and does not self-open when controlled", () => {
|
|
437
|
+
const onOpenChange = vi.fn()
|
|
438
|
+
render(<SignalPriorityPopover {...defaultProps} open={false} onOpenChange={onOpenChange} />)
|
|
439
|
+
|
|
440
|
+
fireEvent.click(screen.getByTestId("priority-popover-trigger"))
|
|
441
|
+
|
|
442
|
+
// Controlled: open stays driven by the prop (still false), but handler fires
|
|
443
|
+
expect(onOpenChange).toHaveBeenCalledWith(true)
|
|
444
|
+
expect(screen.queryByTestId("priority-popover-content")).toBeNull()
|
|
445
|
+
})
|
|
446
|
+
|
|
447
|
+
it("still works uncontrolled when open/onOpenChange are omitted", () => {
|
|
448
|
+
render(<SignalPriorityPopover {...defaultProps} />)
|
|
449
|
+
expect(screen.queryByTestId("priority-popover-content")).toBeNull()
|
|
450
|
+
fireEvent.click(screen.getByTestId("priority-popover-trigger"))
|
|
451
|
+
expect(screen.getByTestId("priority-popover-content")).toBeTruthy()
|
|
452
|
+
})
|
|
381
453
|
})
|
|
@@ -73,7 +73,7 @@ function SalesforceMark({ size = 13 }: { size?: number }) {
|
|
|
73
73
|
|
|
74
74
|
function OwnerAvatar({ person, size = "sm" }: { person: OwnerPerson; size?: "sm" | "default" }) {
|
|
75
75
|
return (
|
|
76
|
-
<Avatar size={size} className="ring-background ring-
|
|
76
|
+
<Avatar size={size} className="ring-background ring-1">
|
|
77
77
|
{person.avatarUrl ? <AvatarImage src={person.avatarUrl} alt={person.name} /> : null}
|
|
78
78
|
<AvatarFallback className="bg-muted text-muted-foreground text-[10px] font-medium uppercase">
|
|
79
79
|
{getInitials({ name: person.name, email: person.email })}
|
|
@@ -74,6 +74,19 @@ export interface SignalPriorityPopoverProps {
|
|
|
74
74
|
onFactorFeedback?: (factorKey: string, type: "up" | "down" | null, detail?: string) => void
|
|
75
75
|
/** Persisted priority-level feedback for the footer. */
|
|
76
76
|
initialPriorityFeedback?: PersistedFeedbackData | null
|
|
77
|
+
/**
|
|
78
|
+
* Optional custom trigger element. When provided, it is rendered inside
|
|
79
|
+
* `<PopoverPrimitive.Trigger asChild>` instead of the default button, so it
|
|
80
|
+
* must be a single `React.ReactElement` capable of receiving props/ref (not a
|
|
81
|
+
* generic `ReactNode`). Receives the resolved `urgencyLabel` and the current
|
|
82
|
+
* `open` state. Provide an accessible name on the element (e.g.
|
|
83
|
+
* `aria-label="View {urgencyLabel} urgency detail"`).
|
|
84
|
+
*/
|
|
85
|
+
renderTrigger?: (args: { urgencyLabel: SignalScoreUrgencyLabel; open: boolean }) => React.ReactElement
|
|
86
|
+
/** Controlled open state. When defined, the popover is controlled; otherwise it falls back to internal state. */
|
|
87
|
+
open?: boolean
|
|
88
|
+
/** Called when the open state should change. Passed straight through to the Radix root. */
|
|
89
|
+
onOpenChange?: (open: boolean) => void
|
|
77
90
|
}
|
|
78
91
|
|
|
79
92
|
// ---------------------------------------------------------------------------
|
|
@@ -290,11 +303,24 @@ export function SignalPriorityPopover({
|
|
|
290
303
|
initialPriorityFeedback,
|
|
291
304
|
scoreDisplay = "number",
|
|
292
305
|
formulaLabel = "Priority factors",
|
|
306
|
+
renderTrigger,
|
|
307
|
+
open: controlledOpen,
|
|
308
|
+
onOpenChange,
|
|
293
309
|
}: SignalPriorityPopoverProps) {
|
|
294
310
|
const urgencyLabel = providedLabel ?? getUrgencyLevel(score)
|
|
295
311
|
const scoreRange = getUrgencyRange(urgencyLabel)
|
|
296
312
|
|
|
297
|
-
|
|
313
|
+
// Controlled/uncontrolled open state: use the controlled prop when defined,
|
|
314
|
+
// otherwise fall back to internal state.
|
|
315
|
+
const [uncontrolledOpen, setUncontrolledOpen] = React.useState(false)
|
|
316
|
+
const open = controlledOpen ?? uncontrolledOpen
|
|
317
|
+
const setOpen = React.useCallback(
|
|
318
|
+
(next: boolean) => {
|
|
319
|
+
if (controlledOpen === undefined) setUncontrolledOpen(next)
|
|
320
|
+
onOpenChange?.(next)
|
|
321
|
+
},
|
|
322
|
+
[controlledOpen, onOpenChange],
|
|
323
|
+
)
|
|
298
324
|
const [feedback, setFeedback] = React.useState<"positive" | "negative" | null>(null)
|
|
299
325
|
|
|
300
326
|
const triggerDefault = URGENCY_TRIGGER_DEFAULT[urgencyLabel]
|
|
@@ -306,26 +332,29 @@ export function SignalPriorityPopover({
|
|
|
306
332
|
|
|
307
333
|
return (
|
|
308
334
|
<PopoverPrimitive.Root open={open} onOpenChange={setOpen}>
|
|
309
|
-
<PopoverPrimitive.Trigger asChild>
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
335
|
+
<PopoverPrimitive.Trigger asChild data-testid="priority-popover-trigger">
|
|
336
|
+
{renderTrigger ? (
|
|
337
|
+
renderTrigger({ urgencyLabel, open })
|
|
338
|
+
) : (
|
|
339
|
+
<button
|
|
340
|
+
type="button"
|
|
341
|
+
className={cn(
|
|
342
|
+
"inline-flex items-center gap-1 rounded-md border px-2.5 py-1 text-xs font-semibold transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
343
|
+
triggerDefault,
|
|
344
|
+
triggerHover,
|
|
345
|
+
open && triggerOpen,
|
|
346
|
+
open && "outline-2 outline-foreground outline-offset-2",
|
|
347
|
+
className,
|
|
348
|
+
)}
|
|
349
|
+
>
|
|
350
|
+
{urgencyLabel} Priority
|
|
351
|
+
{open ? (
|
|
352
|
+
<ChevronUp className="h-3 w-3" />
|
|
353
|
+
) : (
|
|
354
|
+
<ChevronDown className="h-3 w-3" />
|
|
355
|
+
)}
|
|
356
|
+
</button>
|
|
357
|
+
)}
|
|
329
358
|
</PopoverPrimitive.Trigger>
|
|
330
359
|
|
|
331
360
|
<PopoverPrimitive.Portal>
|