@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.
- package/dist/charts/empty-chart-state.d.ts +11 -0
- package/dist/charts/empty-chart-state.js +70 -0
- package/dist/charts/empty-chart-state.js.map +1 -0
- package/dist/charts/index.d.ts +1 -0
- package/dist/charts/index.js +1 -0
- package/dist/charts/index.js.map +1 -1
- package/dist/charts/pipeline-overview.d.ts +2 -1
- package/dist/charts/pipeline-overview.js +29 -1
- package/dist/charts/pipeline-overview.js.map +1 -1
- package/dist/components/actor-byline.d.ts +3 -0
- package/dist/components/actor-byline.js +5 -0
- package/dist/components/actor-byline.js.map +1 -0
- package/dist/components/days-open-cell.d.ts +16 -0
- package/dist/components/days-open-cell.js +73 -0
- package/dist/components/days-open-cell.js.map +1 -0
- package/dist/components/detail-drawer.d.ts +16 -0
- package/dist/components/detail-drawer.js +45 -0
- package/dist/components/detail-drawer.js.map +1 -0
- package/dist/components/insights-filter-bar.d.ts +2 -1
- package/dist/components/insights-filter-bar.js +13 -5
- package/dist/components/insights-filter-bar.js.map +1 -1
- package/dist/components/linked-entity-cell.d.ts +14 -0
- package/dist/components/linked-entity-cell.js +96 -0
- package/dist/components/linked-entity-cell.js.map +1 -0
- package/dist/components/metric-card.d.ts +14 -1
- package/dist/components/metric-card.js +86 -0
- package/dist/components/metric-card.js.map +1 -1
- package/dist/components/performance-metrics-table.d.ts +2 -1
- package/dist/components/performance-metrics-table.js +78 -46
- package/dist/components/performance-metrics-table.js.map +1 -1
- package/dist/components/pill.d.ts +26 -0
- package/dist/components/pill.js +77 -0
- package/dist/components/pill.js.map +1 -0
- package/dist/components/quick-segment.d.ts +13 -0
- package/dist/components/quick-segment.js +96 -0
- package/dist/components/quick-segment.js.map +1 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/charts/__tests__/insights-charts.test.tsx +62 -0
- package/src/charts/empty-chart-state.tsx +44 -0
- package/src/charts/index.ts +1 -0
- package/src/charts/pipeline-overview.tsx +38 -1
- package/src/components/__tests__/insights-primitives.test.tsx +117 -0
- package/src/components/__tests__/performance-metrics-table.test.tsx +54 -0
- package/src/components/__tests__/user-display.test.tsx +75 -0
- package/src/components/actor-byline.tsx +1 -0
- package/src/components/days-open-cell.tsx +50 -0
- package/src/components/detail-drawer.tsx +60 -0
- package/src/components/insights-filter-bar.tsx +13 -4
- package/src/components/linked-entity-cell.tsx +74 -0
- package/src/components/metric-card.tsx +82 -0
- package/src/components/performance-metrics-table.tsx +99 -63
- package/src/components/pill.tsx +67 -0
- package/src/components/quick-segment.tsx +68 -0
- package/src/index.ts +5 -0
- package/src/lib/__tests__/user-display.test.ts +53 -11
|
@@ -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":[]}
|
package/dist/charts/index.d.ts
CHANGED
|
@@ -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';
|
package/dist/charts/index.js
CHANGED
package/dist/charts/index.js.map
CHANGED
|
@@ -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,
|
|
@@ -302,9 +303,36 @@ function PipelineOverview({
|
|
|
302
303
|
)
|
|
303
304
|
] }) }, stage.id);
|
|
304
305
|
}) }),
|
|
305
|
-
/* @__PURE__ */ jsx(
|
|
306
|
+
variant === "compact" ? /* @__PURE__ */ jsx("div", { "data-slot": "pipeline-overview-compact", className: "mt-4 space-y-2", children: stages.map((stage, index) => {
|
|
307
|
+
var _a;
|
|
308
|
+
const maxCount = Math.max(...stages.map((item) => item.count), 1);
|
|
309
|
+
const width = `${Math.max(stage.count / maxCount * 100, 4)}%`;
|
|
310
|
+
return /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-border bg-background p-3", children: [
|
|
311
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center justify-between gap-3", children: [
|
|
312
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
313
|
+
/* @__PURE__ */ jsx("div", { className: "truncate text-sm font-medium text-foreground", children: stage.label }),
|
|
314
|
+
/* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground", children: stage.trend })
|
|
315
|
+
] }),
|
|
316
|
+
/* @__PURE__ */ jsx("div", { className: "text-right text-lg font-bold text-foreground", children: stage.count.toLocaleString() })
|
|
317
|
+
] }),
|
|
318
|
+
/* @__PURE__ */ jsx("div", { className: "h-2 overflow-hidden rounded-full bg-muted", children: /* @__PURE__ */ jsx(
|
|
319
|
+
"div",
|
|
320
|
+
{
|
|
321
|
+
"data-slot": "pipeline-overview-compact-bar",
|
|
322
|
+
className: "h-full rounded-full bg-emerald-600",
|
|
323
|
+
style: { width }
|
|
324
|
+
}
|
|
325
|
+
) }),
|
|
326
|
+
index < stages.length - 1 && stage.nextConversion ? /* @__PURE__ */ jsxs("div", { className: "mt-2 text-xs text-muted-foreground", children: [
|
|
327
|
+
stage.nextConversion,
|
|
328
|
+
" to ",
|
|
329
|
+
(_a = stages[index + 1]) == null ? void 0 : _a.label
|
|
330
|
+
] }) : null
|
|
331
|
+
] }, stage.id);
|
|
332
|
+
}) }) : /* @__PURE__ */ jsx(
|
|
306
333
|
"div",
|
|
307
334
|
{
|
|
335
|
+
"data-slot": "pipeline-overview-sankey",
|
|
308
336
|
className: "relative mt-4 w-full",
|
|
309
337
|
style: { height: 400, minWidth: 0 },
|
|
310
338
|
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\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 maxCount = Math.max(...stages.map((item) => item.count), 1)\n const width = `${Math.max((stage.count / maxCount) * 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":"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","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/actor-byline.tsx"],"sourcesContent":["export { ActorByline, type ActorBylineProps } from \"./user-display\"\n"],"mappings":"AAAA,SAAS,mBAA0C;","names":[]}
|
|
@@ -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("w-full 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(\"w-full 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,gCAAgC,SAAS;AAAA,MAEvD;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":[]}
|
|
@@ -11,11 +11,12 @@ interface FilterDefinition {
|
|
|
11
11
|
}
|
|
12
12
|
interface InsightsFilterBarProps {
|
|
13
13
|
filters: FilterDefinition[];
|
|
14
|
+
variant?: "default" | "compact";
|
|
14
15
|
values: Record<string, string>;
|
|
15
16
|
onChange: (filterId: string, value: string) => void;
|
|
16
17
|
onClearAll?: () => void;
|
|
17
18
|
className?: string;
|
|
18
19
|
}
|
|
19
|
-
declare function InsightsFilterBar({ filters, values, onChange, onClearAll, className, }: InsightsFilterBarProps): React.JSX.Element;
|
|
20
|
+
declare function InsightsFilterBar({ filters, values, onChange, onClearAll, className, variant, }: InsightsFilterBarProps): React.JSX.Element;
|
|
20
21
|
|
|
21
22
|
export { type FilterDefinition, InsightsFilterBar, type InsightsFilterBarProps };
|
|
@@ -24,7 +24,8 @@ function InsightsFilterBar({
|
|
|
24
24
|
values,
|
|
25
25
|
onChange,
|
|
26
26
|
onClearAll,
|
|
27
|
-
className
|
|
27
|
+
className,
|
|
28
|
+
variant = "default"
|
|
28
29
|
}) {
|
|
29
30
|
const showClearAll = onClearAll && hasNonDefaultValue(filters, values);
|
|
30
31
|
return /* @__PURE__ */ jsxs(
|
|
@@ -32,11 +33,12 @@ function InsightsFilterBar({
|
|
|
32
33
|
{
|
|
33
34
|
"data-slot": "insights-filter-bar",
|
|
34
35
|
className: cn(
|
|
35
|
-
"flex flex-wrap items-center
|
|
36
|
+
"flex flex-wrap items-center rounded-md border border-border bg-card shadow-sm",
|
|
37
|
+
variant === "compact" ? "gap-2 p-2" : "gap-3 p-4",
|
|
36
38
|
className
|
|
37
39
|
),
|
|
38
40
|
children: [
|
|
39
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
41
|
+
/* @__PURE__ */ jsxs("div", { className: cn("flex items-center gap-2", variant === "compact" && "sr-only"), children: [
|
|
40
42
|
/* @__PURE__ */ jsx(FilterIcon, { className: "h-4 w-4 text-muted-foreground" }),
|
|
41
43
|
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-muted-foreground", children: "Filters:" })
|
|
42
44
|
] }),
|
|
@@ -51,7 +53,10 @@ function InsightsFilterBar({
|
|
|
51
53
|
{
|
|
52
54
|
variant: "outline",
|
|
53
55
|
size: "sm",
|
|
54
|
-
className:
|
|
56
|
+
className: cn(
|
|
57
|
+
"gap-1.5 text-xs font-normal shadow-none",
|
|
58
|
+
variant === "compact" ? "h-7 px-2" : "h-8"
|
|
59
|
+
),
|
|
55
60
|
children: [
|
|
56
61
|
IconComp ? /* @__PURE__ */ jsx(IconComp, { className: "h-3.5 w-3.5 text-muted-foreground" }) : null,
|
|
57
62
|
filter.label,
|
|
@@ -84,7 +89,10 @@ function InsightsFilterBar({
|
|
|
84
89
|
{
|
|
85
90
|
variant: "ghost",
|
|
86
91
|
size: "sm",
|
|
87
|
-
className:
|
|
92
|
+
className: cn(
|
|
93
|
+
"text-xs text-destructive hover:text-destructive",
|
|
94
|
+
variant === "compact" ? "h-7 px-2" : "h-8"
|
|
95
|
+
),
|
|
88
96
|
onClick: onClearAll,
|
|
89
97
|
children: "Clear All"
|
|
90
98
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/insights-filter-bar.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { CalendarIcon, ChevronDownIcon, FilterIcon } from \"lucide-react\"\n\nimport { cn } from \"../lib/utils\"\nimport { Button } from \"./button\"\nimport {\n DropdownMenu,\n DropdownMenuCheckboxItem,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from \"./dropdown-menu\"\n\nexport interface FilterDefinition {\n id: string\n label: string\n options: string[]\n defaultValue?: string\n icon?: \"calendar\" | React.ComponentType<{ className?: string }>\n}\n\nexport interface InsightsFilterBarProps {\n filters: FilterDefinition[]\n values: Record<string, string>\n onChange: (filterId: string, value: string) => void\n onClearAll?: () => void\n className?: string\n}\n\nfunction hasNonDefaultValue(\n filters: FilterDefinition[],\n values: Record<string, string>\n) {\n return filters.some((filter) => {\n const defaultVal = filter.defaultValue ?? filter.options[0] ?? \"All\"\n return values[filter.id] !== undefined && values[filter.id] !== defaultVal\n })\n}\n\nfunction InsightsFilterBar({\n filters,\n values,\n onChange,\n onClearAll,\n className,\n}: InsightsFilterBarProps) {\n const showClearAll = onClearAll && hasNonDefaultValue(filters, values)\n\n return (\n <div\n data-slot=\"insights-filter-bar\"\n className={cn(\n \"flex flex-wrap items-center
|
|
1
|
+
{"version":3,"sources":["../../src/components/insights-filter-bar.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { CalendarIcon, ChevronDownIcon, FilterIcon } from \"lucide-react\"\n\nimport { cn } from \"../lib/utils\"\nimport { Button } from \"./button\"\nimport {\n DropdownMenu,\n DropdownMenuCheckboxItem,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from \"./dropdown-menu\"\n\nexport interface FilterDefinition {\n id: string\n label: string\n options: string[]\n defaultValue?: string\n icon?: \"calendar\" | React.ComponentType<{ className?: string }>\n}\n\nexport interface InsightsFilterBarProps {\n filters: FilterDefinition[]\n variant?: \"default\" | \"compact\"\n values: Record<string, string>\n onChange: (filterId: string, value: string) => void\n onClearAll?: () => void\n className?: string\n}\n\nfunction hasNonDefaultValue(\n filters: FilterDefinition[],\n values: Record<string, string>\n) {\n return filters.some((filter) => {\n const defaultVal = filter.defaultValue ?? filter.options[0] ?? \"All\"\n return values[filter.id] !== undefined && values[filter.id] !== defaultVal\n })\n}\n\nfunction InsightsFilterBar({\n filters,\n values,\n onChange,\n onClearAll,\n className,\n variant = \"default\",\n}: InsightsFilterBarProps) {\n const showClearAll = onClearAll && hasNonDefaultValue(filters, values)\n\n return (\n <div\n data-slot=\"insights-filter-bar\"\n className={cn(\n \"flex flex-wrap items-center rounded-md border border-border bg-card shadow-sm\",\n variant === \"compact\" ? \"gap-2 p-2\" : \"gap-3 p-4\",\n className\n )}\n >\n <div className={cn(\"flex items-center gap-2\", variant === \"compact\" && \"sr-only\")}>\n <FilterIcon className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-sm font-medium text-muted-foreground\">\n Filters:\n </span>\n </div>\n\n {filters.map((filter) => {\n const current = values[filter.id] ?? filter.defaultValue ?? \"All\"\n const isCheckbox = filter.options.length > 0\n\n const IconComp =\n filter.icon === \"calendar\"\n ? CalendarIcon\n : typeof filter.icon === \"function\"\n ? filter.icon\n : null\n\n return (\n <DropdownMenu key={filter.id}>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"outline\"\n size=\"sm\"\n className={cn(\n \"gap-1.5 text-xs font-normal shadow-none\",\n variant === \"compact\" ? \"h-7 px-2\" : \"h-8\"\n )}\n >\n {IconComp ? (\n <IconComp className=\"h-3.5 w-3.5 text-muted-foreground\" />\n ) : null}\n {filter.label}: {current}\n <ChevronDownIcon className=\"h-3.5 w-3.5 opacity-50\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\">\n {isCheckbox\n ? filter.options.map((option) => (\n <DropdownMenuCheckboxItem\n key={option}\n checked={current === option}\n onCheckedChange={() => onChange(filter.id, option)}\n >\n {option}\n </DropdownMenuCheckboxItem>\n ))\n : filter.options.map((option) => (\n <DropdownMenuItem\n key={option}\n onSelect={() => onChange(filter.id, option)}\n >\n {option}\n </DropdownMenuItem>\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n )\n })}\n\n {showClearAll ? (\n <div className=\"ml-auto\">\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className={cn(\n \"text-xs text-destructive hover:text-destructive\",\n variant === \"compact\" ? \"h-7 px-2\" : \"h-8\"\n )}\n onClick={onClearAll}\n >\n Clear All\n </Button>\n </div>\n ) : null}\n </div>\n )\n}\n\nexport { InsightsFilterBar }\n"],"mappings":";AA6DM,SACE,KADF;AA1DN,SAAS,cAAc,iBAAiB,kBAAkB;AAE1D,SAAS,UAAU;AACnB,SAAS,cAAc;AACvB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAmBP,SAAS,mBACP,SACA,QACA;AACA,SAAO,QAAQ,KAAK,CAAC,WAAW;AApClC;AAqCI,UAAM,cAAa,kBAAO,iBAAP,YAAuB,OAAO,QAAQ,CAAC,MAAvC,YAA4C;AAC/D,WAAO,OAAO,OAAO,EAAE,MAAM,UAAa,OAAO,OAAO,EAAE,MAAM;AAAA,EAClE,CAAC;AACH;AAEA,SAAS,kBAAkB;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AACZ,GAA2B;AACzB,QAAM,eAAe,cAAc,mBAAmB,SAAS,MAAM;AAErE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA,YAAY,YAAY,cAAc;AAAA,QACtC;AAAA,MACF;AAAA,MAEA;AAAA,6BAAC,SAAI,WAAW,GAAG,2BAA2B,YAAY,aAAa,SAAS,GAC9E;AAAA,8BAAC,cAAW,WAAU,iCAAgC;AAAA,UACtD,oBAAC,UAAK,WAAU,6CAA4C,sBAE5D;AAAA,WACF;AAAA,QAEC,QAAQ,IAAI,CAAC,WAAW;AApE/B;AAqEQ,gBAAM,WAAU,kBAAO,OAAO,EAAE,MAAhB,YAAqB,OAAO,iBAA5B,YAA4C;AAC5D,gBAAM,aAAa,OAAO,QAAQ,SAAS;AAE3C,gBAAM,WACJ,OAAO,SAAS,aACZ,eACA,OAAO,OAAO,SAAS,aACrB,OAAO,OACP;AAER,iBACE,qBAAC,gBACC;AAAA,gCAAC,uBAAoB,SAAO,MAC1B;AAAA,cAAC;AAAA;AAAA,gBACC,SAAQ;AAAA,gBACR,MAAK;AAAA,gBACL,WAAW;AAAA,kBACT;AAAA,kBACA,YAAY,YAAY,aAAa;AAAA,gBACvC;AAAA,gBAEC;AAAA,6BACC,oBAAC,YAAS,WAAU,qCAAoC,IACtD;AAAA,kBACH,OAAO;AAAA,kBAAM;AAAA,kBAAG;AAAA,kBACjB,oBAAC,mBAAgB,WAAU,0BAAyB;AAAA;AAAA;AAAA,YACtD,GACF;AAAA,YACA,oBAAC,uBAAoB,OAAM,SACxB,uBACG,OAAO,QAAQ,IAAI,CAAC,WAClB;AAAA,cAAC;AAAA;AAAA,gBAEC,SAAS,YAAY;AAAA,gBACrB,iBAAiB,MAAM,SAAS,OAAO,IAAI,MAAM;AAAA,gBAEhD;AAAA;AAAA,cAJI;AAAA,YAKP,CACD,IACD,OAAO,QAAQ,IAAI,CAAC,WAClB;AAAA,cAAC;AAAA;AAAA,gBAEC,UAAU,MAAM,SAAS,OAAO,IAAI,MAAM;AAAA,gBAEzC;AAAA;AAAA,cAHI;AAAA,YAIP,CACD,GACP;AAAA,eApCiB,OAAO,EAqC1B;AAAA,QAEJ,CAAC;AAAA,QAEA,eACC,oBAAC,SAAI,WAAU,WACb;AAAA,UAAC;AAAA;AAAA,YACC,SAAQ;AAAA,YACR,MAAK;AAAA,YACL,WAAW;AAAA,cACT;AAAA,cACA,YAAY,YAAY,aAAa;AAAA,YACvC;AAAA,YACA,SAAS;AAAA,YACV;AAAA;AAAA,QAED,GACF,IACE;AAAA;AAAA;AAAA,EACN;AAEJ;","names":[]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
interface LinkedEntityCellProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
4
|
+
name: React.ReactNode;
|
|
5
|
+
href?: string;
|
|
6
|
+
subtitle?: React.ReactNode;
|
|
7
|
+
meta?: React.ReactNode;
|
|
8
|
+
icon?: React.ReactNode;
|
|
9
|
+
external?: boolean;
|
|
10
|
+
onNavigate?: () => void;
|
|
11
|
+
}
|
|
12
|
+
declare function LinkedEntityCell({ name, href, subtitle, meta, icon, external, onNavigate, className, ...props }: LinkedEntityCellProps): React.JSX.Element;
|
|
13
|
+
|
|
14
|
+
export { LinkedEntityCell, type LinkedEntityCellProps };
|