@handled-ai/design-system 0.18.4 → 0.18.5

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 (76) hide show
  1. package/dist/charts/chart.d.ts +1 -1
  2. package/dist/charts/empty-chart-state.d.ts +11 -0
  3. package/dist/charts/empty-chart-state.js +70 -0
  4. package/dist/charts/empty-chart-state.js.map +1 -0
  5. package/dist/charts/index.d.ts +1 -0
  6. package/dist/charts/index.js +1 -0
  7. package/dist/charts/index.js.map +1 -1
  8. package/dist/charts/pipeline-overview.d.ts +2 -1
  9. package/dist/charts/pipeline-overview.js +32 -1
  10. package/dist/charts/pipeline-overview.js.map +1 -1
  11. package/dist/components/badge.d.ts +1 -1
  12. package/dist/components/button.d.ts +1 -1
  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/feedback-primitives.d.ts +2 -41
  20. package/dist/components/feedback-primitives.js +6 -241
  21. package/dist/components/feedback-primitives.js.map +1 -1
  22. package/dist/components/insights-filter-bar.d.ts +2 -1
  23. package/dist/components/insights-filter-bar.js +13 -5
  24. package/dist/components/insights-filter-bar.js.map +1 -1
  25. package/dist/components/linked-entity-cell.d.ts +14 -0
  26. package/dist/components/linked-entity-cell.js +96 -0
  27. package/dist/components/linked-entity-cell.js.map +1 -0
  28. package/dist/components/metric-card.d.ts +14 -1
  29. package/dist/components/metric-card.js +97 -0
  30. package/dist/components/metric-card.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/components/score-why-chips.d.ts +1 -1
  38. package/dist/components/score-why-chips.js +5 -26
  39. package/dist/components/score-why-chips.js.map +1 -1
  40. package/dist/components/signal-priority-popover.d.ts +1 -1
  41. package/dist/components/signal-priority-popover.js +6 -32
  42. package/dist/components/signal-priority-popover.js.map +1 -1
  43. package/dist/components/tabs.d.ts +1 -1
  44. package/dist/index.d.ts +9 -3
  45. package/dist/index.js +6 -2
  46. package/dist/index.js.map +1 -1
  47. package/dist/prototype/index.d.ts +1 -1
  48. package/dist/prototype/prototype-accounts-view.d.ts +1 -1
  49. package/dist/prototype/prototype-admin-view.d.ts +1 -1
  50. package/dist/prototype/prototype-config.d.ts +1 -1
  51. package/dist/prototype/prototype-inbox-view.d.ts +1 -1
  52. package/dist/prototype/prototype-inbox-view.js +1 -4
  53. package/dist/prototype/prototype-inbox-view.js.map +1 -1
  54. package/dist/prototype/prototype-insights-view.d.ts +1 -1
  55. package/dist/prototype/prototype-shell.d.ts +1 -1
  56. package/dist/{signal-priority-popover-DWaAMhPI.d.ts → signal-priority-popover-DQ_VuHac.d.ts} +2 -26
  57. package/package.json +1 -3
  58. package/src/charts/__tests__/insights-charts.test.tsx +62 -0
  59. package/src/charts/empty-chart-state.tsx +44 -0
  60. package/src/charts/index.ts +1 -0
  61. package/src/charts/pipeline-overview.tsx +41 -1
  62. package/src/components/__tests__/insights-primitives.test.tsx +135 -0
  63. package/src/components/days-open-cell.tsx +50 -0
  64. package/src/components/detail-drawer.tsx +60 -0
  65. package/src/components/feedback-primitives.tsx +26 -333
  66. package/src/components/insights-filter-bar.tsx +13 -4
  67. package/src/components/linked-entity-cell.tsx +74 -0
  68. package/src/components/metric-card.tsx +98 -0
  69. package/src/components/pill.tsx +67 -0
  70. package/src/components/quick-segment.tsx +68 -0
  71. package/src/components/score-why-chips.tsx +2 -28
  72. package/src/components/signal-priority-popover.tsx +4 -44
  73. package/src/index.ts +7 -2
  74. package/src/prototype/prototype-config.ts +1 -11
  75. package/src/prototype/prototype-inbox-view.tsx +0 -3
  76. package/src/components/__tests__/wit-636-feedback-states.test.tsx +0 -546
@@ -48,7 +48,7 @@ declare function ChartTooltipContent({ active, payload, className, indicator, hi
48
48
  labelClassName?: string;
49
49
  color?: string;
50
50
  }): React.JSX.Element | null;
51
- declare const ChartLegend: React.MemoExoticComponent<(outsideProps: RechartsPrimitive.LegendProps) => React.ReactPortal | null>;
51
+ declare const ChartLegend: typeof RechartsPrimitive.Legend;
52
52
  type LegendPayloadItem = {
53
53
  value?: string;
54
54
  dataKey?: string;
@@ -0,0 +1,11 @@
1
+ import * as React from 'react';
2
+
3
+ interface EmptyChartStateProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "title"> {
4
+ title?: React.ReactNode;
5
+ description?: React.ReactNode;
6
+ icon?: React.ReactNode;
7
+ action?: React.ReactNode;
8
+ }
9
+ declare function EmptyChartState({ title, description, icon, action, className, ...props }: EmptyChartStateProps): React.JSX.Element;
10
+
11
+ export { EmptyChartState, type EmptyChartStateProps };
@@ -0,0 +1,70 @@
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, jsxs } from "react/jsx-runtime";
33
+ import { BarChart3 } from "lucide-react";
34
+ import { cn } from "../lib/utils.js";
35
+ function EmptyChartState(_a) {
36
+ var _b = _a, {
37
+ title = "No chart data",
38
+ description = "Try adjusting filters or selecting a different time range.",
39
+ icon,
40
+ action,
41
+ className
42
+ } = _b, props = __objRest(_b, [
43
+ "title",
44
+ "description",
45
+ "icon",
46
+ "action",
47
+ "className"
48
+ ]);
49
+ return /* @__PURE__ */ jsxs(
50
+ "div",
51
+ __spreadProps(__spreadValues({
52
+ "data-slot": "empty-chart-state",
53
+ className: cn(
54
+ "flex min-h-[240px] flex-col items-center justify-center rounded-xl border border-dashed border-border bg-muted/30 p-8 text-center",
55
+ className
56
+ )
57
+ }, props), {
58
+ children: [
59
+ /* @__PURE__ */ jsx("div", { "data-slot": "empty-chart-state-icon", className: "mb-3 text-muted-foreground [&>svg]:h-10 [&>svg]:w-10", children: icon != null ? icon : /* @__PURE__ */ jsx(BarChart3, { "aria-hidden": "true" }) }),
60
+ /* @__PURE__ */ jsx("div", { "data-slot": "empty-chart-state-title", className: "text-sm font-semibold text-foreground", children: title }),
61
+ description ? /* @__PURE__ */ jsx("div", { "data-slot": "empty-chart-state-description", className: "mt-1 max-w-sm text-sm text-muted-foreground", children: description }) : null,
62
+ action ? /* @__PURE__ */ jsx("div", { "data-slot": "empty-chart-state-action", className: "mt-4", children: action }) : null
63
+ ]
64
+ })
65
+ );
66
+ }
67
+ export {
68
+ EmptyChartState
69
+ };
70
+ //# sourceMappingURL=empty-chart-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/charts/empty-chart-state.tsx"],"sourcesContent":["import * as React from \"react\"\nimport { BarChart3 } from \"lucide-react\"\n\nimport { cn } from \"../lib/utils\"\n\nexport interface EmptyChartStateProps extends Omit<React.HTMLAttributes<HTMLDivElement>, \"title\"> {\n title?: React.ReactNode\n description?: React.ReactNode\n icon?: React.ReactNode\n action?: React.ReactNode\n}\n\nexport function EmptyChartState({\n title = \"No chart data\",\n description = \"Try adjusting filters or selecting a different time range.\",\n icon,\n action,\n className,\n ...props\n}: EmptyChartStateProps) {\n return (\n <div\n data-slot=\"empty-chart-state\"\n className={cn(\n \"flex min-h-[240px] flex-col items-center justify-center rounded-xl border border-dashed border-border bg-muted/30 p-8 text-center\",\n className\n )}\n {...props}\n >\n <div data-slot=\"empty-chart-state-icon\" className=\"mb-3 text-muted-foreground [&>svg]:h-10 [&>svg]:w-10\">\n {icon ?? <BarChart3 aria-hidden=\"true\" />}\n </div>\n <div data-slot=\"empty-chart-state-title\" className=\"text-sm font-semibold text-foreground\">\n {title}\n </div>\n {description ? (\n <div data-slot=\"empty-chart-state-description\" className=\"mt-1 max-w-sm text-sm text-muted-foreground\">\n {description}\n </div>\n ) : null}\n {action ? <div data-slot=\"empty-chart-state-action\" className=\"mt-4\">{action}</div> : null}\n </div>\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqBI,SASa,KATb;AApBJ,SAAS,iBAAiB;AAE1B,SAAS,UAAU;AASZ,SAAS,gBAAgB,IAOP;AAPO,eAC9B;AAAA,YAAQ;AAAA,IACR,cAAc;AAAA,IACd;AAAA,IACA;AAAA,IACA;AAAA,EAjBF,IAYgC,IAM3B,kBAN2B,IAM3B;AAAA,IALH;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA;AAGA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,OACI,QANL;AAAA,MAQC;AAAA,4BAAC,SAAI,aAAU,0BAAyB,WAAU,wDAC/C,gCAAQ,oBAAC,aAAU,eAAY,QAAO,GACzC;AAAA,QACA,oBAAC,SAAI,aAAU,2BAA0B,WAAU,yCAChD,iBACH;AAAA,QACC,cACC,oBAAC,SAAI,aAAU,iCAAgC,WAAU,+CACtD,uBACH,IACE;AAAA,QACH,SAAS,oBAAC,SAAI,aAAU,4BAA2B,WAAU,QAAQ,kBAAO,IAAS;AAAA;AAAA;AAAA,EACxF;AAEJ;","names":[]}
@@ -7,5 +7,6 @@ export { VolumeAnalysisChart, VolumeAnalysisChartProps, VolumeDataKey } from './
7
7
  export { MetricCardData, TopLineMetrics, TopLineMetricsProps } from './top-line-metrics.js';
8
8
  export { PipelineFilterBreakdown, PipelineOverview, PipelineOverviewProps, PipelineStage, PipelineStageMetrics, PipelineStageTiming } from './pipeline-overview.js';
9
9
  export { SankeyChart, SankeyData, SankeyDropOff, SankeyHoverCardData, SankeyLink, SankeyNode, SankeyStageMetrics } from './sankey-chart.js';
10
+ export { EmptyChartState, EmptyChartStateProps } from './empty-chart-state.js';
10
11
  import 'react';
11
12
  import 'recharts';
@@ -7,4 +7,5 @@ export * from "./volume-analysis-chart.js";
7
7
  export * from "./top-line-metrics.js";
8
8
  export * from "./pipeline-overview.js";
9
9
  export * from "./sankey-chart.js";
10
+ export * from "./empty-chart-state.js";
10
11
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/charts/index.ts"],"sourcesContent":["/**\n * @handled-ai/design-system/charts\n * Chart components requiring recharts and @nivo/sankey\n */\nexport * from \"./chart\"\nexport * from \"./chart-tooltip\"\nexport * from \"./bar-chart-component\"\nexport * from \"./donut-chart\"\nexport * from \"./trend-area-chart\"\nexport * from \"./volume-analysis-chart\"\nexport * from \"./top-line-metrics\"\nexport * from \"./pipeline-overview\"\nexport * from \"./sankey-chart\"\n"],"mappings":"AAIA,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;","names":[]}
1
+ {"version":3,"sources":["../../src/charts/index.ts"],"sourcesContent":["/**\n * @handled-ai/design-system/charts\n * Chart components requiring recharts and @nivo/sankey\n */\nexport * from \"./chart\"\nexport * from \"./chart-tooltip\"\nexport * from \"./bar-chart-component\"\nexport * from \"./donut-chart\"\nexport * from \"./trend-area-chart\"\nexport * from \"./volume-analysis-chart\"\nexport * from \"./top-line-metrics\"\nexport * from \"./pipeline-overview\"\nexport * from \"./sankey-chart\"\nexport * from \"./empty-chart-state\"\n"],"mappings":"AAIA,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;","names":[]}
@@ -25,6 +25,7 @@ interface PipelineFilterBreakdown {
25
25
  }
26
26
  interface PipelineOverviewProps {
27
27
  title?: string;
28
+ variant?: "sankey" | "compact";
28
29
  stages: PipelineStage[];
29
30
  stageMetrics: Record<string, PipelineStageMetrics>;
30
31
  stageTimings: (PipelineStageTiming | null)[];
@@ -71,6 +72,6 @@ interface PipelineOverviewProps {
71
72
  onViewInWorkQueue?: (stageId: string) => void;
72
73
  className?: string;
73
74
  }
74
- declare function PipelineOverview({ title, stages, stageMetrics, stageTimings, filterOptions, onFilterChange, filterBreakdowns, countingModes, countingModeTooltip, flowNodes, dropOffDistribution, flowLinks, totalReceived, nodeAmounts, unitLabel, terminalUnitLabel, terminalNodeIds, sankeyMargin, sankeyLabelPadding, alwaysShowConversionBadges, dropOffNodeColor, onViewInWorkQueue: _onViewInWorkQueue, className, }: PipelineOverviewProps): React.JSX.Element;
75
+ declare function PipelineOverview({ title, variant, stages, stageMetrics, stageTimings, filterOptions, onFilterChange, filterBreakdowns, countingModes, countingModeTooltip, flowNodes, dropOffDistribution, flowLinks, totalReceived, nodeAmounts, unitLabel, terminalUnitLabel, terminalNodeIds, sankeyMargin, sankeyLabelPadding, alwaysShowConversionBadges, dropOffNodeColor, onViewInWorkQueue: _onViewInWorkQueue, className, }: PipelineOverviewProps): React.JSX.Element;
75
76
 
76
77
  export { type PipelineFilterBreakdown, PipelineOverview, type PipelineOverviewProps, type PipelineStage, type PipelineStageMetrics, type PipelineStageTiming };
@@ -89,6 +89,7 @@ function StageHoverCard({
89
89
  }
90
90
  function PipelineOverview({
91
91
  title = "Pipeline Overview",
92
+ variant = "sankey",
92
93
  stages,
93
94
  stageMetrics,
94
95
  stageTimings,
@@ -143,6 +144,10 @@ function PipelineOverview({
143
144
  [sankeyMargin]
144
145
  );
145
146
  const effectiveLabelPadding = sankeyLabelPadding != null ? sankeyLabelPadding : 16;
147
+ const compactMaxCount = React.useMemo(
148
+ () => Math.max(...stages.map((stage) => stage.count), 1),
149
+ [stages]
150
+ );
146
151
  const sankeyData = React.useMemo(() => {
147
152
  var _a, _b, _c, _d, _e, _f;
148
153
  const breakdown = (_d = (_a = filterBreakdowns == null ? void 0 : filterBreakdowns[selectedFilter]) == null ? void 0 : _a.received) != null ? _d : { [(_c = (_b = stages[0]) == null ? void 0 : _b.label) != null ? _c : "Received"]: totalReceived };
@@ -302,9 +307,35 @@ function PipelineOverview({
302
307
  )
303
308
  ] }) }, stage.id);
304
309
  }) }),
305
- /* @__PURE__ */ jsx(
310
+ variant === "compact" ? /* @__PURE__ */ jsx("div", { "data-slot": "pipeline-overview-compact", className: "mt-4 space-y-2", children: stages.map((stage, index) => {
311
+ var _a;
312
+ const width = `${Math.max(stage.count / compactMaxCount * 100, 4)}%`;
313
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-border bg-background p-3", children: [
314
+ /* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center justify-between gap-3", children: [
315
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
316
+ /* @__PURE__ */ jsx("div", { className: "truncate text-sm font-medium text-foreground", children: stage.label }),
317
+ /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground", children: stage.trend })
318
+ ] }),
319
+ /* @__PURE__ */ jsx("div", { className: "text-right text-lg font-bold text-foreground", children: stage.count.toLocaleString() })
320
+ ] }),
321
+ /* @__PURE__ */ jsx("div", { className: "h-2 overflow-hidden rounded-full bg-muted", children: /* @__PURE__ */ jsx(
322
+ "div",
323
+ {
324
+ "data-slot": "pipeline-overview-compact-bar",
325
+ className: "h-full rounded-full bg-emerald-600",
326
+ style: { width }
327
+ }
328
+ ) }),
329
+ index < stages.length - 1 && stage.nextConversion ? /* @__PURE__ */ jsxs("div", { className: "mt-2 text-xs text-muted-foreground", children: [
330
+ stage.nextConversion,
331
+ " to ",
332
+ (_a = stages[index + 1]) == null ? void 0 : _a.label
333
+ ] }) : null
334
+ ] }, stage.id);
335
+ }) }) : /* @__PURE__ */ jsx(
306
336
  "div",
307
337
  {
338
+ "data-slot": "pipeline-overview-sankey",
308
339
  className: "relative mt-4 w-full",
309
340
  style: { height: 400, minWidth: 0 },
310
341
  children: /* @__PURE__ */ jsx(
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/charts/pipeline-overview.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { TrendingUp, Info, ArrowRight } from \"lucide-react\"\nimport { ResponsiveSankey } from \"@nivo/sankey\"\n\nimport { cn } from \"../lib/utils\"\nimport {\n Tooltip,\n TooltipContent,\n TooltipProvider,\n TooltipTrigger,\n} from \"../components/tooltip\"\n\nexport interface PipelineStage {\n id: string\n label: string\n count: number\n trend: string\n nextConversion: string | null\n}\n\nexport interface PipelineStageMetrics {\n medianTime: string\n avgTime: string\n dropOffs: { reason: string; count: number; pct: string }[]\n}\n\nexport interface PipelineStageTiming {\n median: string\n avg: string\n}\n\nexport interface PipelineFilterBreakdown {\n [stageId: string]: Record<string, number>\n}\n\nconst SEGMENT_PALETTE = [\n \"#0F4C3A\",\n \"#15803d\",\n \"#0ea5e9\",\n \"#8b5cf6\",\n \"#f59e0b\",\n \"#ef4444\",\n]\n\nconst DROP_OFF_NODES = [\n { id: \"Lost/Other\", nodeColor: \"#CBD5E1\" },\n { id: \"Coverage\", nodeColor: \"#F59E0B\" },\n { id: \"Unqualified\", nodeColor: \"#F59E0B\" },\n { id: \"No Contact\", nodeColor: \"#F59E0B\" },\n { id: \"Intake Drop\", nodeColor: \"#F59E0B\" },\n { id: \"No Show/Cancel\", nodeColor: \"#F59E0B\" },\n]\n\nfunction StageHoverCard({\n title,\n count,\n metrics,\n}: {\n title: string\n count: number | string\n metrics?: PipelineStageMetrics\n}) {\n return (\n <div className=\"w-[260px] overflow-hidden rounded-lg border border-border bg-card font-sans text-left shadow-xl\">\n <div className=\"border-b border-border p-3\">\n <div className=\"mb-0.5 text-xs font-medium text-muted-foreground\">{title}</div>\n <div className=\"text-2xl font-bold text-foreground\">{count}</div>\n </div>\n\n {metrics && (\n <div className=\"grid grid-cols-2 gap-2 border-b border-border bg-muted/30 px-3 py-2\">\n <div>\n <div className=\"text-[9px] font-semibold uppercase tracking-wider text-muted-foreground/70\">\n Median\n </div>\n <div className=\"text-xs font-bold text-foreground\">\n {metrics.medianTime}\n </div>\n </div>\n <div className=\"border-l border-border pl-2\">\n <div className=\"text-[9px] font-semibold uppercase tracking-wider text-muted-foreground/70\">\n Average\n </div>\n <div className=\"text-xs font-bold text-foreground\">\n {metrics.avgTime}\n </div>\n </div>\n </div>\n )}\n\n {metrics?.dropOffs && metrics.dropOffs.length > 0 && (\n <div className=\"p-2\">\n <div className=\"mb-1.5 px-1 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground\">\n Drop-off Reasons\n </div>\n <div className=\"space-y-0.5\">\n {metrics.dropOffs.map((drop, i) => (\n <div\n key={i}\n className=\"group flex cursor-pointer items-center justify-between rounded p-1.5 text-xs transition-colors hover:bg-muted\"\n >\n <span className=\"truncate pr-2 text-muted-foreground group-hover:text-foreground\">\n {drop.reason}\n </span>\n <div className=\"flex items-center gap-1.5\">\n <span className=\"text-[10px] text-muted-foreground/70\">{drop.pct}</span>\n <span className=\"inline-flex h-4 min-w-[20px] items-center justify-center rounded border border-border bg-muted px-1 text-[9px] font-semibold text-muted-foreground group-hover:bg-muted group-hover:text-foreground\">\n {drop.count}\n </span>\n </div>\n </div>\n ))}\n </div>\n </div>\n )}\n\n <div className=\"flex cursor-pointer items-center justify-between border-t border-border bg-muted px-3 py-2 text-xs font-medium text-blue-600 transition-colors hover:bg-muted hover:text-blue-700\">\n View in Work Queue <ArrowRight className=\"h-3 w-3\" />\n </div>\n </div>\n )\n}\n\nexport interface PipelineOverviewProps {\n title?: string\n stages: PipelineStage[]\n stageMetrics: Record<string, PipelineStageMetrics>\n stageTimings: (PipelineStageTiming | null)[]\n filterOptions?: string[]\n onFilterChange?: (filterOption: string) => void\n filterBreakdowns?: Record<string, PipelineFilterBreakdown>\n countingModes?: string[]\n countingModeTooltip?: string\n /** Main pipeline flow nodes (after the first stage) */\n flowNodes?: { id: string; nodeColor: string }[]\n /** Drop-off distribution from initial stage: { \"Lost/Other\": 56, \"Coverage\": 40, ... } */\n dropOffDistribution?: Record<string, number>\n /** Flow links after the first stage (middle of pipeline onward) */\n flowLinks?: { source: string; target: string; value: number }[]\n totalReceived?: number\n /** Dollar amounts to display on terminal node labels: { \"Retained\": \"$18.2M\" } */\n nodeAmounts?: Record<string, string>\n /** Unit noun for standard node/link tooltips (e.g. \"signals\", \"accounts\"). */\n unitLabel?: string\n /** Unit noun for terminal/drop-off node tooltips (e.g. \"opportunities\"). Defaults to unitLabel. */\n terminalUnitLabel?: string\n /** Node IDs that should use terminalUnitLabel. Defaults to keys of dropOffDistribution. */\n terminalNodeIds?: string[]\n /** Sankey chart margins, merged with defaults { top: 20, right: 120, bottom: 20, left: 140 }. */\n sankeyMargin?: { top?: number; right?: number; bottom?: number; left?: number }\n /** Gap between Sankey node bar and its outside label. */\n sankeyLabelPadding?: number\n /** When true, conversion badges use `flex`; when false, `hidden xl:flex`. */\n alwaysShowConversionBadges?: boolean\n /** Color for dynamically generated drop-off nodes. */\n dropOffNodeColor?: string\n onViewInWorkQueue?: (stageId: string) => void\n className?: string\n}\n\nexport function PipelineOverview({\n title = \"Pipeline Overview\",\n stages,\n stageMetrics,\n stageTimings,\n filterOptions = [\"Facility\", \"Source\", \"Lead Source\", \"Payer\", \"Channel\"],\n onFilterChange,\n filterBreakdowns,\n countingModes = [\"Unique Patients\", \"All Referrals\"],\n countingModeTooltip = \"Patients may be referred through multiple channels. 'All Referrals' shows total volume; 'Unique Patients' deduplicates to show distinct patient counts.\",\n flowNodes = [\n { id: \"Contacted\", nodeColor: \"#2A8F7A\" },\n { id: \"Intake Sent\", nodeColor: \"#3DB4A0\" },\n { id: \"Intake Done\", nodeColor: \"#4CC9B0\" },\n { id: \"Scheduled\", nodeColor: \"#5FCFBC\" },\n { id: \"Completed\", nodeColor: \"#79E2C9\" },\n ],\n dropOffDistribution = {\n \"Lost/Other\": 56,\n Coverage: 40,\n Unqualified: 30,\n },\n flowLinks = [\n { source: \"Contacted\", target: \"Intake Sent\", value: 660 },\n { source: \"Contacted\", target: \"No Contact\", value: 60 },\n { source: \"Intake Sent\", target: \"Intake Done\", value: 612 },\n { source: \"Intake Sent\", target: \"Intake Drop\", value: 48 },\n { source: \"Intake Done\", target: \"Scheduled\", value: 612 },\n { source: \"Scheduled\", target: \"Completed\", value: 520 },\n { source: \"Scheduled\", target: \"No Show/Cancel\", value: 92 },\n ],\n totalReceived = 847,\n nodeAmounts,\n unitLabel,\n terminalUnitLabel,\n terminalNodeIds,\n sankeyMargin,\n sankeyLabelPadding,\n alwaysShowConversionBadges = false,\n dropOffNodeColor,\n onViewInWorkQueue: _onViewInWorkQueue,\n className,\n}: PipelineOverviewProps) {\n const [selectedFilter, setSelectedFilter] = React.useState(filterOptions[0])\n const [countingMode, setCountingMode] = React.useState(countingModes[0])\n\n const effectiveUnitLabel = unitLabel ?? \"items\"\n const effectiveTerminalUnitLabel = terminalUnitLabel ?? effectiveUnitLabel\n const effectiveTerminalNodeIds = React.useMemo(\n () => new Set(terminalNodeIds ?? Object.keys(dropOffDistribution)),\n [terminalNodeIds, dropOffDistribution],\n )\n const effectiveMargin = React.useMemo(\n () => ({ top: 20, right: 120, bottom: 20, left: 140, ...sankeyMargin }),\n [sankeyMargin],\n )\n const effectiveLabelPadding = sankeyLabelPadding ?? 16\n\n const sankeyData = React.useMemo(() => {\n const breakdown =\n filterBreakdowns?.[selectedFilter]?.received ??\n ({ [stages[0]?.label ?? \"Received\"]: totalReceived } as Record<\n string,\n number\n >)\n\n const segments = Object.entries(breakdown).map(([name, value], index) => ({\n id: name,\n nodeColor: SEGMENT_PALETTE[index % SEGMENT_PALETTE.length],\n value,\n }))\n\n const dropOffKeys = Object.keys(dropOffDistribution)\n const dropOffNodes = dropOffKeys.length > 0\n ? dropOffKeys.map((reason) => ({\n id: reason,\n nodeColor: dropOffNodeColor ?? \"#F59E0B\",\n }))\n : DROP_OFF_NODES\n\n const nodes = [\n ...segments,\n ...flowNodes,\n ...dropOffNodes,\n ]\n\n const nodeIds = new Set(nodes.map((n) => n.id))\n for (const link of flowLinks) {\n for (const endpoint of [link.source, link.target]) {\n if (!nodeIds.has(endpoint)) {\n nodes.push({ id: endpoint, nodeColor: dropOffNodeColor ?? \"#F59E0B\" })\n nodeIds.add(endpoint)\n }\n }\n }\n\n const links: { source: string; target: string; value: number }[] = []\n\n const firstFlowNode = flowNodes[0]?.id ?? \"Contacted\"\n\n segments.forEach((segment) => {\n if (segment.value > 0) {\n links.push({\n source: segment.id,\n target: firstFlowNode,\n value: segment.value,\n })\n }\n })\n\n for (const [reason, count] of Object.entries(dropOffDistribution)) {\n if (count > 0) {\n links.push({ source: firstFlowNode, target: reason, value: count })\n }\n }\n\n flowLinks.forEach((link) => links.push({ ...link }))\n\n return { nodes, links }\n }, [\n selectedFilter,\n filterBreakdowns,\n stages,\n totalReceived,\n flowNodes,\n dropOffDistribution,\n dropOffNodeColor,\n flowLinks,\n ])\n\n return (\n <div\n className={cn(\n \"rounded-xl border border-border bg-card p-5 shadow-sm\",\n className,\n )}\n >\n {/* Header */}\n <div className=\"mb-6 flex flex-col gap-4\">\n <div className=\"flex flex-col gap-4 md:flex-row md:items-center md:justify-between\">\n <div className=\"flex items-center gap-4\">\n <h3 className=\"text-lg font-semibold text-foreground\">{title}</h3>\n\n {/* Counting Mode Toggle */}\n {countingModes.length > 1 && (\n <div className=\"flex items-center gap-2 rounded-lg border border-border bg-muted p-1\">\n {countingModes.map((mode) => (\n <button\n key={mode}\n onClick={() => setCountingMode(mode)}\n className={cn(\n \"rounded-md px-3 py-1 text-xs font-medium transition-all\",\n countingMode === mode\n ? \"border border-border bg-background text-foreground shadow-sm\"\n : \"text-muted-foreground hover:text-foreground\",\n )}\n >\n {mode}\n </button>\n ))}\n <TooltipProvider>\n <Tooltip>\n <TooltipTrigger asChild>\n <Info className=\"ml-1 h-3.5 w-3.5 cursor-help text-muted-foreground/70\" />\n </TooltipTrigger>\n <TooltipContent className=\"max-w-[250px] text-xs\">\n {countingModeTooltip}\n </TooltipContent>\n </Tooltip>\n </TooltipProvider>\n </div>\n )}\n </div>\n\n {/* Filter Tabs */}\n {filterOptions.length > 1 && (\n <div className=\"flex items-center gap-1 self-start rounded-lg bg-muted p-1 md:self-auto\">\n {filterOptions.map((option) => (\n <button\n key={option}\n onClick={() => { setSelectedFilter(option); onFilterChange?.(option); }}\n className={cn(\n \"h-7 rounded-md border-none bg-transparent px-3 text-xs font-medium shadow-none transition-all hover:bg-background\",\n selectedFilter === option &&\n \"bg-background text-foreground shadow-sm\",\n )}\n >\n {option}\n </button>\n ))}\n </div>\n )}\n </div>\n </div>\n\n {/* Stage Metrics Row */}\n <div className=\"mb-2 grid gap-4\" style={{ gridTemplateColumns: `repeat(${stages.length}, minmax(0, 1fr))` }}>\n {stages.map((stage, index) => {\n const details = stageMetrics[stage.id]\n const timing = stageTimings[index] ?? null\n\n return (\n <TooltipProvider key={stage.id} delayDuration={100}>\n <Tooltip>\n <TooltipTrigger asChild>\n <div className=\"group relative flex cursor-pointer flex-col items-center rounded-lg p-2 text-center transition-colors hover:bg-muted\">\n <div\n className=\"mb-1 w-full truncate text-xs font-medium text-muted-foreground\"\n title={stage.label}\n >\n {stage.label}\n </div>\n <div className=\"mb-1 text-2xl font-bold text-foreground\">\n {stage.count.toLocaleString()}\n </div>\n <div className=\"mb-1 flex items-center justify-center gap-1 text-xs font-medium text-emerald-600\">\n {stage.trend} <TrendingUp className=\"h-3 w-3\" />\n </div>\n\n {/* Conversion badge + timing between stages */}\n {index < stages.length - 1 && stage.nextConversion && (\n <div className={cn(\n alwaysShowConversionBadges ? \"flex\" : \"hidden xl:flex\",\n \"absolute -right-2 top-1/2 z-10 -translate-y-1/2 translate-x-1/2 flex-col items-center\",\n )}>\n <span className=\"z-10 whitespace-nowrap rounded-full border border-border bg-muted px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground shadow-sm\">\n {stage.nextConversion}\n </span>\n {timing && (\n <div className=\"mt-1 flex flex-col items-center\">\n <div className=\"h-2 w-px bg-border\" />\n <div className=\"whitespace-nowrap rounded bg-background/80 px-1 py-0.5 text-[9px] font-medium leading-3 text-muted-foreground/70 backdrop-blur-[2px]\">\n <span className=\"mr-1 font-semibold text-muted-foreground\">\n Med: {timing.median}\n </span>\n <span className=\"text-muted-foreground/70\">\n Avg: {timing.avg}\n </span>\n </div>\n </div>\n )}\n </div>\n )}\n\n {/* Connector line down to Sankey */}\n <div className=\"mx-auto mb-1 mt-2 h-4 w-px bg-border\" />\n </div>\n </TooltipTrigger>\n <TooltipContent\n className=\"border-none bg-transparent p-0 shadow-none\"\n sideOffset={8}\n >\n <StageHoverCard\n title={stage.label}\n count={stage.count}\n metrics={details}\n />\n </TooltipContent>\n </Tooltip>\n </TooltipProvider>\n )\n })}\n </div>\n\n {/* Sankey Chart */}\n <div\n className=\"relative mt-4 w-full\"\n style={{ height: 400, minWidth: 0 }}\n >\n <ResponsiveSankey\n data={sankeyData}\n margin={effectiveMargin}\n align=\"justify\"\n colors={(node: { nodeColor?: string }) => node.nodeColor || \"#94a3b8\"}\n nodeOpacity={1}\n nodeHoverOthersOpacity={0.35}\n nodeThickness={18}\n nodeSpacing={16}\n nodeBorderWidth={0}\n nodeBorderRadius={3}\n linkOpacity={0.5}\n linkHoverOthersOpacity={0.1}\n linkContract={3}\n enableLinkGradient\n labelPosition=\"outside\"\n labelOrientation=\"horizontal\"\n labelPadding={effectiveLabelPadding}\n label={(node: { id: string }) => nodeAmounts?.[node.id] ? `${node.id} (${nodeAmounts[node.id]})` : node.id}\n labelTextColor={{ from: \"color\", modifiers: [[\"darker\", 1]] }}\n nodeTooltip={({ node }: { node: { id: string; value: number } }) => {\n const unit = effectiveTerminalNodeIds.has(node.id) ? effectiveTerminalUnitLabel : effectiveUnitLabel\n return (\n <div className=\"rounded-md border border-border bg-card p-2 text-xs shadow-lg\">\n <span className=\"mb-1 block font-bold\">{node.id}{nodeAmounts?.[node.id] ? ` — ${nodeAmounts[node.id]}` : \"\"}</span>\n <span>{node.value} {unit}</span>\n </div>\n )\n }}\n linkTooltip={({ link }: { link: { source: { id: string }; target: { id: string }; value: number } }) => {\n const unit = effectiveTerminalNodeIds.has(link.target.id) ? effectiveTerminalUnitLabel : effectiveUnitLabel\n return (\n <div className=\"rounded-md border border-border bg-card p-2 text-xs shadow-lg\">\n <span className=\"mb-1 block font-bold\">\n {link.source.id} → {link.target.id}\n </span>\n <span>{link.value} {unit}</span>\n </div>\n )\n }}\n />\n </div>\n </div>\n )\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/charts/pipeline-overview.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { TrendingUp, Info, ArrowRight } from \"lucide-react\"\nimport { ResponsiveSankey } from \"@nivo/sankey\"\n\nimport { cn } from \"../lib/utils\"\nimport {\n Tooltip,\n TooltipContent,\n TooltipProvider,\n TooltipTrigger,\n} from \"../components/tooltip\"\n\nexport interface PipelineStage {\n id: string\n label: string\n count: number\n trend: string\n nextConversion: string | null\n}\n\nexport interface PipelineStageMetrics {\n medianTime: string\n avgTime: string\n dropOffs: { reason: string; count: number; pct: string }[]\n}\n\nexport interface PipelineStageTiming {\n median: string\n avg: string\n}\n\nexport interface PipelineFilterBreakdown {\n [stageId: string]: Record<string, number>\n}\n\nconst SEGMENT_PALETTE = [\n \"#0F4C3A\",\n \"#15803d\",\n \"#0ea5e9\",\n \"#8b5cf6\",\n \"#f59e0b\",\n \"#ef4444\",\n]\n\nconst DROP_OFF_NODES = [\n { id: \"Lost/Other\", nodeColor: \"#CBD5E1\" },\n { id: \"Coverage\", nodeColor: \"#F59E0B\" },\n { id: \"Unqualified\", nodeColor: \"#F59E0B\" },\n { id: \"No Contact\", nodeColor: \"#F59E0B\" },\n { id: \"Intake Drop\", nodeColor: \"#F59E0B\" },\n { id: \"No Show/Cancel\", nodeColor: \"#F59E0B\" },\n]\n\nfunction StageHoverCard({\n title,\n count,\n metrics,\n}: {\n title: string\n count: number | string\n metrics?: PipelineStageMetrics\n}) {\n return (\n <div className=\"w-[260px] overflow-hidden rounded-lg border border-border bg-card font-sans text-left shadow-xl\">\n <div className=\"border-b border-border p-3\">\n <div className=\"mb-0.5 text-xs font-medium text-muted-foreground\">{title}</div>\n <div className=\"text-2xl font-bold text-foreground\">{count}</div>\n </div>\n\n {metrics && (\n <div className=\"grid grid-cols-2 gap-2 border-b border-border bg-muted/30 px-3 py-2\">\n <div>\n <div className=\"text-[9px] font-semibold uppercase tracking-wider text-muted-foreground/70\">\n Median\n </div>\n <div className=\"text-xs font-bold text-foreground\">\n {metrics.medianTime}\n </div>\n </div>\n <div className=\"border-l border-border pl-2\">\n <div className=\"text-[9px] font-semibold uppercase tracking-wider text-muted-foreground/70\">\n Average\n </div>\n <div className=\"text-xs font-bold text-foreground\">\n {metrics.avgTime}\n </div>\n </div>\n </div>\n )}\n\n {metrics?.dropOffs && metrics.dropOffs.length > 0 && (\n <div className=\"p-2\">\n <div className=\"mb-1.5 px-1 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground\">\n Drop-off Reasons\n </div>\n <div className=\"space-y-0.5\">\n {metrics.dropOffs.map((drop, i) => (\n <div\n key={i}\n className=\"group flex cursor-pointer items-center justify-between rounded p-1.5 text-xs transition-colors hover:bg-muted\"\n >\n <span className=\"truncate pr-2 text-muted-foreground group-hover:text-foreground\">\n {drop.reason}\n </span>\n <div className=\"flex items-center gap-1.5\">\n <span className=\"text-[10px] text-muted-foreground/70\">{drop.pct}</span>\n <span className=\"inline-flex h-4 min-w-[20px] items-center justify-center rounded border border-border bg-muted px-1 text-[9px] font-semibold text-muted-foreground group-hover:bg-muted group-hover:text-foreground\">\n {drop.count}\n </span>\n </div>\n </div>\n ))}\n </div>\n </div>\n )}\n\n <div className=\"flex cursor-pointer items-center justify-between border-t border-border bg-muted px-3 py-2 text-xs font-medium text-blue-600 transition-colors hover:bg-muted hover:text-blue-700\">\n View in Work Queue <ArrowRight className=\"h-3 w-3\" />\n </div>\n </div>\n )\n}\n\nexport interface PipelineOverviewProps {\n title?: string\n variant?: \"sankey\" | \"compact\"\n stages: PipelineStage[]\n stageMetrics: Record<string, PipelineStageMetrics>\n stageTimings: (PipelineStageTiming | null)[]\n filterOptions?: string[]\n onFilterChange?: (filterOption: string) => void\n filterBreakdowns?: Record<string, PipelineFilterBreakdown>\n countingModes?: string[]\n countingModeTooltip?: string\n /** Main pipeline flow nodes (after the first stage) */\n flowNodes?: { id: string; nodeColor: string }[]\n /** Drop-off distribution from initial stage: { \"Lost/Other\": 56, \"Coverage\": 40, ... } */\n dropOffDistribution?: Record<string, number>\n /** Flow links after the first stage (middle of pipeline onward) */\n flowLinks?: { source: string; target: string; value: number }[]\n totalReceived?: number\n /** Dollar amounts to display on terminal node labels: { \"Retained\": \"$18.2M\" } */\n nodeAmounts?: Record<string, string>\n /** Unit noun for standard node/link tooltips (e.g. \"signals\", \"accounts\"). */\n unitLabel?: string\n /** Unit noun for terminal/drop-off node tooltips (e.g. \"opportunities\"). Defaults to unitLabel. */\n terminalUnitLabel?: string\n /** Node IDs that should use terminalUnitLabel. Defaults to keys of dropOffDistribution. */\n terminalNodeIds?: string[]\n /** Sankey chart margins, merged with defaults { top: 20, right: 120, bottom: 20, left: 140 }. */\n sankeyMargin?: { top?: number; right?: number; bottom?: number; left?: number }\n /** Gap between Sankey node bar and its outside label. */\n sankeyLabelPadding?: number\n /** When true, conversion badges use `flex`; when false, `hidden xl:flex`. */\n alwaysShowConversionBadges?: boolean\n /** Color for dynamically generated drop-off nodes. */\n dropOffNodeColor?: string\n onViewInWorkQueue?: (stageId: string) => void\n className?: string\n}\n\nexport function PipelineOverview({\n title = \"Pipeline Overview\",\n variant = \"sankey\",\n stages,\n stageMetrics,\n stageTimings,\n filterOptions = [\"Facility\", \"Source\", \"Lead Source\", \"Payer\", \"Channel\"],\n onFilterChange,\n filterBreakdowns,\n countingModes = [\"Unique Patients\", \"All Referrals\"],\n countingModeTooltip = \"Patients may be referred through multiple channels. 'All Referrals' shows total volume; 'Unique Patients' deduplicates to show distinct patient counts.\",\n flowNodes = [\n { id: \"Contacted\", nodeColor: \"#2A8F7A\" },\n { id: \"Intake Sent\", nodeColor: \"#3DB4A0\" },\n { id: \"Intake Done\", nodeColor: \"#4CC9B0\" },\n { id: \"Scheduled\", nodeColor: \"#5FCFBC\" },\n { id: \"Completed\", nodeColor: \"#79E2C9\" },\n ],\n dropOffDistribution = {\n \"Lost/Other\": 56,\n Coverage: 40,\n Unqualified: 30,\n },\n flowLinks = [\n { source: \"Contacted\", target: \"Intake Sent\", value: 660 },\n { source: \"Contacted\", target: \"No Contact\", value: 60 },\n { source: \"Intake Sent\", target: \"Intake Done\", value: 612 },\n { source: \"Intake Sent\", target: \"Intake Drop\", value: 48 },\n { source: \"Intake Done\", target: \"Scheduled\", value: 612 },\n { source: \"Scheduled\", target: \"Completed\", value: 520 },\n { source: \"Scheduled\", target: \"No Show/Cancel\", value: 92 },\n ],\n totalReceived = 847,\n nodeAmounts,\n unitLabel,\n terminalUnitLabel,\n terminalNodeIds,\n sankeyMargin,\n sankeyLabelPadding,\n alwaysShowConversionBadges = false,\n dropOffNodeColor,\n onViewInWorkQueue: _onViewInWorkQueue,\n className,\n}: PipelineOverviewProps) {\n const [selectedFilter, setSelectedFilter] = React.useState(filterOptions[0])\n const [countingMode, setCountingMode] = React.useState(countingModes[0])\n\n const effectiveUnitLabel = unitLabel ?? \"items\"\n const effectiveTerminalUnitLabel = terminalUnitLabel ?? effectiveUnitLabel\n const effectiveTerminalNodeIds = React.useMemo(\n () => new Set(terminalNodeIds ?? Object.keys(dropOffDistribution)),\n [terminalNodeIds, dropOffDistribution],\n )\n const effectiveMargin = React.useMemo(\n () => ({ top: 20, right: 120, bottom: 20, left: 140, ...sankeyMargin }),\n [sankeyMargin],\n )\n const effectiveLabelPadding = sankeyLabelPadding ?? 16\n const compactMaxCount = React.useMemo(\n () => Math.max(...stages.map((stage) => stage.count), 1),\n [stages],\n )\n\n const sankeyData = React.useMemo(() => {\n const breakdown =\n filterBreakdowns?.[selectedFilter]?.received ??\n ({ [stages[0]?.label ?? \"Received\"]: totalReceived } as Record<\n string,\n number\n >)\n\n const segments = Object.entries(breakdown).map(([name, value], index) => ({\n id: name,\n nodeColor: SEGMENT_PALETTE[index % SEGMENT_PALETTE.length],\n value,\n }))\n\n const dropOffKeys = Object.keys(dropOffDistribution)\n const dropOffNodes = dropOffKeys.length > 0\n ? dropOffKeys.map((reason) => ({\n id: reason,\n nodeColor: dropOffNodeColor ?? \"#F59E0B\",\n }))\n : DROP_OFF_NODES\n\n const nodes = [\n ...segments,\n ...flowNodes,\n ...dropOffNodes,\n ]\n\n const nodeIds = new Set(nodes.map((n) => n.id))\n for (const link of flowLinks) {\n for (const endpoint of [link.source, link.target]) {\n if (!nodeIds.has(endpoint)) {\n nodes.push({ id: endpoint, nodeColor: dropOffNodeColor ?? \"#F59E0B\" })\n nodeIds.add(endpoint)\n }\n }\n }\n\n const links: { source: string; target: string; value: number }[] = []\n\n const firstFlowNode = flowNodes[0]?.id ?? \"Contacted\"\n\n segments.forEach((segment) => {\n if (segment.value > 0) {\n links.push({\n source: segment.id,\n target: firstFlowNode,\n value: segment.value,\n })\n }\n })\n\n for (const [reason, count] of Object.entries(dropOffDistribution)) {\n if (count > 0) {\n links.push({ source: firstFlowNode, target: reason, value: count })\n }\n }\n\n flowLinks.forEach((link) => links.push({ ...link }))\n\n return { nodes, links }\n }, [\n selectedFilter,\n filterBreakdowns,\n stages,\n totalReceived,\n flowNodes,\n dropOffDistribution,\n dropOffNodeColor,\n flowLinks,\n ])\n\n return (\n <div\n className={cn(\n \"rounded-xl border border-border bg-card p-5 shadow-sm\",\n className,\n )}\n >\n {/* Header */}\n <div className=\"mb-6 flex flex-col gap-4\">\n <div className=\"flex flex-col gap-4 md:flex-row md:items-center md:justify-between\">\n <div className=\"flex items-center gap-4\">\n <h3 className=\"text-lg font-semibold text-foreground\">{title}</h3>\n\n {/* Counting Mode Toggle */}\n {countingModes.length > 1 && (\n <div className=\"flex items-center gap-2 rounded-lg border border-border bg-muted p-1\">\n {countingModes.map((mode) => (\n <button\n key={mode}\n onClick={() => setCountingMode(mode)}\n className={cn(\n \"rounded-md px-3 py-1 text-xs font-medium transition-all\",\n countingMode === mode\n ? \"border border-border bg-background text-foreground shadow-sm\"\n : \"text-muted-foreground hover:text-foreground\",\n )}\n >\n {mode}\n </button>\n ))}\n <TooltipProvider>\n <Tooltip>\n <TooltipTrigger asChild>\n <Info className=\"ml-1 h-3.5 w-3.5 cursor-help text-muted-foreground/70\" />\n </TooltipTrigger>\n <TooltipContent className=\"max-w-[250px] text-xs\">\n {countingModeTooltip}\n </TooltipContent>\n </Tooltip>\n </TooltipProvider>\n </div>\n )}\n </div>\n\n {/* Filter Tabs */}\n {filterOptions.length > 1 && (\n <div className=\"flex items-center gap-1 self-start rounded-lg bg-muted p-1 md:self-auto\">\n {filterOptions.map((option) => (\n <button\n key={option}\n onClick={() => { setSelectedFilter(option); onFilterChange?.(option); }}\n className={cn(\n \"h-7 rounded-md border-none bg-transparent px-3 text-xs font-medium shadow-none transition-all hover:bg-background\",\n selectedFilter === option &&\n \"bg-background text-foreground shadow-sm\",\n )}\n >\n {option}\n </button>\n ))}\n </div>\n )}\n </div>\n </div>\n\n {/* Stage Metrics Row */}\n <div className=\"mb-2 grid gap-4\" style={{ gridTemplateColumns: `repeat(${stages.length}, minmax(0, 1fr))` }}>\n {stages.map((stage, index) => {\n const details = stageMetrics[stage.id]\n const timing = stageTimings[index] ?? null\n\n return (\n <TooltipProvider key={stage.id} delayDuration={100}>\n <Tooltip>\n <TooltipTrigger asChild>\n <div className=\"group relative flex cursor-pointer flex-col items-center rounded-lg p-2 text-center transition-colors hover:bg-muted\">\n <div\n className=\"mb-1 w-full truncate text-xs font-medium text-muted-foreground\"\n title={stage.label}\n >\n {stage.label}\n </div>\n <div className=\"mb-1 text-2xl font-bold text-foreground\">\n {stage.count.toLocaleString()}\n </div>\n <div className=\"mb-1 flex items-center justify-center gap-1 text-xs font-medium text-emerald-600\">\n {stage.trend} <TrendingUp className=\"h-3 w-3\" />\n </div>\n\n {/* Conversion badge + timing between stages */}\n {index < stages.length - 1 && stage.nextConversion && (\n <div className={cn(\n alwaysShowConversionBadges ? \"flex\" : \"hidden xl:flex\",\n \"absolute -right-2 top-1/2 z-10 -translate-y-1/2 translate-x-1/2 flex-col items-center\",\n )}>\n <span className=\"z-10 whitespace-nowrap rounded-full border border-border bg-muted px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground shadow-sm\">\n {stage.nextConversion}\n </span>\n {timing && (\n <div className=\"mt-1 flex flex-col items-center\">\n <div className=\"h-2 w-px bg-border\" />\n <div className=\"whitespace-nowrap rounded bg-background/80 px-1 py-0.5 text-[9px] font-medium leading-3 text-muted-foreground/70 backdrop-blur-[2px]\">\n <span className=\"mr-1 font-semibold text-muted-foreground\">\n Med: {timing.median}\n </span>\n <span className=\"text-muted-foreground/70\">\n Avg: {timing.avg}\n </span>\n </div>\n </div>\n )}\n </div>\n )}\n\n {/* Connector line down to Sankey */}\n <div className=\"mx-auto mb-1 mt-2 h-4 w-px bg-border\" />\n </div>\n </TooltipTrigger>\n <TooltipContent\n className=\"border-none bg-transparent p-0 shadow-none\"\n sideOffset={8}\n >\n <StageHoverCard\n title={stage.label}\n count={stage.count}\n metrics={details}\n />\n </TooltipContent>\n </Tooltip>\n </TooltipProvider>\n )\n })}\n </div>\n\n {variant === \"compact\" ? (\n <div data-slot=\"pipeline-overview-compact\" className=\"mt-4 space-y-2\">\n {stages.map((stage, index) => {\n const width = `${Math.max((stage.count / compactMaxCount) * 100, 4)}%`\n\n return (\n <div key={stage.id} className=\"rounded-lg border border-border bg-background p-3\">\n <div className=\"mb-2 flex items-center justify-between gap-3\">\n <div className=\"min-w-0\">\n <div className=\"truncate text-sm font-medium text-foreground\">{stage.label}</div>\n <div className=\"text-xs text-muted-foreground\">{stage.trend}</div>\n </div>\n <div className=\"text-right text-lg font-bold text-foreground\">\n {stage.count.toLocaleString()}\n </div>\n </div>\n <div className=\"h-2 overflow-hidden rounded-full bg-muted\">\n <div\n data-slot=\"pipeline-overview-compact-bar\"\n className=\"h-full rounded-full bg-emerald-600\"\n style={{ width }}\n />\n </div>\n {index < stages.length - 1 && stage.nextConversion ? (\n <div className=\"mt-2 text-xs text-muted-foreground\">\n {stage.nextConversion} to {stages[index + 1]?.label}\n </div>\n ) : null}\n </div>\n )\n })}\n </div>\n ) : (\n <div\n data-slot=\"pipeline-overview-sankey\"\n className=\"relative mt-4 w-full\"\n style={{ height: 400, minWidth: 0 }}\n >\n <ResponsiveSankey\n data={sankeyData}\n margin={effectiveMargin}\n align=\"justify\"\n colors={(node: { nodeColor?: string }) => node.nodeColor || \"#94a3b8\"}\n nodeOpacity={1}\n nodeHoverOthersOpacity={0.35}\n nodeThickness={18}\n nodeSpacing={16}\n nodeBorderWidth={0}\n nodeBorderRadius={3}\n linkOpacity={0.5}\n linkHoverOthersOpacity={0.1}\n linkContract={3}\n enableLinkGradient\n labelPosition=\"outside\"\n labelOrientation=\"horizontal\"\n labelPadding={effectiveLabelPadding}\n label={(node: { id: string }) => nodeAmounts?.[node.id] ? `${node.id} (${nodeAmounts[node.id]})` : node.id}\n labelTextColor={{ from: \"color\", modifiers: [[\"darker\", 1]] }}\n nodeTooltip={({ node }: { node: { id: string; value: number } }) => {\n const unit = effectiveTerminalNodeIds.has(node.id) ? effectiveTerminalUnitLabel : effectiveUnitLabel\n return (\n <div className=\"rounded-md border border-border bg-card p-2 text-xs shadow-lg\">\n <span className=\"mb-1 block font-bold\">{node.id}{nodeAmounts?.[node.id] ? ` — ${nodeAmounts[node.id]}` : \"\"}</span>\n <span>{node.value} {unit}</span>\n </div>\n )\n }}\n linkTooltip={({ link }: { link: { source: { id: string }; target: { id: string }; value: number } }) => {\n const unit = effectiveTerminalNodeIds.has(link.target.id) ? effectiveTerminalUnitLabel : effectiveUnitLabel\n return (\n <div className=\"rounded-md border border-border bg-card p-2 text-xs shadow-lg\">\n <span className=\"mb-1 block font-bold\">\n {link.source.id} → {link.target.id}\n </span>\n <span>{link.value} {unit}</span>\n </div>\n )\n }}\n />\n </div>\n )}\n </div>\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAkEM,SACE,KADF;AAhEN,YAAY,WAAW;AACvB,SAAS,YAAY,MAAM,kBAAkB;AAC7C,SAAS,wBAAwB;AAEjC,SAAS,UAAU;AACnB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAyBP,MAAM,kBAAkB;AAAA,EACtB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,MAAM,iBAAiB;AAAA,EACrB,EAAE,IAAI,cAAc,WAAW,UAAU;AAAA,EACzC,EAAE,IAAI,YAAY,WAAW,UAAU;AAAA,EACvC,EAAE,IAAI,eAAe,WAAW,UAAU;AAAA,EAC1C,EAAE,IAAI,cAAc,WAAW,UAAU;AAAA,EACzC,EAAE,IAAI,eAAe,WAAW,UAAU;AAAA,EAC1C,EAAE,IAAI,kBAAkB,WAAW,UAAU;AAC/C;AAEA,SAAS,eAAe;AAAA,EACtB;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,SACE,qBAAC,SAAI,WAAU,mGACb;AAAA,yBAAC,SAAI,WAAU,8BACb;AAAA,0BAAC,SAAI,WAAU,oDAAoD,iBAAM;AAAA,MACzE,oBAAC,SAAI,WAAU,sCAAsC,iBAAM;AAAA,OAC7D;AAAA,IAEC,WACC,qBAAC,SAAI,WAAU,uEACb;AAAA,2BAAC,SACC;AAAA,4BAAC,SAAI,WAAU,8EAA6E,oBAE5F;AAAA,QACA,oBAAC,SAAI,WAAU,qCACZ,kBAAQ,YACX;AAAA,SACF;AAAA,MACA,qBAAC,SAAI,WAAU,+BACb;AAAA,4BAAC,SAAI,WAAU,8EAA6E,qBAE5F;AAAA,QACA,oBAAC,SAAI,WAAU,qCACZ,kBAAQ,SACX;AAAA,SACF;AAAA,OACF;AAAA,KAGD,mCAAS,aAAY,QAAQ,SAAS,SAAS,KAC9C,qBAAC,SAAI,WAAU,OACb;AAAA,0BAAC,SAAI,WAAU,uFAAsF,8BAErG;AAAA,MACA,oBAAC,SAAI,WAAU,eACZ,kBAAQ,SAAS,IAAI,CAAC,MAAM,MAC3B;AAAA,QAAC;AAAA;AAAA,UAEC,WAAU;AAAA,UAEV;AAAA,gCAAC,UAAK,WAAU,mEACb,eAAK,QACR;AAAA,YACA,qBAAC,SAAI,WAAU,6BACb;AAAA,kCAAC,UAAK,WAAU,wCAAwC,eAAK,KAAI;AAAA,cACjE,oBAAC,UAAK,WAAU,uMACb,eAAK,OACR;AAAA,eACF;AAAA;AAAA;AAAA,QAXK;AAAA,MAYP,CACD,GACH;AAAA,OACF;AAAA,IAGF,qBAAC,SAAI,WAAU,qLAAoL;AAAA;AAAA,MAC9K,oBAAC,cAAW,WAAU,WAAU;AAAA,OACrD;AAAA,KACF;AAEJ;AAwCO,SAAS,iBAAiB;AAAA,EAC/B,QAAQ;AAAA,EACR,UAAU;AAAA,EACV;AAAA,EACA;AAAA,EACA;AAAA,EACA,gBAAgB,CAAC,YAAY,UAAU,eAAe,SAAS,SAAS;AAAA,EACxE;AAAA,EACA;AAAA,EACA,gBAAgB,CAAC,mBAAmB,eAAe;AAAA,EACnD,sBAAsB;AAAA,EACtB,YAAY;AAAA,IACV,EAAE,IAAI,aAAa,WAAW,UAAU;AAAA,IACxC,EAAE,IAAI,eAAe,WAAW,UAAU;AAAA,IAC1C,EAAE,IAAI,eAAe,WAAW,UAAU;AAAA,IAC1C,EAAE,IAAI,aAAa,WAAW,UAAU;AAAA,IACxC,EAAE,IAAI,aAAa,WAAW,UAAU;AAAA,EAC1C;AAAA,EACA,sBAAsB;AAAA,IACpB,cAAc;AAAA,IACd,UAAU;AAAA,IACV,aAAa;AAAA,EACf;AAAA,EACA,YAAY;AAAA,IACV,EAAE,QAAQ,aAAa,QAAQ,eAAe,OAAO,IAAI;AAAA,IACzD,EAAE,QAAQ,aAAa,QAAQ,cAAc,OAAO,GAAG;AAAA,IACvD,EAAE,QAAQ,eAAe,QAAQ,eAAe,OAAO,IAAI;AAAA,IAC3D,EAAE,QAAQ,eAAe,QAAQ,eAAe,OAAO,GAAG;AAAA,IAC1D,EAAE,QAAQ,eAAe,QAAQ,aAAa,OAAO,IAAI;AAAA,IACzD,EAAE,QAAQ,aAAa,QAAQ,aAAa,OAAO,IAAI;AAAA,IACvD,EAAE,QAAQ,aAAa,QAAQ,kBAAkB,OAAO,GAAG;AAAA,EAC7D;AAAA,EACA,gBAAgB;AAAA,EAChB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,6BAA6B;AAAA,EAC7B;AAAA,EACA,mBAAmB;AAAA,EACnB;AACF,GAA0B;AACxB,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,MAAM,SAAS,cAAc,CAAC,CAAC;AAC3E,QAAM,CAAC,cAAc,eAAe,IAAI,MAAM,SAAS,cAAc,CAAC,CAAC;AAEvE,QAAM,qBAAqB,gCAAa;AACxC,QAAM,6BAA6B,gDAAqB;AACxD,QAAM,2BAA2B,MAAM;AAAA,IACrC,MAAM,IAAI,IAAI,4CAAmB,OAAO,KAAK,mBAAmB,CAAC;AAAA,IACjE,CAAC,iBAAiB,mBAAmB;AAAA,EACvC;AACA,QAAM,kBAAkB,MAAM;AAAA,IAC5B,MAAO,iBAAE,KAAK,IAAI,OAAO,KAAK,QAAQ,IAAI,MAAM,OAAQ;AAAA,IACxD,CAAC,YAAY;AAAA,EACf;AACA,QAAM,wBAAwB,kDAAsB;AACpD,QAAM,kBAAkB,MAAM;AAAA,IAC5B,MAAM,KAAK,IAAI,GAAG,OAAO,IAAI,CAAC,UAAU,MAAM,KAAK,GAAG,CAAC;AAAA,IACvD,CAAC,MAAM;AAAA,EACT;AAEA,QAAM,aAAa,MAAM,QAAQ,MAAM;AAlOzC;AAmOI,UAAM,aACJ,gEAAmB,oBAAnB,mBAAoC,aAApC,YACC,EAAE,EAAC,kBAAO,CAAC,MAAR,mBAAW,UAAX,YAAoB,UAAU,GAAG,cAAc;AAKrD,UAAM,WAAW,OAAO,QAAQ,SAAS,EAAE,IAAI,CAAC,CAAC,MAAM,KAAK,GAAG,WAAW;AAAA,MACxE,IAAI;AAAA,MACJ,WAAW,gBAAgB,QAAQ,gBAAgB,MAAM;AAAA,MACzD;AAAA,IACF,EAAE;AAEF,UAAM,cAAc,OAAO,KAAK,mBAAmB;AACnD,UAAM,eAAe,YAAY,SAAS,IACtC,YAAY,IAAI,CAAC,YAAY;AAAA,MAC3B,IAAI;AAAA,MACJ,WAAW,8CAAoB;AAAA,IACjC,EAAE,IACF;AAEJ,UAAM,QAAQ;AAAA,MACZ,GAAG;AAAA,MACH,GAAG;AAAA,MACH,GAAG;AAAA,IACL;AAEA,UAAM,UAAU,IAAI,IAAI,MAAM,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;AAC9C,eAAW,QAAQ,WAAW;AAC5B,iBAAW,YAAY,CAAC,KAAK,QAAQ,KAAK,MAAM,GAAG;AACjD,YAAI,CAAC,QAAQ,IAAI,QAAQ,GAAG;AAC1B,gBAAM,KAAK,EAAE,IAAI,UAAU,WAAW,8CAAoB,UAAU,CAAC;AACrE,kBAAQ,IAAI,QAAQ;AAAA,QACtB;AAAA,MACF;AAAA,IACF;AAEA,UAAM,QAA6D,CAAC;AAEpE,UAAM,iBAAgB,qBAAU,CAAC,MAAX,mBAAc,OAAd,YAAoB;AAE1C,aAAS,QAAQ,CAAC,YAAY;AAC5B,UAAI,QAAQ,QAAQ,GAAG;AACrB,cAAM,KAAK;AAAA,UACT,QAAQ,QAAQ;AAAA,UAChB,QAAQ;AAAA,UACR,OAAO,QAAQ;AAAA,QACjB,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAED,eAAW,CAAC,QAAQ,KAAK,KAAK,OAAO,QAAQ,mBAAmB,GAAG;AACjE,UAAI,QAAQ,GAAG;AACb,cAAM,KAAK,EAAE,QAAQ,eAAe,QAAQ,QAAQ,OAAO,MAAM,CAAC;AAAA,MACpE;AAAA,IACF;AAEA,cAAU,QAAQ,CAAC,SAAS,MAAM,KAAK,mBAAK,KAAM,CAAC;AAEnD,WAAO,EAAE,OAAO,MAAM;AAAA,EACxB,GAAG;AAAA,IACD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MAGA;AAAA,4BAAC,SAAI,WAAU,4BACb,+BAAC,SAAI,WAAU,sEACb;AAAA,+BAAC,SAAI,WAAU,2BACb;AAAA,gCAAC,QAAG,WAAU,yCAAyC,iBAAM;AAAA,YAG5D,cAAc,SAAS,KACtB,qBAAC,SAAI,WAAU,wEACZ;AAAA,4BAAc,IAAI,CAAC,SAClB;AAAA,gBAAC;AAAA;AAAA,kBAEC,SAAS,MAAM,gBAAgB,IAAI;AAAA,kBACnC,WAAW;AAAA,oBACT;AAAA,oBACA,iBAAiB,OACb,iEACA;AAAA,kBACN;AAAA,kBAEC;AAAA;AAAA,gBATI;AAAA,cAUP,CACD;AAAA,cACD,oBAAC,mBACC,+BAAC,WACC;AAAA,oCAAC,kBAAe,SAAO,MACrB,8BAAC,QAAK,WAAU,yDAAwD,GAC1E;AAAA,gBACA,oBAAC,kBAAe,WAAU,yBACvB,+BACH;AAAA,iBACF,GACF;AAAA,eACF;AAAA,aAEJ;AAAA,UAGC,cAAc,SAAS,KACtB,oBAAC,SAAI,WAAU,2EACZ,wBAAc,IAAI,CAAC,WAClB;AAAA,YAAC;AAAA;AAAA,cAEC,SAAS,MAAM;AAAE,kCAAkB,MAAM;AAAG,iEAAiB;AAAA,cAAS;AAAA,cACtE,WAAW;AAAA,gBACT;AAAA,gBACA,mBAAmB,UACjB;AAAA,cACJ;AAAA,cAEC;AAAA;AAAA,YARI;AAAA,UASP,CACD,GACH;AAAA,WAEJ,GACF;AAAA,QAGA,oBAAC,SAAI,WAAU,mBAAkB,OAAO,EAAE,qBAAqB,UAAU,OAAO,MAAM,oBAAoB,GACvG,iBAAO,IAAI,CAAC,OAAO,UAAU;AA7WtC;AA8WU,gBAAM,UAAU,aAAa,MAAM,EAAE;AACrC,gBAAM,UAAS,kBAAa,KAAK,MAAlB,YAAuB;AAEtC,iBACE,oBAAC,mBAA+B,eAAe,KAC7C,+BAAC,WACC;AAAA,gCAAC,kBAAe,SAAO,MACrB,+BAAC,SAAI,WAAU,wHACb;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,WAAU;AAAA,kBACV,OAAO,MAAM;AAAA,kBAEZ,gBAAM;AAAA;AAAA,cACT;AAAA,cACA,oBAAC,SAAI,WAAU,2CACZ,gBAAM,MAAM,eAAe,GAC9B;AAAA,cACA,qBAAC,SAAI,WAAU,oFACZ;AAAA,sBAAM;AAAA,gBAAM;AAAA,gBAAC,oBAAC,cAAW,WAAU,WAAU;AAAA,iBAChD;AAAA,cAGC,QAAQ,OAAO,SAAS,KAAK,MAAM,kBAClC,qBAAC,SAAI,WAAW;AAAA,gBACd,6BAA6B,SAAS;AAAA,gBACtC;AAAA,cACF,GACE;AAAA,oCAAC,UAAK,WAAU,2IACb,gBAAM,gBACT;AAAA,gBACC,UACC,qBAAC,SAAI,WAAU,mCACb;AAAA,sCAAC,SAAI,WAAU,sBAAqB;AAAA,kBACpC,qBAAC,SAAI,WAAU,wIACb;AAAA,yCAAC,UAAK,WAAU,4CAA2C;AAAA;AAAA,sBACnD,OAAO;AAAA,uBACf;AAAA,oBACA,qBAAC,UAAK,WAAU,4BAA2B;AAAA;AAAA,sBACnC,OAAO;AAAA,uBACf;AAAA,qBACF;AAAA,mBACF;AAAA,iBAEJ;AAAA,cAIF,oBAAC,SAAI,WAAU,wCAAuC;AAAA,eACxD,GACF;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,WAAU;AAAA,gBACV,YAAY;AAAA,gBAEZ;AAAA,kBAAC;AAAA;AAAA,oBACC,OAAO,MAAM;AAAA,oBACb,OAAO,MAAM;AAAA,oBACb,SAAS;AAAA;AAAA,gBACX;AAAA;AAAA,YACF;AAAA,aACF,KAxDoB,MAAM,EAyD5B;AAAA,QAEJ,CAAC,GACH;AAAA,QAEC,YAAY,YACX,oBAAC,SAAI,aAAU,6BAA4B,WAAU,kBAClD,iBAAO,IAAI,CAAC,OAAO,UAAU;AAlbxC;AAmbY,gBAAM,QAAQ,GAAG,KAAK,IAAK,MAAM,QAAQ,kBAAmB,KAAK,CAAC,CAAC;AAEnE,iBACE,qBAAC,SAAmB,WAAU,qDAC5B;AAAA,iCAAC,SAAI,WAAU,gDACb;AAAA,mCAAC,SAAI,WAAU,WACb;AAAA,oCAAC,SAAI,WAAU,gDAAgD,gBAAM,OAAM;AAAA,gBAC3E,oBAAC,SAAI,WAAU,iCAAiC,gBAAM,OAAM;AAAA,iBAC9D;AAAA,cACA,oBAAC,SAAI,WAAU,gDACZ,gBAAM,MAAM,eAAe,GAC9B;AAAA,eACF;AAAA,YACA,oBAAC,SAAI,WAAU,6CACb;AAAA,cAAC;AAAA;AAAA,gBACC,aAAU;AAAA,gBACV,WAAU;AAAA,gBACV,OAAO,EAAE,MAAM;AAAA;AAAA,YACjB,GACF;AAAA,YACC,QAAQ,OAAO,SAAS,KAAK,MAAM,iBAClC,qBAAC,SAAI,WAAU,sCACZ;AAAA,oBAAM;AAAA,cAAe;AAAA,eAAK,YAAO,QAAQ,CAAC,MAAhB,mBAAmB;AAAA,eAChD,IACE;AAAA,eArBI,MAAM,EAsBhB;AAAA,QAEJ,CAAC,GACH,IAEF;AAAA,UAAC;AAAA;AAAA,YACC,aAAU;AAAA,YACV,WAAU;AAAA,YACV,OAAO,EAAE,QAAQ,KAAK,UAAU,EAAE;AAAA,YAElC;AAAA,cAAC;AAAA;AAAA,gBACC,MAAM;AAAA,gBACN,QAAQ;AAAA,gBACR,OAAM;AAAA,gBACN,QAAQ,CAAC,SAAiC,KAAK,aAAa;AAAA,gBAC5D,aAAa;AAAA,gBACb,wBAAwB;AAAA,gBACxB,eAAe;AAAA,gBACf,aAAa;AAAA,gBACb,iBAAiB;AAAA,gBACjB,kBAAkB;AAAA,gBAClB,aAAa;AAAA,gBACb,wBAAwB;AAAA,gBACxB,cAAc;AAAA,gBACd,oBAAkB;AAAA,gBAClB,eAAc;AAAA,gBACd,kBAAiB;AAAA,gBACjB,cAAc;AAAA,gBACd,OAAO,CAAC,UAAyB,2CAAc,KAAK,OAAM,GAAG,KAAK,EAAE,KAAK,YAAY,KAAK,EAAE,CAAC,MAAM,KAAK;AAAA,gBACxG,gBAAgB,EAAE,MAAM,SAAS,WAAW,CAAC,CAAC,UAAU,CAAC,CAAC,EAAE;AAAA,gBAC5D,aAAa,CAAC,EAAE,KAAK,MAA+C;AAClE,wBAAM,OAAO,yBAAyB,IAAI,KAAK,EAAE,IAAI,6BAA6B;AAClF,yBACE,qBAAC,SAAI,WAAU,iEACb;AAAA,yCAAC,UAAK,WAAU,wBAAwB;AAAA,2BAAK;AAAA,uBAAI,2CAAc,KAAK,OAAM,WAAM,YAAY,KAAK,EAAE,CAAC,KAAK;AAAA,uBAAG;AAAA,oBAC5G,qBAAC,UAAM;AAAA,2BAAK;AAAA,sBAAM;AAAA,sBAAE;AAAA,uBAAK;AAAA,qBAC3B;AAAA,gBAEJ;AAAA,gBACA,aAAa,CAAC,EAAE,KAAK,MAAmF;AACtG,wBAAM,OAAO,yBAAyB,IAAI,KAAK,OAAO,EAAE,IAAI,6BAA6B;AACzF,yBACE,qBAAC,SAAI,WAAU,iEACb;AAAA,yCAAC,UAAK,WAAU,wBACb;AAAA,2BAAK,OAAO;AAAA,sBAAG;AAAA,sBAAI,KAAK,OAAO;AAAA,uBAClC;AAAA,oBACA,qBAAC,UAAM;AAAA,2BAAK;AAAA,sBAAM;AAAA,sBAAE;AAAA,uBAAK;AAAA,qBAC3B;AAAA,gBAEJ;AAAA;AAAA,YACF;AAAA;AAAA,QACF;AAAA;AAAA;AAAA,EAEF;AAEJ;","names":[]}
@@ -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> & {
@@ -0,0 +1,16 @@
1
+ import * as React from 'react';
2
+ import { PillStatus } from './pill.js';
3
+ import 'class-variance-authority/types';
4
+ import 'class-variance-authority';
5
+
6
+ interface DaysOpenCellProps extends React.HTMLAttributes<HTMLDivElement> {
7
+ days: number | null | undefined;
8
+ warningAt?: number;
9
+ criticalAt?: number;
10
+ emptyLabel?: string;
11
+ suffix?: string;
12
+ }
13
+ declare function getDaysOpenIntent(days: number, warningAt: number, criticalAt: number): PillStatus;
14
+ declare function DaysOpenCell({ days, warningAt, criticalAt, emptyLabel, suffix, className, ...props }: DaysOpenCellProps): React.JSX.Element;
15
+
16
+ export { DaysOpenCell, type DaysOpenCellProps, getDaysOpenIntent };
@@ -0,0 +1,73 @@
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
+ import { StatusPill } from "./pill.js";
38
+ function getDaysOpenIntent(days, warningAt, criticalAt) {
39
+ if (days >= criticalAt) return "error";
40
+ if (days >= warningAt) return "warning";
41
+ return "success";
42
+ }
43
+ function DaysOpenCell(_a) {
44
+ var _b = _a, {
45
+ days,
46
+ warningAt = 7,
47
+ criticalAt = 30,
48
+ emptyLabel = "\u2014",
49
+ suffix = "d open",
50
+ className
51
+ } = _b, props = __objRest(_b, [
52
+ "days",
53
+ "warningAt",
54
+ "criticalAt",
55
+ "emptyLabel",
56
+ "suffix",
57
+ "className"
58
+ ]);
59
+ if (days === null || days === void 0) {
60
+ return /* @__PURE__ */ jsx("div", __spreadProps(__spreadValues({ "data-slot": "days-open-cell", className: cn("text-sm text-muted-foreground", className) }, props), { children: emptyLabel }));
61
+ }
62
+ const intent = getDaysOpenIntent(days, warningAt, criticalAt);
63
+ return /* @__PURE__ */ jsx("div", __spreadProps(__spreadValues({ "data-slot": "days-open-cell", className: cn("inline-flex items-center", className) }, props), { children: /* @__PURE__ */ jsxs(StatusPill, { "data-testid": "days-open-pill", status: `${days} ${suffix}`, intent, children: [
64
+ days,
65
+ " ",
66
+ suffix
67
+ ] }) }));
68
+ }
69
+ export {
70
+ DaysOpenCell,
71
+ getDaysOpenIntent
72
+ };
73
+ //# sourceMappingURL=days-open-cell.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/days-open-cell.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\n\nimport { cn } from \"../lib/utils\"\nimport { StatusPill, type PillStatus } from \"./pill\"\n\nexport interface DaysOpenCellProps extends React.HTMLAttributes<HTMLDivElement> {\n days: number | null | undefined\n warningAt?: number\n criticalAt?: number\n emptyLabel?: string\n suffix?: string\n}\n\nfunction getDaysOpenIntent(days: number, warningAt: number, criticalAt: number): PillStatus {\n if (days >= criticalAt) return \"error\"\n if (days >= warningAt) return \"warning\"\n return \"success\"\n}\n\nexport function DaysOpenCell({\n days,\n warningAt = 7,\n criticalAt = 30,\n emptyLabel = \"—\",\n suffix = \"d open\",\n className,\n ...props\n}: DaysOpenCellProps) {\n if (days === null || days === undefined) {\n return (\n <div data-slot=\"days-open-cell\" className={cn(\"text-sm text-muted-foreground\", className)} {...props}>\n {emptyLabel}\n </div>\n )\n }\n\n const intent = getDaysOpenIntent(days, warningAt, criticalAt)\n\n return (\n <div data-slot=\"days-open-cell\" className={cn(\"inline-flex items-center\", className)} {...props}>\n <StatusPill data-testid=\"days-open-pill\" status={`${days} ${suffix}`} intent={intent}>\n {days} {suffix}\n </StatusPill>\n </div>\n )\n}\n\nexport { getDaysOpenIntent }\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCM,cAUA,YAVA;AA5BN,SAAS,UAAU;AACnB,SAAS,kBAAmC;AAU5C,SAAS,kBAAkB,MAAc,WAAmB,YAAgC;AAC1F,MAAI,QAAQ,WAAY,QAAO;AAC/B,MAAI,QAAQ,UAAW,QAAO;AAC9B,SAAO;AACT;AAEO,SAAS,aAAa,IAQP;AARO,eAC3B;AAAA;AAAA,IACA,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,aAAa;AAAA,IACb,SAAS;AAAA,IACT;AAAA,EA3BF,IAqB6B,IAOxB,kBAPwB,IAOxB;AAAA,IANH;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA;AAGA,MAAI,SAAS,QAAQ,SAAS,QAAW;AACvC,WACE,oBAAC,sCAAI,aAAU,kBAAiB,WAAW,GAAG,iCAAiC,SAAS,KAAO,QAA9F,EACE,uBACH;AAAA,EAEJ;AAEA,QAAM,SAAS,kBAAkB,MAAM,WAAW,UAAU;AAE5D,SACE,oBAAC,sCAAI,aAAU,kBAAiB,WAAW,GAAG,4BAA4B,SAAS,KAAO,QAAzF,EACC,+BAAC,cAAW,eAAY,kBAAiB,QAAQ,GAAG,IAAI,IAAI,MAAM,IAAI,QACnE;AAAA;AAAA,IAAK;AAAA,IAAE;AAAA,KACV,IACF;AAEJ;","names":[]}
@@ -0,0 +1,16 @@
1
+ import * as React from 'react';
2
+
3
+ interface DetailDrawerProps {
4
+ open: boolean;
5
+ onOpenChange: (open: boolean) => void;
6
+ title: React.ReactNode;
7
+ description?: React.ReactNode;
8
+ children: React.ReactNode;
9
+ footer?: React.ReactNode;
10
+ side?: "right" | "left";
11
+ className?: string;
12
+ contentClassName?: string;
13
+ }
14
+ declare function DetailDrawer({ open, onOpenChange, title, description, children, footer, side, className, contentClassName, }: DetailDrawerProps): React.JSX.Element;
15
+
16
+ export { DetailDrawer, type DetailDrawerProps };
@@ -0,0 +1,45 @@
1
+ "use client"
2
+
3
+ "use client";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ import { cn } from "../lib/utils.js";
6
+ import {
7
+ Sheet,
8
+ SheetContent,
9
+ SheetDescription,
10
+ SheetFooter,
11
+ SheetHeader,
12
+ SheetTitle
13
+ } from "./sheet.js";
14
+ function DetailDrawer({
15
+ open,
16
+ onOpenChange,
17
+ title,
18
+ description,
19
+ children,
20
+ footer,
21
+ side = "right",
22
+ className,
23
+ contentClassName
24
+ }) {
25
+ return /* @__PURE__ */ jsx(Sheet, { open, onOpenChange, children: /* @__PURE__ */ jsxs(
26
+ SheetContent,
27
+ {
28
+ "data-slot": "detail-drawer",
29
+ side,
30
+ className: cn("flex w-full flex-col gap-0 p-0 sm:max-w-xl", className),
31
+ children: [
32
+ /* @__PURE__ */ jsxs(SheetHeader, { "data-slot": "detail-drawer-header", className: "border-b border-border p-5", children: [
33
+ /* @__PURE__ */ jsx(SheetTitle, { children: title }),
34
+ description ? /* @__PURE__ */ jsx(SheetDescription, { children: description }) : null
35
+ ] }),
36
+ /* @__PURE__ */ jsx("div", { "data-slot": "detail-drawer-content", className: cn("flex-1 overflow-y-auto p-5", contentClassName), children }),
37
+ footer ? /* @__PURE__ */ jsx(SheetFooter, { "data-slot": "detail-drawer-footer", className: "border-t border-border p-5", children: footer }) : null
38
+ ]
39
+ }
40
+ ) });
41
+ }
42
+ export {
43
+ DetailDrawer
44
+ };
45
+ //# sourceMappingURL=detail-drawer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/detail-drawer.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\n\nimport { cn } from \"../lib/utils\"\nimport {\n Sheet,\n SheetContent,\n SheetDescription,\n SheetFooter,\n SheetHeader,\n SheetTitle,\n} from \"./sheet\"\n\nexport interface DetailDrawerProps {\n open: boolean\n onOpenChange: (open: boolean) => void\n title: React.ReactNode\n description?: React.ReactNode\n children: React.ReactNode\n footer?: React.ReactNode\n side?: \"right\" | \"left\"\n className?: string\n contentClassName?: string\n}\n\nexport function DetailDrawer({\n open,\n onOpenChange,\n title,\n description,\n children,\n footer,\n side = \"right\",\n className,\n contentClassName,\n}: DetailDrawerProps) {\n return (\n <Sheet open={open} onOpenChange={onOpenChange}>\n <SheetContent\n data-slot=\"detail-drawer\"\n side={side}\n className={cn(\"flex w-full flex-col gap-0 p-0 sm:max-w-xl\", className)}\n >\n <SheetHeader data-slot=\"detail-drawer-header\" className=\"border-b border-border p-5\">\n <SheetTitle>{title}</SheetTitle>\n {description ? <SheetDescription>{description}</SheetDescription> : null}\n </SheetHeader>\n <div data-slot=\"detail-drawer-content\" className={cn(\"flex-1 overflow-y-auto p-5\", contentClassName)}>\n {children}\n </div>\n {footer ? (\n <SheetFooter data-slot=\"detail-drawer-footer\" className=\"border-t border-border p-5\">\n {footer}\n </SheetFooter>\n ) : null}\n </SheetContent>\n </Sheet>\n )\n}\n"],"mappings":";AA4CQ,SACE,KADF;AAxCR,SAAS,UAAU;AACnB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAcA,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP;AAAA,EACA;AACF,GAAsB;AACpB,SACE,oBAAC,SAAM,MAAY,cACjB;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV;AAAA,MACA,WAAW,GAAG,8CAA8C,SAAS;AAAA,MAErE;AAAA,6BAAC,eAAY,aAAU,wBAAuB,WAAU,8BACtD;AAAA,8BAAC,cAAY,iBAAM;AAAA,UAClB,cAAc,oBAAC,oBAAkB,uBAAY,IAAsB;AAAA,WACtE;AAAA,QACA,oBAAC,SAAI,aAAU,yBAAwB,WAAW,GAAG,8BAA8B,gBAAgB,GAChG,UACH;AAAA,QACC,SACC,oBAAC,eAAY,aAAU,wBAAuB,WAAU,8BACrD,kBACH,IACE;AAAA;AAAA;AAAA,EACN,GACF;AAEJ;","names":[]}
@@ -16,18 +16,6 @@ interface FeedbackSubmitData {
16
16
  pills: string[];
17
17
  detail: string;
18
18
  }
19
- /**
20
- * Persisted feedback data from a previous submission, used to hydrate the
21
- * footer into its "already submitted" visual state.
22
- */
23
- interface PersistedFeedbackData {
24
- sentiment: "positive" | "negative";
25
- reasonTop?: string;
26
- reasonSub?: string;
27
- pills?: string[];
28
- detail?: string;
29
- ownershipLabel: "Your feedback" | "Team feedback";
30
- }
31
19
  /**
32
20
  * Defines a tier-1 chip that may have tier-2 sub-chips.
33
21
  */
@@ -72,34 +60,7 @@ interface FeedbackFooterProps {
72
60
  negativeChips?: FeedbackChipTree[];
73
61
  positiveChips?: string[];
74
62
  className?: string;
75
- /** Pre-existing feedback to hydrate from (e.g. after page reload). */
76
- initialFeedback?: PersistedFeedbackData | null;
77
- /** Label shown in the transient confirmation pill after submit. */
78
- submittedLabel?: string;
79
- /** Stable key for syncing initialFeedback into local state. When this
80
- * changes, the component resets to the new initialFeedback value. */
81
- feedbackKey?: string;
82
- }
83
- declare function FeedbackFooter({ feedback, onFeedbackChange, onSubmit, metaText, positivePrompt, negativePrompt, negativeChips, positiveChips, className, initialFeedback, submittedLabel, feedbackKey, }: FeedbackFooterProps): React.JSX.Element;
84
- interface InlineFeedbackControlProps {
85
- /** Unique key identifying the feedback target (e.g. factor key). */
86
- feedbackKey: string;
87
- /** Persisted/initial feedback to hydrate from. */
88
- initialFeedback?: {
89
- type: "up" | "down";
90
- detail: string;
91
- ownershipLabel?: string;
92
- };
93
- /** Called when user submits or clears feedback. */
94
- onFeedback?: (key: string, type: "up" | "down" | null, detail?: string) => void;
95
- /** Test ID prefix for all sub-elements. */
96
- testIdPrefix?: string;
97
63
  }
98
- /**
99
- * Compact inline thumb-up/thumb-down feedback with optional detail text.
100
- * Used by PriorityFactorRow and any other component that needs
101
- * a lightweight feedback control.
102
- */
103
- declare function InlineFeedbackControl({ feedbackKey, initialFeedback, onFeedback, testIdPrefix, }: InlineFeedbackControlProps): React.JSX.Element;
64
+ declare function FeedbackFooter({ feedback, onFeedbackChange, onSubmit, metaText, positivePrompt, negativePrompt, negativeChips, positiveChips, className, }: FeedbackFooterProps): React.JSX.Element;
104
65
 
105
- export { FeedbackActions, type FeedbackActionsProps, FeedbackChipGroup, type FeedbackChipGroupProps, type FeedbackChipTree, FeedbackFooter, type FeedbackFooterProps, FeedbackInput, type FeedbackInputProps, type FeedbackSubmitData, InlineFeedbackControl, type InlineFeedbackControlProps, type PersistedFeedbackData };
66
+ export { FeedbackActions, type FeedbackActionsProps, FeedbackChipGroup, type FeedbackChipGroupProps, type FeedbackChipTree, FeedbackFooter, type FeedbackFooterProps, FeedbackInput, type FeedbackInputProps, type FeedbackSubmitData };