@handled-ai/design-system 0.21.0 → 0.21.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.
@@ -3,7 +3,7 @@ import * as React from 'react';
3
3
  import { VariantProps } from 'class-variance-authority';
4
4
 
5
5
  declare const badgeVariants: (props?: ({
6
- variant?: "default" | "secondary" | "destructive" | "outline" | "ghost" | "link" | null | undefined;
6
+ variant?: "link" | "default" | "secondary" | "destructive" | "outline" | "ghost" | null | undefined;
7
7
  } & class_variance_authority_types.ClassProp) | undefined) => string;
8
8
  declare function Badge({ className, variant, asChild, ...props }: React.ComponentProps<"span"> & VariantProps<typeof badgeVariants> & {
9
9
  asChild?: boolean;
@@ -3,7 +3,7 @@ import * as React from 'react';
3
3
  import { VariantProps } from 'class-variance-authority';
4
4
 
5
5
  declare const buttonVariants: (props?: ({
6
- variant?: "default" | "secondary" | "destructive" | "outline" | "ghost" | "link" | null | undefined;
6
+ variant?: "link" | "default" | "secondary" | "destructive" | "outline" | "ghost" | null | undefined;
7
7
  size?: "default" | "sm" | "lg" | "icon" | null | undefined;
8
8
  } & class_variance_authority_types.ClassProp) | undefined) => string;
9
9
  declare function Button({ className, variant, size, asChild, ...props }: React.ComponentProps<"button"> & VariantProps<typeof buttonVariants> & {
@@ -8,6 +8,12 @@ interface ContextualQuickActionItem {
8
8
  disabled?: boolean;
9
9
  disabledReason?: string;
10
10
  meta?: React.ReactNode;
11
+ /**
12
+ * Optional stable test id stamped on the rendered menu item. Lets callers
13
+ * preserve an existing testid when an action moves into the launcher (e.g.
14
+ * the inbox `case-sync-button`).
15
+ */
16
+ testId?: string;
11
17
  }
12
18
  type ContextualQuickActionLauncherVariant = "default" | "case-panel";
13
19
  interface ContextualQuickActionContextLabelProps {
@@ -160,6 +160,7 @@ function ContextualQuickActionLauncher({
160
160
  DropdownMenuItem,
161
161
  {
162
162
  disabled: item.disabled,
163
+ "data-testid": item.testId,
163
164
  onSelect: (event) => handleSelect(item, event),
164
165
  className: cn(
165
166
  isCasePanel ? "grid cursor-pointer grid-cols-[34px_minmax(0,1fr)_auto] items-center gap-3 rounded-[9px] px-2.5 py-[9px] text-left outline-none focus:bg-muted/60 data-[disabled]:cursor-not-allowed data-[disabled]:opacity-100" : "flex cursor-pointer items-center gap-2.5 rounded-md px-2 py-2 text-left outline-none focus:bg-accent data-[disabled]:cursor-not-allowed data-[disabled]:opacity-100",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/contextual-quick-action-launcher.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { ChevronDown, Zap } from \"lucide-react\"\n\nimport { cn } from \"../lib/utils\"\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from \"./dropdown-menu\"\n\nexport interface ContextualQuickActionItem {\n id: string\n label: string\n description?: string\n icon?: React.ReactNode\n disabled?: boolean\n disabledReason?: string\n meta?: React.ReactNode\n}\n\nexport type ContextualQuickActionLauncherVariant = \"default\" | \"case-panel\"\n\nexport interface ContextualQuickActionContextLabelProps {\n contextLabel: string\n contextSecondary?: string\n variant?: ContextualQuickActionLauncherVariant\n className?: string\n}\n\nexport interface ContextualQuickActionLauncherProps {\n contextLabel: string\n contextSecondary?: string\n items: ContextualQuickActionItem[]\n onSelect: (item: ContextualQuickActionItem) => void\n onBrowseAll?: () => void\n showHint?: boolean\n align?: \"start\" | \"end\"\n variant?: ContextualQuickActionLauncherVariant\n className?: string\n open?: boolean\n defaultOpen?: boolean\n onOpenChange?: (open: boolean) => void\n}\n\nfunction ContextualQuickActionContextLabel({\n contextLabel,\n contextSecondary,\n variant = \"default\",\n className,\n}: ContextualQuickActionContextLabelProps) {\n const isCasePanel = variant === \"case-panel\"\n\n return (\n <div\n data-slot=\"contextual-quick-action-context-label\"\n data-variant={variant}\n className={cn(\n isCasePanel\n ? \"-mx-2 -mt-2 mb-1 flex items-center gap-1.5 whitespace-nowrap border-b px-3 py-2.5 text-[13px] text-muted-foreground\"\n : \"-mx-1 -mt-1 mb-1 flex items-center gap-1.5 border-b px-3 py-2 text-[11px] text-muted-foreground\",\n className\n )}\n >\n <Zap\n className={cn(isCasePanel ? \"h-3.5 w-3.5 shrink-0\" : \"h-3 w-3 shrink-0\")}\n strokeWidth={2.25}\n aria-hidden=\"true\"\n />\n <span>Acting on</span>\n <strong\n className={cn(\n isCasePanel\n ? \"max-w-[260px] truncate font-semibold text-foreground\"\n : \"max-w-[180px] truncate font-semibold text-foreground\"\n )}\n >\n {contextLabel}\n </strong>\n {contextSecondary ? (\n <span className=\"min-w-0 truncate text-muted-foreground\">\n · {contextSecondary}\n </span>\n ) : null}\n </div>\n )\n}\n\nfunction DefaultActionIcon() {\n return <Zap className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n}\n\nfunction ContextualQuickActionLauncher({\n contextLabel,\n contextSecondary,\n items,\n onSelect,\n onBrowseAll,\n showHint = false,\n align = \"start\",\n variant = \"default\",\n className,\n open,\n defaultOpen,\n onOpenChange,\n}: ContextualQuickActionLauncherProps) {\n const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen ?? false)\n const isCasePanel = variant === \"case-panel\"\n const isControlled = open !== undefined\n const isOpen = isControlled ? open : uncontrolledOpen\n\n const handleOpenChange = React.useCallback(\n (nextOpen: boolean) => {\n if (!isControlled) {\n setUncontrolledOpen(nextOpen)\n }\n onOpenChange?.(nextOpen)\n },\n [isControlled, onOpenChange]\n )\n\n const closeMenu = React.useCallback(() => {\n handleOpenChange(false)\n }, [handleOpenChange])\n\n const handleSelect = React.useCallback(\n (item: ContextualQuickActionItem, event?: Event) => {\n if (item.disabled) {\n event?.preventDefault()\n return\n }\n\n onSelect(item)\n closeMenu()\n },\n [closeMenu, onSelect]\n )\n\n const handleBrowseAll = React.useCallback(() => {\n onBrowseAll?.()\n closeMenu()\n }, [closeMenu, onBrowseAll])\n\n return (\n <div\n data-slot=\"contextual-quick-action-launcher\"\n className={cn(\"inline-flex items-center gap-2\", className)}\n >\n <DropdownMenu open={isOpen} onOpenChange={handleOpenChange}>\n <DropdownMenuTrigger asChild>\n <button\n type=\"button\"\n data-slot=\"contextual-quick-action-trigger\"\n data-state={isOpen ? \"open\" : \"closed\"}\n className={cn(\n \"inline-flex h-8 items-center gap-2 rounded-lg border border-border bg-background py-1.5 pr-2.5 pl-2 text-xs font-medium text-foreground shadow-sm transition-colors hover:border-muted-foreground/40 hover:bg-muted/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n \"data-[state=open]:border-foreground data-[state=open]:bg-foreground data-[state=open]:text-background data-[state=open]:hover:bg-foreground\"\n )}\n >\n <span\n className={cn(\n \"inline-flex h-[18px] w-[18px] items-center justify-center rounded bg-foreground/[0.04]\",\n isOpen && \"bg-background/15\"\n )}\n >\n <Zap className=\"h-3 w-3\" strokeWidth={2} aria-hidden=\"true\" />\n </span>\n <span className=\"tracking-[-0.005em]\">Quick action</span>\n <ChevronDown className=\"h-3 w-3 opacity-60\" strokeWidth={2} aria-hidden=\"true\" />\n </button>\n </DropdownMenuTrigger>\n\n <DropdownMenuContent\n align={align}\n side=\"bottom\"\n sideOffset={6}\n className={cn(\n isCasePanel\n ? \"pointer-events-auto w-[432px] rounded-[13px] border border-border bg-popover p-2 text-[13px] shadow-[0_18px_44px_rgba(0,0,0,0.14),0_2px_10px_rgba(0,0,0,0.06)]\"\n : \"pointer-events-auto w-[308px] rounded-[10px] p-1.5 text-[12.5px] shadow-[0_12px_28px_rgba(0,0,0,0.12),0_2px_6px_rgba(0,0,0,0.04)]\"\n )}\n >\n <ContextualQuickActionContextLabel\n contextLabel={contextLabel}\n contextSecondary={contextSecondary}\n variant={variant}\n />\n\n {items.map((item) => (\n <DropdownMenuItem\n key={item.id}\n disabled={item.disabled}\n onSelect={(event) => handleSelect(item, event)}\n className={cn(\n isCasePanel\n ? \"grid cursor-pointer grid-cols-[34px_minmax(0,1fr)_auto] items-center gap-3 rounded-[9px] px-2.5 py-[9px] text-left outline-none focus:bg-muted/60 data-[disabled]:cursor-not-allowed data-[disabled]:opacity-100\"\n : \"flex cursor-pointer items-center gap-2.5 rounded-md px-2 py-2 text-left outline-none focus:bg-accent data-[disabled]:cursor-not-allowed data-[disabled]:opacity-100\",\n item.disabled && \"text-muted-foreground\"\n )}\n >\n <span\n data-slot=\"contextual-quick-action-item-icon\"\n className={cn(\n isCasePanel\n ? \"flex h-[34px] w-[34px] shrink-0 items-center justify-center rounded-[9px] border border-border/70 bg-muted/60 text-foreground [&_svg]:h-[18px] [&_svg]:w-[18px]\"\n : \"flex h-[26px] w-[26px] shrink-0 items-center justify-center rounded-md bg-secondary text-foreground\",\n item.disabled && \"opacity-60\"\n )}\n >\n {item.icon ?? <DefaultActionIcon />}\n </span>\n <span className=\"min-w-0 flex-1\">\n <span\n className={cn(\n isCasePanel\n ? \"block truncate text-[13.5px] font-semibold leading-tight text-current\"\n : \"block truncate text-[12.5px] font-medium leading-tight text-current\"\n )}\n >\n {item.label}\n </span>\n {item.description ? (\n <span\n className={cn(\n isCasePanel\n ? \"mt-1 block truncate text-[12px] leading-tight text-muted-foreground\"\n : \"mt-0.5 block truncate text-[11px] leading-tight text-muted-foreground\"\n )}\n >\n {item.description}\n </span>\n ) : null}\n </span>\n {item.disabled && item.disabledReason ? (\n <span\n data-slot=\"contextual-quick-action-item-disabled-meta\"\n className={cn(\n isCasePanel\n ? \"ml-auto shrink-0 whitespace-nowrap text-[12px] italic text-muted-foreground/70\"\n : \"ml-auto shrink-0 text-[10.5px] italic text-muted-foreground/80\"\n )}\n >\n {item.disabledReason}\n </span>\n ) : item.meta ? (\n <span\n data-slot=\"contextual-quick-action-item-meta\"\n className={cn(\n isCasePanel\n ? \"ml-auto shrink-0 whitespace-nowrap text-[12px] italic text-muted-foreground/70\"\n : \"ml-auto shrink-0 text-[10.5px] text-muted-foreground/80\"\n )}\n >\n {item.meta}\n </span>\n ) : null}\n </DropdownMenuItem>\n ))}\n\n <DropdownMenuSeparator className={cn(isCasePanel ? \"-mx-2 mt-1 mb-1\" : \"-mx-1.5 my-1\")} />\n <div\n className={cn(\n isCasePanel\n ? \"flex items-center justify-between whitespace-nowrap px-2.5 py-2 text-[13px] text-muted-foreground\"\n : \"flex items-center justify-between px-2 py-1.5 text-[11px] text-muted-foreground\"\n )}\n >\n <button\n type=\"button\"\n className={cn(\n isCasePanel\n ? \"inline-flex items-center gap-1 font-semibold text-foreground hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\"\n : \"inline-flex items-center gap-1 font-medium text-foreground hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\"\n )}\n onClick={handleBrowseAll}\n >\n Browse all actions\n </button>\n <span\n className={cn(\n isCasePanel\n ? \"inline-flex items-center rounded-[5px] border border-border bg-muted/50 px-1.5 py-0.5 font-mono text-[11px] font-medium leading-none text-muted-foreground\"\n : \"inline-flex items-center rounded border border-border px-1 py-0.5 text-[10px] font-medium leading-none text-muted-foreground\"\n )}\n >\n ⌘K\n </span>\n </div>\n </DropdownMenuContent>\n </DropdownMenu>\n\n {showHint ? (\n <span className=\"text-[11px] text-muted-foreground\">\n Or press{\" \"}\n <span className=\"inline-flex items-center rounded border border-border bg-muted/40 px-1 py-0.5 text-[10px] font-medium leading-none\">\n ⌘K\n </span>{\" \"}\n for all actions\n </span>\n ) : null}\n </div>\n )\n}\n\nexport { ContextualQuickActionContextLabel, ContextualQuickActionLauncher }\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/components/contextual-quick-action-launcher.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { ChevronDown, Zap } from \"lucide-react\"\n\nimport { cn } from \"../lib/utils\"\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from \"./dropdown-menu\"\n\nexport interface ContextualQuickActionItem {\n id: string\n label: string\n description?: string\n icon?: React.ReactNode\n disabled?: boolean\n disabledReason?: string\n meta?: React.ReactNode\n /**\n * Optional stable test id stamped on the rendered menu item. Lets callers\n * preserve an existing testid when an action moves into the launcher (e.g.\n * the inbox `case-sync-button`).\n */\n testId?: string\n}\n\nexport type ContextualQuickActionLauncherVariant = \"default\" | \"case-panel\"\n\nexport interface ContextualQuickActionContextLabelProps {\n contextLabel: string\n contextSecondary?: string\n variant?: ContextualQuickActionLauncherVariant\n className?: string\n}\n\nexport interface ContextualQuickActionLauncherProps {\n contextLabel: string\n contextSecondary?: string\n items: ContextualQuickActionItem[]\n onSelect: (item: ContextualQuickActionItem) => void\n onBrowseAll?: () => void\n showHint?: boolean\n align?: \"start\" | \"end\"\n variant?: ContextualQuickActionLauncherVariant\n className?: string\n open?: boolean\n defaultOpen?: boolean\n onOpenChange?: (open: boolean) => void\n}\n\nfunction ContextualQuickActionContextLabel({\n contextLabel,\n contextSecondary,\n variant = \"default\",\n className,\n}: ContextualQuickActionContextLabelProps) {\n const isCasePanel = variant === \"case-panel\"\n\n return (\n <div\n data-slot=\"contextual-quick-action-context-label\"\n data-variant={variant}\n className={cn(\n isCasePanel\n ? \"-mx-2 -mt-2 mb-1 flex items-center gap-1.5 whitespace-nowrap border-b px-3 py-2.5 text-[13px] text-muted-foreground\"\n : \"-mx-1 -mt-1 mb-1 flex items-center gap-1.5 border-b px-3 py-2 text-[11px] text-muted-foreground\",\n className\n )}\n >\n <Zap\n className={cn(isCasePanel ? \"h-3.5 w-3.5 shrink-0\" : \"h-3 w-3 shrink-0\")}\n strokeWidth={2.25}\n aria-hidden=\"true\"\n />\n <span>Acting on</span>\n <strong\n className={cn(\n isCasePanel\n ? \"max-w-[260px] truncate font-semibold text-foreground\"\n : \"max-w-[180px] truncate font-semibold text-foreground\"\n )}\n >\n {contextLabel}\n </strong>\n {contextSecondary ? (\n <span className=\"min-w-0 truncate text-muted-foreground\">\n · {contextSecondary}\n </span>\n ) : null}\n </div>\n )\n}\n\nfunction DefaultActionIcon() {\n return <Zap className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n}\n\nfunction ContextualQuickActionLauncher({\n contextLabel,\n contextSecondary,\n items,\n onSelect,\n onBrowseAll,\n showHint = false,\n align = \"start\",\n variant = \"default\",\n className,\n open,\n defaultOpen,\n onOpenChange,\n}: ContextualQuickActionLauncherProps) {\n const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen ?? false)\n const isCasePanel = variant === \"case-panel\"\n const isControlled = open !== undefined\n const isOpen = isControlled ? open : uncontrolledOpen\n\n const handleOpenChange = React.useCallback(\n (nextOpen: boolean) => {\n if (!isControlled) {\n setUncontrolledOpen(nextOpen)\n }\n onOpenChange?.(nextOpen)\n },\n [isControlled, onOpenChange]\n )\n\n const closeMenu = React.useCallback(() => {\n handleOpenChange(false)\n }, [handleOpenChange])\n\n const handleSelect = React.useCallback(\n (item: ContextualQuickActionItem, event?: Event) => {\n if (item.disabled) {\n event?.preventDefault()\n return\n }\n\n onSelect(item)\n closeMenu()\n },\n [closeMenu, onSelect]\n )\n\n const handleBrowseAll = React.useCallback(() => {\n onBrowseAll?.()\n closeMenu()\n }, [closeMenu, onBrowseAll])\n\n return (\n <div\n data-slot=\"contextual-quick-action-launcher\"\n className={cn(\"inline-flex items-center gap-2\", className)}\n >\n <DropdownMenu open={isOpen} onOpenChange={handleOpenChange}>\n <DropdownMenuTrigger asChild>\n <button\n type=\"button\"\n data-slot=\"contextual-quick-action-trigger\"\n data-state={isOpen ? \"open\" : \"closed\"}\n className={cn(\n \"inline-flex h-8 items-center gap-2 rounded-lg border border-border bg-background py-1.5 pr-2.5 pl-2 text-xs font-medium text-foreground shadow-sm transition-colors hover:border-muted-foreground/40 hover:bg-muted/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n \"data-[state=open]:border-foreground data-[state=open]:bg-foreground data-[state=open]:text-background data-[state=open]:hover:bg-foreground\"\n )}\n >\n <span\n className={cn(\n \"inline-flex h-[18px] w-[18px] items-center justify-center rounded bg-foreground/[0.04]\",\n isOpen && \"bg-background/15\"\n )}\n >\n <Zap className=\"h-3 w-3\" strokeWidth={2} aria-hidden=\"true\" />\n </span>\n <span className=\"tracking-[-0.005em]\">Quick action</span>\n <ChevronDown className=\"h-3 w-3 opacity-60\" strokeWidth={2} aria-hidden=\"true\" />\n </button>\n </DropdownMenuTrigger>\n\n <DropdownMenuContent\n align={align}\n side=\"bottom\"\n sideOffset={6}\n className={cn(\n isCasePanel\n ? \"pointer-events-auto w-[432px] rounded-[13px] border border-border bg-popover p-2 text-[13px] shadow-[0_18px_44px_rgba(0,0,0,0.14),0_2px_10px_rgba(0,0,0,0.06)]\"\n : \"pointer-events-auto w-[308px] rounded-[10px] p-1.5 text-[12.5px] shadow-[0_12px_28px_rgba(0,0,0,0.12),0_2px_6px_rgba(0,0,0,0.04)]\"\n )}\n >\n <ContextualQuickActionContextLabel\n contextLabel={contextLabel}\n contextSecondary={contextSecondary}\n variant={variant}\n />\n\n {items.map((item) => (\n <DropdownMenuItem\n key={item.id}\n disabled={item.disabled}\n data-testid={item.testId}\n onSelect={(event) => handleSelect(item, event)}\n className={cn(\n isCasePanel\n ? \"grid cursor-pointer grid-cols-[34px_minmax(0,1fr)_auto] items-center gap-3 rounded-[9px] px-2.5 py-[9px] text-left outline-none focus:bg-muted/60 data-[disabled]:cursor-not-allowed data-[disabled]:opacity-100\"\n : \"flex cursor-pointer items-center gap-2.5 rounded-md px-2 py-2 text-left outline-none focus:bg-accent data-[disabled]:cursor-not-allowed data-[disabled]:opacity-100\",\n item.disabled && \"text-muted-foreground\"\n )}\n >\n <span\n data-slot=\"contextual-quick-action-item-icon\"\n className={cn(\n isCasePanel\n ? \"flex h-[34px] w-[34px] shrink-0 items-center justify-center rounded-[9px] border border-border/70 bg-muted/60 text-foreground [&_svg]:h-[18px] [&_svg]:w-[18px]\"\n : \"flex h-[26px] w-[26px] shrink-0 items-center justify-center rounded-md bg-secondary text-foreground\",\n item.disabled && \"opacity-60\"\n )}\n >\n {item.icon ?? <DefaultActionIcon />}\n </span>\n <span className=\"min-w-0 flex-1\">\n <span\n className={cn(\n isCasePanel\n ? \"block truncate text-[13.5px] font-semibold leading-tight text-current\"\n : \"block truncate text-[12.5px] font-medium leading-tight text-current\"\n )}\n >\n {item.label}\n </span>\n {item.description ? (\n <span\n className={cn(\n isCasePanel\n ? \"mt-1 block truncate text-[12px] leading-tight text-muted-foreground\"\n : \"mt-0.5 block truncate text-[11px] leading-tight text-muted-foreground\"\n )}\n >\n {item.description}\n </span>\n ) : null}\n </span>\n {item.disabled && item.disabledReason ? (\n <span\n data-slot=\"contextual-quick-action-item-disabled-meta\"\n className={cn(\n isCasePanel\n ? \"ml-auto shrink-0 whitespace-nowrap text-[12px] italic text-muted-foreground/70\"\n : \"ml-auto shrink-0 text-[10.5px] italic text-muted-foreground/80\"\n )}\n >\n {item.disabledReason}\n </span>\n ) : item.meta ? (\n <span\n data-slot=\"contextual-quick-action-item-meta\"\n className={cn(\n isCasePanel\n ? \"ml-auto shrink-0 whitespace-nowrap text-[12px] italic text-muted-foreground/70\"\n : \"ml-auto shrink-0 text-[10.5px] text-muted-foreground/80\"\n )}\n >\n {item.meta}\n </span>\n ) : null}\n </DropdownMenuItem>\n ))}\n\n <DropdownMenuSeparator className={cn(isCasePanel ? \"-mx-2 mt-1 mb-1\" : \"-mx-1.5 my-1\")} />\n <div\n className={cn(\n isCasePanel\n ? \"flex items-center justify-between whitespace-nowrap px-2.5 py-2 text-[13px] text-muted-foreground\"\n : \"flex items-center justify-between px-2 py-1.5 text-[11px] text-muted-foreground\"\n )}\n >\n <button\n type=\"button\"\n className={cn(\n isCasePanel\n ? \"inline-flex items-center gap-1 font-semibold text-foreground hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\"\n : \"inline-flex items-center gap-1 font-medium text-foreground hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\"\n )}\n onClick={handleBrowseAll}\n >\n Browse all actions\n </button>\n <span\n className={cn(\n isCasePanel\n ? \"inline-flex items-center rounded-[5px] border border-border bg-muted/50 px-1.5 py-0.5 font-mono text-[11px] font-medium leading-none text-muted-foreground\"\n : \"inline-flex items-center rounded border border-border px-1 py-0.5 text-[10px] font-medium leading-none text-muted-foreground\"\n )}\n >\n ⌘K\n </span>\n </div>\n </DropdownMenuContent>\n </DropdownMenu>\n\n {showHint ? (\n <span className=\"text-[11px] text-muted-foreground\">\n Or press{\" \"}\n <span className=\"inline-flex items-center rounded border border-border bg-muted/40 px-1 py-0.5 text-[10px] font-medium leading-none\">\n ⌘K\n </span>{\" \"}\n for all actions\n </span>\n ) : null}\n </div>\n )\n}\n\nexport { ContextualQuickActionContextLabel, ContextualQuickActionLauncher }\n"],"mappings":"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","names":[]}
@@ -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-2", children: [
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":[]}
@@ -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?: "default" | "secondary" | "destructive" | "outline" | "ghost" | "error" | "neutral" | "info" | "success" | "warning" | null | undefined;
15
+ variant?: "error" | "default" | "secondary" | "destructive" | "outline" | "ghost" | "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
  }
@@ -5,7 +5,7 @@ import { Tabs as Tabs$1 } from 'radix-ui';
5
5
 
6
6
  declare function Tabs({ className, orientation, ...props }: React.ComponentProps<typeof Tabs$1.Root>): React.JSX.Element;
7
7
  declare const tabsListVariants: (props?: ({
8
- variant?: "default" | "line" | null | undefined;
8
+ variant?: "line" | "default" | null | undefined;
9
9
  } & class_variance_authority_types.ClassProp) | undefined) => string;
10
10
  declare function TabsList({ className, variant, ...props }: React.ComponentProps<typeof Tabs$1.List> & VariantProps<typeof tabsListVariants>): React.JSX.Element;
11
11
  declare function TabsTrigger({ className, ...props }: React.ComponentProps<typeof Tabs$1.Trigger>): React.JSX.Element;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@handled-ai/design-system",
3
- "version": "0.21.0",
3
+ "version": "0.21.1",
4
4
  "description": "Handled UI component library (shadcn-style, New York)",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@9.12.0",
@@ -179,6 +179,20 @@ describe("ContextualQuickActionLauncher", () => {
179
179
  expect(onOpenChange).toHaveBeenCalledWith(false)
180
180
  })
181
181
 
182
+ it("stamps an optional per-item testId on the rendered menu item", () => {
183
+ render(
184
+ <ContextualQuickActionLauncher
185
+ contextLabel="Acme Corp"
186
+ items={[{ id: "sync-work-item", label: "Sync", testId: "case-sync-button" }]}
187
+ onSelect={() => {}}
188
+ open
189
+ />,
190
+ )
191
+
192
+ expect(screen.getByTestId("case-sync-button")).toBeTruthy()
193
+ expect(screen.getByTestId("case-sync-button").textContent).toContain("Sync")
194
+ })
195
+
182
196
  it("renders optional hint outside the menu", () => {
183
197
  render(
184
198
  <ContextualQuickActionLauncher
@@ -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"]');
@@ -20,6 +20,12 @@ export interface ContextualQuickActionItem {
20
20
  disabled?: boolean
21
21
  disabledReason?: string
22
22
  meta?: React.ReactNode
23
+ /**
24
+ * Optional stable test id stamped on the rendered menu item. Lets callers
25
+ * preserve an existing testid when an action moves into the launcher (e.g.
26
+ * the inbox `case-sync-button`).
27
+ */
28
+ testId?: string
23
29
  }
24
30
 
25
31
  export type ContextualQuickActionLauncherVariant = "default" | "case-panel"
@@ -193,6 +199,7 @@ function ContextualQuickActionLauncher({
193
199
  <DropdownMenuItem
194
200
  key={item.id}
195
201
  disabled={item.disabled}
202
+ data-testid={item.testId}
196
203
  onSelect={(event) => handleSelect(item, event)}
197
204
  className={cn(
198
205
  isCasePanel
@@ -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-2">
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 })}