@handled-ai/design-system 0.17.2 → 0.18.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/dist/charts/empty-chart-state.d.ts +11 -0
  2. package/dist/charts/empty-chart-state.js +70 -0
  3. package/dist/charts/empty-chart-state.js.map +1 -0
  4. package/dist/charts/index.d.ts +1 -0
  5. package/dist/charts/index.js +1 -0
  6. package/dist/charts/index.js.map +1 -1
  7. package/dist/charts/pipeline-overview.d.ts +2 -1
  8. package/dist/charts/pipeline-overview.js +29 -1
  9. package/dist/charts/pipeline-overview.js.map +1 -1
  10. package/dist/components/actor-byline.d.ts +3 -0
  11. package/dist/components/actor-byline.js +5 -0
  12. package/dist/components/actor-byline.js.map +1 -0
  13. package/dist/components/days-open-cell.d.ts +16 -0
  14. package/dist/components/days-open-cell.js +73 -0
  15. package/dist/components/days-open-cell.js.map +1 -0
  16. package/dist/components/detail-drawer.d.ts +16 -0
  17. package/dist/components/detail-drawer.js +45 -0
  18. package/dist/components/detail-drawer.js.map +1 -0
  19. package/dist/components/insights-filter-bar.d.ts +2 -1
  20. package/dist/components/insights-filter-bar.js +13 -5
  21. package/dist/components/insights-filter-bar.js.map +1 -1
  22. package/dist/components/linked-entity-cell.d.ts +14 -0
  23. package/dist/components/linked-entity-cell.js +96 -0
  24. package/dist/components/linked-entity-cell.js.map +1 -0
  25. package/dist/components/metric-card.d.ts +14 -1
  26. package/dist/components/metric-card.js +86 -0
  27. package/dist/components/metric-card.js.map +1 -1
  28. package/dist/components/performance-metrics-table.d.ts +2 -1
  29. package/dist/components/performance-metrics-table.js +78 -46
  30. package/dist/components/performance-metrics-table.js.map +1 -1
  31. package/dist/components/pill.d.ts +26 -0
  32. package/dist/components/pill.js +77 -0
  33. package/dist/components/pill.js.map +1 -0
  34. package/dist/components/quick-segment.d.ts +13 -0
  35. package/dist/components/quick-segment.js +96 -0
  36. package/dist/components/quick-segment.js.map +1 -0
  37. package/dist/index.d.ts +7 -1
  38. package/dist/index.js +5 -0
  39. package/dist/index.js.map +1 -1
  40. package/package.json +1 -1
  41. package/src/charts/__tests__/insights-charts.test.tsx +62 -0
  42. package/src/charts/empty-chart-state.tsx +44 -0
  43. package/src/charts/index.ts +1 -0
  44. package/src/charts/pipeline-overview.tsx +38 -1
  45. package/src/components/__tests__/insights-primitives.test.tsx +117 -0
  46. package/src/components/__tests__/performance-metrics-table.test.tsx +54 -0
  47. package/src/components/__tests__/user-display.test.tsx +75 -0
  48. package/src/components/actor-byline.tsx +1 -0
  49. package/src/components/days-open-cell.tsx +50 -0
  50. package/src/components/detail-drawer.tsx +60 -0
  51. package/src/components/insights-filter-bar.tsx +13 -4
  52. package/src/components/linked-entity-cell.tsx +74 -0
  53. package/src/components/metric-card.tsx +82 -0
  54. package/src/components/performance-metrics-table.tsx +99 -63
  55. package/src/components/pill.tsx +67 -0
  56. package/src/components/quick-segment.tsx +68 -0
  57. package/src/index.ts +5 -0
  58. package/src/lib/__tests__/user-display.test.ts +53 -11
@@ -0,0 +1,26 @@
1
+ import * as class_variance_authority_types from 'class-variance-authority/types';
2
+ import * as React from 'react';
3
+ import { VariantProps } from 'class-variance-authority';
4
+
5
+ /**
6
+ * Insights-friendly pill convenience wrappers.
7
+ *
8
+ * Pill and StatusPill are small, rounded wrappers for dense Insights surfaces
9
+ * such as tables, KPI strips, and filter summaries. They intentionally wrap the
10
+ * existing Badge/StatusBadge visual language and are not a replacement for all
11
+ * Badge usage across the design system.
12
+ */
13
+ type PillStatus = "success" | "warning" | "error" | "neutral" | "info";
14
+ declare const pillVariants: (props?: ({
15
+ variant?: "default" | "secondary" | "destructive" | "outline" | "ghost" | "error" | "neutral" | "info" | "warning" | "success" | null | undefined;
16
+ } & class_variance_authority_types.ClassProp) | undefined) => string;
17
+ interface PillProps extends React.ComponentProps<"span">, VariantProps<typeof pillVariants> {
18
+ }
19
+ declare function Pill({ className, variant, ...props }: PillProps): React.JSX.Element;
20
+ interface StatusPillProps extends Omit<PillProps, "variant"> {
21
+ status: React.ReactNode;
22
+ intent?: PillStatus;
23
+ }
24
+ declare function StatusPill({ status, intent, children, ...props }: StatusPillProps): React.JSX.Element;
25
+
26
+ export { Pill, type PillProps, type PillStatus, StatusPill, type StatusPillProps, pillVariants };
@@ -0,0 +1,77 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defProps = Object.defineProperties;
3
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
7
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
+ var __spreadValues = (a, b) => {
9
+ for (var prop in b || (b = {}))
10
+ if (__hasOwnProp.call(b, prop))
11
+ __defNormalProp(a, prop, b[prop]);
12
+ if (__getOwnPropSymbols)
13
+ for (var prop of __getOwnPropSymbols(b)) {
14
+ if (__propIsEnum.call(b, prop))
15
+ __defNormalProp(a, prop, b[prop]);
16
+ }
17
+ return a;
18
+ };
19
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
+ var __objRest = (source, exclude) => {
21
+ var target = {};
22
+ for (var prop in source)
23
+ if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
24
+ target[prop] = source[prop];
25
+ if (source != null && __getOwnPropSymbols)
26
+ for (var prop of __getOwnPropSymbols(source)) {
27
+ if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
28
+ target[prop] = source[prop];
29
+ }
30
+ return target;
31
+ };
32
+ import { jsx } from "react/jsx-runtime";
33
+ import { cva } from "class-variance-authority";
34
+ import { cn } from "../lib/utils.js";
35
+ const pillVariants = cva(
36
+ "inline-flex w-fit shrink-0 items-center justify-center gap-1 whitespace-nowrap rounded-full border px-2.5 py-0.5 text-xs font-medium transition-colors [&>svg]:size-3",
37
+ {
38
+ variants: {
39
+ variant: {
40
+ default: "border-transparent bg-primary text-primary-foreground",
41
+ secondary: "border-transparent bg-secondary text-secondary-foreground",
42
+ destructive: "border-transparent bg-destructive text-white dark:bg-destructive/60",
43
+ outline: "border-border bg-background text-foreground",
44
+ ghost: "border-transparent bg-transparent text-muted-foreground",
45
+ success: "border-transparent bg-green-100 text-green-950 dark:bg-green-950 dark:text-green-100",
46
+ warning: "border-transparent bg-yellow-100 text-yellow-950 dark:bg-yellow-950 dark:text-yellow-100",
47
+ error: "border-transparent bg-red-100 text-red-950 dark:bg-red-950 dark:text-red-100",
48
+ neutral: "border-transparent bg-muted text-foreground",
49
+ info: "border-transparent bg-blue-100 text-blue-950 dark:bg-blue-950 dark:text-blue-100"
50
+ }
51
+ },
52
+ defaultVariants: {
53
+ variant: "neutral"
54
+ }
55
+ }
56
+ );
57
+ function Pill(_a) {
58
+ var _b = _a, { className, variant = "neutral" } = _b, props = __objRest(_b, ["className", "variant"]);
59
+ return /* @__PURE__ */ jsx(
60
+ "span",
61
+ __spreadValues({
62
+ "data-slot": "pill",
63
+ "data-variant": variant,
64
+ className: cn(pillVariants({ variant }), className)
65
+ }, props)
66
+ );
67
+ }
68
+ function StatusPill(_c) {
69
+ var _d = _c, { status, intent = "neutral", children } = _d, props = __objRest(_d, ["status", "intent", "children"]);
70
+ return /* @__PURE__ */ jsx(Pill, __spreadProps(__spreadValues({ "data-slot": "status-pill", variant: intent }, props), { children: children != null ? children : status }));
71
+ }
72
+ export {
73
+ Pill,
74
+ StatusPill,
75
+ pillVariants
76
+ };
77
+ //# sourceMappingURL=pill.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/pill.tsx"],"sourcesContent":["import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"../lib/utils\"\n\n/**\n * Insights-friendly pill convenience wrappers.\n *\n * Pill and StatusPill are small, rounded wrappers for dense Insights surfaces\n * such as tables, KPI strips, and filter summaries. They intentionally wrap the\n * existing Badge/StatusBadge visual language and are not a replacement for all\n * Badge usage across the design system.\n */\nexport type PillStatus = \"success\" | \"warning\" | \"error\" | \"neutral\" | \"info\"\n\nconst pillVariants = cva(\n \"inline-flex w-fit shrink-0 items-center justify-center gap-1 whitespace-nowrap rounded-full border px-2.5 py-0.5 text-xs font-medium transition-colors [&>svg]:size-3\",\n {\n variants: {\n variant: {\n default: \"border-transparent bg-primary text-primary-foreground\",\n secondary: \"border-transparent bg-secondary text-secondary-foreground\",\n destructive: \"border-transparent bg-destructive text-white dark:bg-destructive/60\",\n outline: \"border-border bg-background text-foreground\",\n ghost: \"border-transparent bg-transparent text-muted-foreground\",\n success: \"border-transparent bg-green-100 text-green-950 dark:bg-green-950 dark:text-green-100\",\n warning: \"border-transparent bg-yellow-100 text-yellow-950 dark:bg-yellow-950 dark:text-yellow-100\",\n error: \"border-transparent bg-red-100 text-red-950 dark:bg-red-950 dark:text-red-100\",\n neutral: \"border-transparent bg-muted text-foreground\",\n info: \"border-transparent bg-blue-100 text-blue-950 dark:bg-blue-950 dark:text-blue-100\",\n },\n },\n defaultVariants: {\n variant: \"neutral\",\n },\n }\n)\n\nexport interface PillProps\n extends React.ComponentProps<\"span\">,\n VariantProps<typeof pillVariants> {}\n\nexport function Pill({ className, variant = \"neutral\", ...props }: PillProps) {\n return (\n <span\n data-slot=\"pill\"\n data-variant={variant}\n className={cn(pillVariants({ variant }), className)}\n {...props}\n />\n )\n}\n\nexport interface StatusPillProps extends Omit<PillProps, \"variant\"> {\n status: React.ReactNode\n intent?: PillStatus\n}\n\nexport function StatusPill({ status, intent = \"neutral\", children, ...props }: StatusPillProps) {\n return (\n <Pill data-slot=\"status-pill\" variant={intent} {...props}>\n {children ?? status}\n </Pill>\n )\n}\n\nexport { pillVariants }\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4CI;AA3CJ,SAAS,WAA8B;AAEvC,SAAS,UAAU;AAYnB,MAAM,eAAe;AAAA,EACnB;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,SAAS;AAAA,QACP,SAAS;AAAA,QACT,WAAW;AAAA,QACX,aAAa;AAAA,QACb,SAAS;AAAA,QACT,OAAO;AAAA,QACP,SAAS;AAAA,QACT,SAAS;AAAA,QACT,OAAO;AAAA,QACP,SAAS;AAAA,QACT,MAAM;AAAA,MACR;AAAA,IACF;AAAA,IACA,iBAAiB;AAAA,MACf,SAAS;AAAA,IACX;AAAA,EACF;AACF;AAMO,SAAS,KAAK,IAAyD;AAAzD,eAAE,aAAW,UAAU,UA1C5C,IA0CqB,IAAqC,kBAArC,IAAqC,CAAnC,aAAW;AAChC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,gBAAc;AAAA,MACd,WAAW,GAAG,aAAa,EAAE,QAAQ,CAAC,GAAG,SAAS;AAAA,OAC9C;AAAA,EACN;AAEJ;AAOO,SAAS,WAAW,IAAqE;AAArE,eAAE,UAAQ,SAAS,WAAW,SA1DzD,IA0D2B,IAA2C,kBAA3C,IAA2C,CAAzC,UAAQ,UAAoB;AACvD,SACE,oBAAC,qCAAK,aAAU,eAAc,SAAS,UAAY,QAAlD,EACE,wCAAY,SACf;AAEJ;","names":[]}
@@ -0,0 +1,13 @@
1
+ import * as React from 'react';
2
+
3
+ interface QuickSegmentProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "onSelect" | "value"> {
4
+ label: React.ReactNode;
5
+ value: string;
6
+ selected?: boolean;
7
+ count?: number | string;
8
+ description?: React.ReactNode;
9
+ onSelect?: (value: string) => void;
10
+ }
11
+ declare function QuickSegment({ label, value, selected, count, description, onSelect, className, type, ...props }: QuickSegmentProps): React.JSX.Element;
12
+
13
+ export { QuickSegment, type QuickSegmentProps };
@@ -0,0 +1,96 @@
1
+ "use client"
2
+
3
+ "use client";
4
+ var __defProp = Object.defineProperty;
5
+ var __defProps = Object.defineProperties;
6
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
7
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
10
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
11
+ var __spreadValues = (a, b) => {
12
+ for (var prop in b || (b = {}))
13
+ if (__hasOwnProp.call(b, prop))
14
+ __defNormalProp(a, prop, b[prop]);
15
+ if (__getOwnPropSymbols)
16
+ for (var prop of __getOwnPropSymbols(b)) {
17
+ if (__propIsEnum.call(b, prop))
18
+ __defNormalProp(a, prop, b[prop]);
19
+ }
20
+ return a;
21
+ };
22
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
23
+ var __objRest = (source, exclude) => {
24
+ var target = {};
25
+ for (var prop in source)
26
+ if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
27
+ target[prop] = source[prop];
28
+ if (source != null && __getOwnPropSymbols)
29
+ for (var prop of __getOwnPropSymbols(source)) {
30
+ if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
31
+ target[prop] = source[prop];
32
+ }
33
+ return target;
34
+ };
35
+ import { jsx, jsxs } from "react/jsx-runtime";
36
+ import { cn } from "../lib/utils.js";
37
+ function QuickSegment(_a) {
38
+ var _b = _a, {
39
+ label,
40
+ value,
41
+ selected = false,
42
+ count,
43
+ description,
44
+ onSelect,
45
+ className,
46
+ type = "button"
47
+ } = _b, props = __objRest(_b, [
48
+ "label",
49
+ "value",
50
+ "selected",
51
+ "count",
52
+ "description",
53
+ "onSelect",
54
+ "className",
55
+ "type"
56
+ ]);
57
+ return /* @__PURE__ */ jsxs(
58
+ "button",
59
+ __spreadProps(__spreadValues({
60
+ "data-slot": "quick-segment",
61
+ "data-selected": selected ? "true" : "false",
62
+ type,
63
+ "aria-pressed": selected,
64
+ className: cn(
65
+ "inline-flex min-h-8 items-center gap-2 rounded-full border px-3 py-1.5 text-sm font-medium transition-colors",
66
+ selected ? "border-primary bg-primary text-primary-foreground shadow-sm" : "border-border bg-background text-muted-foreground hover:bg-muted hover:text-foreground",
67
+ className
68
+ ),
69
+ onClick: (event) => {
70
+ var _a2;
71
+ (_a2 = props.onClick) == null ? void 0 : _a2.call(props, event);
72
+ if (!event.defaultPrevented) onSelect == null ? void 0 : onSelect(value);
73
+ }
74
+ }, props), {
75
+ children: [
76
+ /* @__PURE__ */ jsx("span", { "data-slot": "quick-segment-label", children: label }),
77
+ count !== void 0 ? /* @__PURE__ */ jsx(
78
+ "span",
79
+ {
80
+ "data-slot": "quick-segment-count",
81
+ className: cn(
82
+ "rounded-full px-1.5 py-0.5 text-[11px] leading-none",
83
+ selected ? "bg-primary-foreground/20 text-primary-foreground" : "bg-muted text-muted-foreground"
84
+ ),
85
+ children: count
86
+ }
87
+ ) : null,
88
+ description ? /* @__PURE__ */ jsx("span", { "data-slot": "quick-segment-description", className: "sr-only", children: description }) : null
89
+ ]
90
+ })
91
+ );
92
+ }
93
+ export {
94
+ QuickSegment
95
+ };
96
+ //# sourceMappingURL=quick-segment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/quick-segment.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\n\nimport { cn } from \"../lib/utils\"\n\nexport interface QuickSegmentProps\n extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, \"onSelect\" | \"value\"> {\n label: React.ReactNode\n value: string\n selected?: boolean\n count?: number | string\n description?: React.ReactNode\n onSelect?: (value: string) => void\n}\n\nexport function QuickSegment({\n label,\n value,\n selected = false,\n count,\n description,\n onSelect,\n className,\n type = \"button\",\n ...props\n}: QuickSegmentProps) {\n return (\n <button\n data-slot=\"quick-segment\"\n data-selected={selected ? \"true\" : \"false\"}\n type={type}\n aria-pressed={selected}\n className={cn(\n \"inline-flex min-h-8 items-center gap-2 rounded-full border px-3 py-1.5 text-sm font-medium transition-colors\",\n selected\n ? \"border-primary bg-primary text-primary-foreground shadow-sm\"\n : \"border-border bg-background text-muted-foreground hover:bg-muted hover:text-foreground\",\n className\n )}\n onClick={(event) => {\n props.onClick?.(event)\n if (!event.defaultPrevented) onSelect?.(value)\n }}\n {...props}\n >\n <span data-slot=\"quick-segment-label\">{label}</span>\n {count !== undefined ? (\n <span\n data-slot=\"quick-segment-count\"\n className={cn(\n \"rounded-full px-1.5 py-0.5 text-[11px] leading-none\",\n selected\n ? \"bg-primary-foreground/20 text-primary-foreground\"\n : \"bg-muted text-muted-foreground\"\n )}\n >\n {count}\n </span>\n ) : null}\n {description ? (\n <span data-slot=\"quick-segment-description\" className=\"sr-only\">\n {description}\n </span>\n ) : null}\n </button>\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BI,SAkBE,KAlBF;AAxBJ,SAAS,UAAU;AAYZ,SAAS,aAAa,IAUP;AAVO,eAC3B;AAAA;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO;AAAA,EAxBT,IAgB6B,IASxB,kBATwB,IASxB;AAAA,IARH;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA;AAGA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,iBAAe,WAAW,SAAS;AAAA,MACnC;AAAA,MACA,gBAAc;AAAA,MACd,WAAW;AAAA,QACT;AAAA,QACA,WACI,gEACA;AAAA,QACJ;AAAA,MACF;AAAA,MACA,SAAS,CAAC,UAAU;AAxC1B,YAAAA;AAyCQ,SAAAA,MAAA,MAAM,YAAN,gBAAAA,IAAA,YAAgB;AAChB,YAAI,CAAC,MAAM,iBAAkB,sCAAW;AAAA,MAC1C;AAAA,OACI,QAhBL;AAAA,MAkBC;AAAA,4BAAC,UAAK,aAAU,uBAAuB,iBAAM;AAAA,QAC5C,UAAU,SACT;AAAA,UAAC;AAAA;AAAA,YACC,aAAU;AAAA,YACV,WAAW;AAAA,cACT;AAAA,cACA,WACI,qDACA;AAAA,YACN;AAAA,YAEC;AAAA;AAAA,QACH,IACE;AAAA,QACH,cACC,oBAAC,UAAK,aAAU,6BAA4B,WAAU,WACnD,uBACH,IACE;AAAA;AAAA;AAAA,EACN;AAEJ;","names":["_a"]}
package/dist/index.d.ts CHANGED
@@ -23,6 +23,7 @@ export { DataTableFilter, DataTableFilterCategory, DataTableFilterProps, FilterO
23
23
  export { DataTableQuickViewValue, DataTableQuickViews } from './components/data-table-quick-views.js';
24
24
  export { DataTableToolbar } from './components/data-table-toolbar.js';
25
25
  export { Citation, DetailViewHeader, DetailViewSummary, DetailViewThread, SourceDef, SourceList, ThreadMessage } from './components/detail-view.js';
26
+ export { DetailDrawer, DetailDrawerProps } from './components/detail-drawer.js';
26
27
  export { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger } from './components/dialog.js';
27
28
  export { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger } from './components/dropdown-menu.js';
28
29
  export { EmptyState, EmptyStateProps } from './components/empty-state.js';
@@ -35,6 +36,8 @@ export { AssigneeFilter, InboxFilterCategory, InboxToolbar, InboxToolbarProps }
35
36
  export { InlineBanner, InlineBannerProps } from './components/inline-banner.js';
36
37
  export { Input } from './components/input.js';
37
38
  export { FilterDefinition, InsightsFilterBar, InsightsFilterBarProps } from './components/insights-filter-bar.js';
39
+ export { DaysOpenCell, DaysOpenCellProps, getDaysOpenIntent } from './components/days-open-cell.js';
40
+ export { LinkedEntityCell, LinkedEntityCellProps } from './components/linked-entity-cell.js';
38
41
  export { GroupedListGroup, GroupedListView, GroupedListViewProps, ItemList } from './components/item-list.js';
39
42
  export { ItemListDisplay, ItemListDisplayState, ItemListGrouping, ItemListViewMode } from './components/item-list-display.js';
40
43
  export { ItemListFilter, ItemListFilterCategory } from './components/item-list-filter.js';
@@ -42,11 +45,13 @@ export { ItemListQuickView, ItemListToolbar } from './components/item-list-toolb
42
45
  export { KbdHint } from './components/kbd-hint.js';
43
46
  export { Label } from './components/label.js';
44
47
  export { Message, MessageAvatar, MessageAvatarProps, MessageContent, MessageContentProps, MessageProps } from './components/message.js';
45
- export { MetricCard, MetricCardProps, MetricDataPoint } from './components/metric-card.js';
48
+ export { KpiStrip, KpiStripItem, KpiStripProps, MetricCard, MetricCardProps, MetricDataPoint } from './components/metric-card.js';
46
49
  export { PerformanceMetricsTable, PerformanceMetricsTableRow, PerformanceMetricsTableSortOption } from './components/performance-metrics-table.js';
50
+ export { Pill, PillProps, PillStatus, StatusPill, StatusPillProps, pillVariants } from './components/pill.js';
47
51
  export { PreviewList, PreviewListItem, PreviewListItemProps } from './components/preview-list.js';
48
52
  export { Progress } from './components/progress.js';
49
53
  export { QuickActionChatArea, QuickActionSubmitPayload } from './components/quick-action-chat-area.js';
54
+ export { QuickSegment, QuickSegmentProps } from './components/quick-segment.js';
50
55
  export { QuickActionModal, QuickActionPriority, QuickActionTaskDraft, QuickActionTemplate } from './components/quick-action-modal.js';
51
56
  export { ActiveVariant, QuickActionSidebarNav, SidebarNavItem, SidebarNavSection, SidebarUserProfile, UserMenuItem } from './components/quick-action-sidebar-nav.js';
52
57
  export { RecommendedAction, RecommendedActionsSection } from './components/recommended-actions-section.js';
@@ -92,6 +97,7 @@ export { VolumeAnalysisChart, VolumeAnalysisChartProps, VolumeDataKey } from './
92
97
  export { MetricCardData, TopLineMetrics, TopLineMetricsProps } from './charts/top-line-metrics.js';
93
98
  export { PipelineFilterBreakdown, PipelineOverview, PipelineOverviewProps, PipelineStage, PipelineStageMetrics, PipelineStageTiming } from './charts/pipeline-overview.js';
94
99
  export { SankeyChart, SankeyData, SankeyDropOff, SankeyHoverCardData, SankeyLink, SankeyNode, SankeyStageMetrics } from './charts/sankey-chart.js';
100
+ export { EmptyChartState, EmptyChartStateProps } from './charts/empty-chart-state.js';
95
101
  export { PrototypeShell, PrototypeShellProps } from './prototype/prototype-shell.js';
96
102
  export { DetailView, DetailViewProps, PrototypeInboxView, PrototypeInboxViewProps } from './prototype/prototype-inbox-view.js';
97
103
  export { PrototypeInsightsView, PrototypeInsightsViewProps } from './prototype/prototype-insights-view.js';
package/dist/index.js CHANGED
@@ -23,6 +23,7 @@ export * from "./components/data-table-filter.js";
23
23
  export * from "./components/data-table-quick-views.js";
24
24
  export * from "./components/data-table-toolbar.js";
25
25
  export * from "./components/detail-view.js";
26
+ export * from "./components/detail-drawer.js";
26
27
  export * from "./components/dialog.js";
27
28
  export * from "./components/dropdown-menu.js";
28
29
  export * from "./components/empty-state.js";
@@ -35,6 +36,8 @@ export * from "./components/inbox-toolbar.js";
35
36
  export * from "./components/inline-banner.js";
36
37
  export * from "./components/input.js";
37
38
  export * from "./components/insights-filter-bar.js";
39
+ export * from "./components/days-open-cell.js";
40
+ export * from "./components/linked-entity-cell.js";
38
41
  export * from "./components/item-list.js";
39
42
  export * from "./components/item-list-display.js";
40
43
  export * from "./components/item-list-filter.js";
@@ -44,9 +47,11 @@ export * from "./components/label.js";
44
47
  export * from "./components/message.js";
45
48
  export * from "./components/metric-card.js";
46
49
  export * from "./components/performance-metrics-table.js";
50
+ export * from "./components/pill.js";
47
51
  export * from "./components/preview-list.js";
48
52
  export * from "./components/progress.js";
49
53
  export * from "./components/quick-action-chat-area.js";
54
+ export * from "./components/quick-segment.js";
50
55
  import {
51
56
  QuickActionModal
52
57
  } from "./components/quick-action-modal.js";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * @handled-ai/design-system\n * UI components and utilities (shadcn-style, New York)\n */\n\n// Utilities\nexport { cn } from \"./lib/utils\"\nexport { BRAND_ICONS, BRAND_GRAPHICS } from \"./lib/icons\"\nexport { displayName, getInitials, shortName, type ProfileLike } from \"./lib/user-display\"\n\n// Hooks\nexport { useIsMobile } from \"./hooks/use-mobile\"\n\n// Components (light — no recharts/nivo/three transitive deps)\nexport * from \"./components/activity-detail\"\nexport * from \"./components/activity-log\"\nexport * from \"./components/agent-popover\"\nexport * from \"./components/agent-widget\"\nexport * from \"./components/avatar\"\nexport * from \"./components/badge\"\nexport * from \"./components/button\"\nexport * from \"./components/card\"\nexport { CollapsibleSection, type CollapsibleSectionProps } from \"./components/collapsible-section\"\nexport * from \"./components/compliance-badge\"\nexport * from \"./components/contact-chip\"\nexport * from \"./components/contact-list\"\nexport * from \"./components/contextual-quick-action-launcher\"\nexport * from \"./components/dashboard-cards\"\nexport * from \"./components/data-table\"\nexport * from \"./components/data-table-condition-filter\"\nexport * from \"./components/data-table-display\"\nexport * from \"./components/data-table-filter\"\nexport * from \"./components/data-table-quick-views\"\nexport * from \"./components/data-table-toolbar\"\nexport * from \"./components/detail-view\"\nexport * from \"./components/dialog\"\nexport * from \"./components/dropdown-menu\"\nexport * from \"./components/empty-state\"\nexport * from \"./components/entity-panel\"\nexport { FeedbackFooter, FeedbackChipGroup, FeedbackInput, FeedbackActions } from \"./components/feedback-primitives\"\nexport type { FeedbackFooterProps, FeedbackChipTree, FeedbackChipGroupProps, FeedbackInputProps, FeedbackActionsProps, FeedbackSubmitData } from \"./components/feedback-primitives\"\nexport { SignalPriorityPopover } from \"./components/signal-priority-popover\"\nexport type { SignalPriorityPopoverProps, PriorityFactor } from \"./components/signal-priority-popover\"\nexport * from \"./components/filter-chip\"\nexport * from \"./components/inbox-row\"\nexport * from \"./components/inbox-toolbar\"\nexport * from \"./components/inline-banner\"\nexport * from \"./components/input\"\nexport * from \"./components/insights-filter-bar\"\nexport * from \"./components/item-list\"\nexport * from \"./components/item-list-display\"\nexport * from \"./components/item-list-filter\"\nexport * from \"./components/item-list-toolbar\"\nexport * from \"./components/kbd-hint\"\nexport * from \"./components/label\"\nexport * from \"./components/message\"\nexport * from \"./components/metric-card\"\nexport * from \"./components/performance-metrics-table\"\nexport * from \"./components/preview-list\"\nexport * from \"./components/progress\"\nexport * from \"./components/quick-action-chat-area\"\nexport {\n QuickActionModal,\n type QuickActionPriority,\n type QuickActionTaskDraft,\n type QuickActionTemplate,\n} from \"./components/quick-action-modal\"\nexport * from \"./components/quick-action-sidebar-nav\"\nexport * from \"./components/recommended-actions-section\"\nexport * from \"./components/report-card\"\nexport * from \"./components/rich-text-toolbar\"\nexport * from \"./components/score-analysis-modal\"\nexport * from \"./components/score-breakdown\"\nexport * from \"./components/score-feedback\"\nexport * from \"./components/score-why-chips\"\nexport * from \"./components/score-ring\"\nexport * from \"./components/scroll-area\"\nexport * from \"./components/select\"\nexport * from \"./components/separator\"\nexport * from \"./components/sheet\"\nexport * from \"./components/sidebar\"\nexport * from \"./components/signal-feedback-inline\"\nexport * from \"./components/simple-data-table\"\nexport * from \"./components/skeleton\"\nexport * from \"./components/status-badge\"\nexport * from \"./components/step-timeline\"\nexport * from \"./components/sticky-action-bar\"\nexport * from \"./components/styled-bar-list\"\nexport { DraftFeedbackInline } from \"./components/draft-feedback-inline\"\nexport type { DraftFeedbackInlineProps } from \"./components/draft-feedback-inline\"\nexport { AccountContactsPopover, BrandIcon } from \"./components/account-contacts-popover\"\nexport type { AccountContactsPopoverProps } from \"./components/account-contacts-popover\"\nexport * from \"./components/suggested-actions\"\nexport * from \"./components/switch\"\nexport * from \"./components/table\"\nexport * from \"./components/tabs\"\nexport * from \"./components/textarea\"\nexport * from \"./components/timeline-activity\"\nexport * from \"./components/tooltip\"\nexport * from \"./components/user-display\"\nexport * from \"./components/variable-autocomplete\"\nexport * from \"./components/view-mode-toggle\"\nexport * from \"./components/virtualized-data-table\"\nexport type { ColumnSizingState } from \"@tanstack/react-table\"\n\n// Charts (re-exported for backward compatibility with root imports)\nexport * from \"./charts/index\"\n\n// Prototype template system (re-exported for backward compatibility)\nexport * from \"./prototype/prototype-config\"\nexport * from \"./prototype/prototype-shell\"\nexport * from \"./prototype/prototype-inbox-view\"\nexport * from \"./prototype/prototype-insights-view\"\nexport * from \"./prototype/prototype-accounts-view\"\nexport * from \"./prototype/prototype-admin-view\"\nexport * from \"./prototype/prototype-work-queue-view\"\n"],"mappings":"AAMA,SAAS,UAAU;AACnB,SAAS,aAAa,sBAAsB;AAC5C,SAAS,aAAa,aAAa,iBAAmC;AAGtE,SAAS,mBAAmB;AAG5B,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,SAAS,0BAAwD;AACjE,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,SAAS,gBAAgB,mBAAmB,eAAe,uBAAuB;AAElF,SAAS,6BAA6B;AAEtC,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd;AAAA,EACE;AAAA,OAIK;AACP,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,SAAS,2BAA2B;AAEpC,SAAS,wBAAwB,iBAAiB;AAElD,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AAId,cAAc;AAGd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * @handled-ai/design-system\n * UI components and utilities (shadcn-style, New York)\n */\n\n// Utilities\nexport { cn } from \"./lib/utils\"\nexport { BRAND_ICONS, BRAND_GRAPHICS } from \"./lib/icons\"\nexport { displayName, getInitials, shortName, type ProfileLike } from \"./lib/user-display\"\n\n// Hooks\nexport { useIsMobile } from \"./hooks/use-mobile\"\n\n// Components (light — no recharts/nivo/three transitive deps)\nexport * from \"./components/activity-detail\"\nexport * from \"./components/activity-log\"\nexport * from \"./components/agent-popover\"\nexport * from \"./components/agent-widget\"\nexport * from \"./components/avatar\"\nexport * from \"./components/badge\"\nexport * from \"./components/button\"\nexport * from \"./components/card\"\nexport { CollapsibleSection, type CollapsibleSectionProps } from \"./components/collapsible-section\"\nexport * from \"./components/compliance-badge\"\nexport * from \"./components/contact-chip\"\nexport * from \"./components/contact-list\"\nexport * from \"./components/contextual-quick-action-launcher\"\nexport * from \"./components/dashboard-cards\"\nexport * from \"./components/data-table\"\nexport * from \"./components/data-table-condition-filter\"\nexport * from \"./components/data-table-display\"\nexport * from \"./components/data-table-filter\"\nexport * from \"./components/data-table-quick-views\"\nexport * from \"./components/data-table-toolbar\"\nexport * from \"./components/detail-view\"\nexport * from \"./components/detail-drawer\"\nexport * from \"./components/dialog\"\nexport * from \"./components/dropdown-menu\"\nexport * from \"./components/empty-state\"\nexport * from \"./components/entity-panel\"\nexport { FeedbackFooter, FeedbackChipGroup, FeedbackInput, FeedbackActions } from \"./components/feedback-primitives\"\nexport type { FeedbackFooterProps, FeedbackChipTree, FeedbackChipGroupProps, FeedbackInputProps, FeedbackActionsProps, FeedbackSubmitData } from \"./components/feedback-primitives\"\nexport { SignalPriorityPopover } from \"./components/signal-priority-popover\"\nexport type { SignalPriorityPopoverProps, PriorityFactor } from \"./components/signal-priority-popover\"\nexport * from \"./components/filter-chip\"\nexport * from \"./components/inbox-row\"\nexport * from \"./components/inbox-toolbar\"\nexport * from \"./components/inline-banner\"\nexport * from \"./components/input\"\nexport * from \"./components/insights-filter-bar\"\nexport * from \"./components/days-open-cell\"\nexport * from \"./components/linked-entity-cell\"\nexport * from \"./components/item-list\"\nexport * from \"./components/item-list-display\"\nexport * from \"./components/item-list-filter\"\nexport * from \"./components/item-list-toolbar\"\nexport * from \"./components/kbd-hint\"\nexport * from \"./components/label\"\nexport * from \"./components/message\"\nexport * from \"./components/metric-card\"\nexport * from \"./components/performance-metrics-table\"\nexport * from \"./components/pill\"\nexport * from \"./components/preview-list\"\nexport * from \"./components/progress\"\nexport * from \"./components/quick-action-chat-area\"\nexport * from \"./components/quick-segment\"\nexport {\n QuickActionModal,\n type QuickActionPriority,\n type QuickActionTaskDraft,\n type QuickActionTemplate,\n} from \"./components/quick-action-modal\"\nexport * from \"./components/quick-action-sidebar-nav\"\nexport * from \"./components/recommended-actions-section\"\nexport * from \"./components/report-card\"\nexport * from \"./components/rich-text-toolbar\"\nexport * from \"./components/score-analysis-modal\"\nexport * from \"./components/score-breakdown\"\nexport * from \"./components/score-feedback\"\nexport * from \"./components/score-why-chips\"\nexport * from \"./components/score-ring\"\nexport * from \"./components/scroll-area\"\nexport * from \"./components/select\"\nexport * from \"./components/separator\"\nexport * from \"./components/sheet\"\nexport * from \"./components/sidebar\"\nexport * from \"./components/signal-feedback-inline\"\nexport * from \"./components/simple-data-table\"\nexport * from \"./components/skeleton\"\nexport * from \"./components/status-badge\"\nexport * from \"./components/step-timeline\"\nexport * from \"./components/sticky-action-bar\"\nexport * from \"./components/styled-bar-list\"\nexport { DraftFeedbackInline } from \"./components/draft-feedback-inline\"\nexport type { DraftFeedbackInlineProps } from \"./components/draft-feedback-inline\"\nexport { AccountContactsPopover, BrandIcon } from \"./components/account-contacts-popover\"\nexport type { AccountContactsPopoverProps } from \"./components/account-contacts-popover\"\nexport * from \"./components/suggested-actions\"\nexport * from \"./components/switch\"\nexport * from \"./components/table\"\nexport * from \"./components/tabs\"\nexport * from \"./components/textarea\"\nexport * from \"./components/timeline-activity\"\nexport * from \"./components/tooltip\"\nexport * from \"./components/user-display\"\nexport * from \"./components/variable-autocomplete\"\nexport * from \"./components/view-mode-toggle\"\nexport * from \"./components/virtualized-data-table\"\nexport type { ColumnSizingState } from \"@tanstack/react-table\"\n\n// Charts (re-exported for backward compatibility with root imports)\nexport * from \"./charts/index\"\n\n// Prototype template system (re-exported for backward compatibility)\nexport * from \"./prototype/prototype-config\"\nexport * from \"./prototype/prototype-shell\"\nexport * from \"./prototype/prototype-inbox-view\"\nexport * from \"./prototype/prototype-insights-view\"\nexport * from \"./prototype/prototype-accounts-view\"\nexport * from \"./prototype/prototype-admin-view\"\nexport * from \"./prototype/prototype-work-queue-view\"\n"],"mappings":"AAMA,SAAS,UAAU;AACnB,SAAS,aAAa,sBAAsB;AAC5C,SAAS,aAAa,aAAa,iBAAmC;AAGtE,SAAS,mBAAmB;AAG5B,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,SAAS,0BAAwD;AACjE,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,SAAS,gBAAgB,mBAAmB,eAAe,uBAAuB;AAElF,SAAS,6BAA6B;AAEtC,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd;AAAA,EACE;AAAA,OAIK;AACP,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,SAAS,2BAA2B;AAEpC,SAAS,wBAAwB,iBAAiB;AAElD,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AAId,cAAc;AAGd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@handled-ai/design-system",
3
- "version": "0.17.2",
3
+ "version": "0.18.1",
4
4
  "description": "Handled UI component library (shadcn-style, New York)",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@9.12.0",
@@ -0,0 +1,62 @@
1
+ import React from "react"
2
+ import { describe, expect, it, vi } from "vitest"
3
+ import { render, screen, fireEvent } from "@testing-library/react"
4
+
5
+ import { EmptyChartState } from "../empty-chart-state"
6
+ import { PipelineOverview, type PipelineStage, type PipelineStageMetrics } from "../pipeline-overview"
7
+
8
+ vi.mock("@nivo/sankey", () => ({
9
+ ResponsiveSankey: ({ data }: { data: { nodes: unknown[]; links: unknown[] } }) => (
10
+ <div data-testid="mock-sankey">{data.nodes.length}:{data.links.length}</div>
11
+ ),
12
+ }))
13
+
14
+ const stages: PipelineStage[] = [
15
+ { id: "received", label: "Received", count: 100, trend: "+5%", nextConversion: "80%" },
16
+ { id: "contacted", label: "Contacted", count: 80, trend: "+3%", nextConversion: null },
17
+ ]
18
+
19
+ const metrics: Record<string, PipelineStageMetrics> = {
20
+ received: { medianTime: "1d", avgTime: "2d", dropOffs: [] },
21
+ contacted: { medianTime: "2d", avgTime: "3d", dropOffs: [] },
22
+ }
23
+
24
+ function renderPipeline(variant?: "sankey" | "compact", onFilterChange = vi.fn()) {
25
+ return render(
26
+ <PipelineOverview
27
+ variant={variant}
28
+ stages={stages}
29
+ stageMetrics={metrics}
30
+ stageTimings={[{ median: "1d", avg: "2d" }, null]}
31
+ filterOptions={["Facility", "Source"]}
32
+ onFilterChange={onFilterChange}
33
+ />
34
+ )
35
+ }
36
+
37
+ describe("Insights chart primitives", () => {
38
+ it("EmptyChartState renders defaults and action", () => {
39
+ const { container } = render(<EmptyChartState action={<button type="button">Reset</button>} />)
40
+
41
+ expect(container.querySelector('[data-slot="empty-chart-state"]')).not.toBeNull()
42
+ expect(screen.getByText("No chart data")).not.toBeNull()
43
+ expect(screen.getByRole("button", { name: "Reset" })).not.toBeNull()
44
+ })
45
+
46
+ it("PipelineOverview default variant preserves Sankey rendering and callbacks", () => {
47
+ const onFilterChange = vi.fn()
48
+ renderPipeline(undefined, onFilterChange)
49
+
50
+ expect(screen.getByTestId("mock-sankey")).not.toBeNull()
51
+ fireEvent.click(screen.getByRole("button", { name: "Source" }))
52
+ expect(onFilterChange).toHaveBeenCalledWith("Source")
53
+ })
54
+
55
+ it("PipelineOverview compact variant renders compact bars instead of Sankey", () => {
56
+ const { container } = renderPipeline("compact")
57
+
58
+ expect(screen.queryByTestId("mock-sankey")).toBeNull()
59
+ expect(container.querySelector('[data-slot="pipeline-overview-compact"]')).not.toBeNull()
60
+ expect(container.querySelectorAll('[data-slot="pipeline-overview-compact-bar"]')).toHaveLength(2)
61
+ })
62
+ })
@@ -0,0 +1,44 @@
1
+ import * as React from "react"
2
+ import { BarChart3 } from "lucide-react"
3
+
4
+ import { cn } from "../lib/utils"
5
+
6
+ export interface EmptyChartStateProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "title"> {
7
+ title?: React.ReactNode
8
+ description?: React.ReactNode
9
+ icon?: React.ReactNode
10
+ action?: React.ReactNode
11
+ }
12
+
13
+ export function EmptyChartState({
14
+ title = "No chart data",
15
+ description = "Try adjusting filters or selecting a different time range.",
16
+ icon,
17
+ action,
18
+ className,
19
+ ...props
20
+ }: EmptyChartStateProps) {
21
+ return (
22
+ <div
23
+ data-slot="empty-chart-state"
24
+ className={cn(
25
+ "flex min-h-[240px] flex-col items-center justify-center rounded-xl border border-dashed border-border bg-muted/30 p-8 text-center",
26
+ className
27
+ )}
28
+ {...props}
29
+ >
30
+ <div data-slot="empty-chart-state-icon" className="mb-3 text-muted-foreground [&>svg]:h-10 [&>svg]:w-10">
31
+ {icon ?? <BarChart3 aria-hidden="true" />}
32
+ </div>
33
+ <div data-slot="empty-chart-state-title" className="text-sm font-semibold text-foreground">
34
+ {title}
35
+ </div>
36
+ {description ? (
37
+ <div data-slot="empty-chart-state-description" className="mt-1 max-w-sm text-sm text-muted-foreground">
38
+ {description}
39
+ </div>
40
+ ) : null}
41
+ {action ? <div data-slot="empty-chart-state-action" className="mt-4">{action}</div> : null}
42
+ </div>
43
+ )
44
+ }
@@ -11,3 +11,4 @@ export * from "./volume-analysis-chart"
11
11
  export * from "./top-line-metrics"
12
12
  export * from "./pipeline-overview"
13
13
  export * from "./sankey-chart"
14
+ export * from "./empty-chart-state"
@@ -125,6 +125,7 @@ function StageHoverCard({
125
125
 
126
126
  export interface PipelineOverviewProps {
127
127
  title?: string
128
+ variant?: "sankey" | "compact"
128
129
  stages: PipelineStage[]
129
130
  stageMetrics: Record<string, PipelineStageMetrics>
130
131
  stageTimings: (PipelineStageTiming | null)[]
@@ -162,6 +163,7 @@ export interface PipelineOverviewProps {
162
163
 
163
164
  export function PipelineOverview({
164
165
  title = "Pipeline Overview",
166
+ variant = "sankey",
165
167
  stages,
166
168
  stageMetrics,
167
169
  stageTimings,
@@ -424,8 +426,42 @@ export function PipelineOverview({
424
426
  })}
425
427
  </div>
426
428
 
427
- {/* Sankey Chart */}
429
+ {variant === "compact" ? (
430
+ <div data-slot="pipeline-overview-compact" className="mt-4 space-y-2">
431
+ {stages.map((stage, index) => {
432
+ const maxCount = Math.max(...stages.map((item) => item.count), 1)
433
+ const width = `${Math.max((stage.count / maxCount) * 100, 4)}%`
434
+
435
+ return (
436
+ <div key={stage.id} className="rounded-lg border border-border bg-background p-3">
437
+ <div className="mb-2 flex items-center justify-between gap-3">
438
+ <div className="min-w-0">
439
+ <div className="truncate text-sm font-medium text-foreground">{stage.label}</div>
440
+ <div className="text-xs text-muted-foreground">{stage.trend}</div>
441
+ </div>
442
+ <div className="text-right text-lg font-bold text-foreground">
443
+ {stage.count.toLocaleString()}
444
+ </div>
445
+ </div>
446
+ <div className="h-2 overflow-hidden rounded-full bg-muted">
447
+ <div
448
+ data-slot="pipeline-overview-compact-bar"
449
+ className="h-full rounded-full bg-emerald-600"
450
+ style={{ width }}
451
+ />
452
+ </div>
453
+ {index < stages.length - 1 && stage.nextConversion ? (
454
+ <div className="mt-2 text-xs text-muted-foreground">
455
+ {stage.nextConversion} to {stages[index + 1]?.label}
456
+ </div>
457
+ ) : null}
458
+ </div>
459
+ )
460
+ })}
461
+ </div>
462
+ ) : (
428
463
  <div
464
+ data-slot="pipeline-overview-sankey"
429
465
  className="relative mt-4 w-full"
430
466
  style={{ height: 400, minWidth: 0 }}
431
467
  >
@@ -471,6 +507,7 @@ export function PipelineOverview({
471
507
  }}
472
508
  />
473
509
  </div>
510
+ )}
474
511
  </div>
475
512
  )
476
513
  }