@handled-ai/design-system 0.18.1 → 0.18.2
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/chart.d.ts +1 -1
- package/dist/charts/index.d.ts +0 -1
- package/dist/charts/index.js +0 -1
- package/dist/charts/index.js.map +1 -1
- package/dist/charts/pipeline-overview.d.ts +1 -2
- package/dist/charts/pipeline-overview.js +1 -29
- package/dist/charts/pipeline-overview.js.map +1 -1
- package/dist/components/feedback-primitives.d.ts +21 -2
- package/dist/components/feedback-primitives.js +90 -6
- package/dist/components/feedback-primitives.js.map +1 -1
- package/dist/components/insights-filter-bar.d.ts +1 -2
- package/dist/components/insights-filter-bar.js +5 -13
- package/dist/components/insights-filter-bar.js.map +1 -1
- package/dist/components/metric-card.d.ts +1 -14
- package/dist/components/metric-card.js +0 -86
- package/dist/components/metric-card.js.map +1 -1
- package/dist/components/score-why-chips.d.ts +1 -1
- package/dist/components/score-why-chips.js +26 -5
- package/dist/components/score-why-chips.js.map +1 -1
- package/dist/components/signal-priority-popover.d.ts +1 -1
- package/dist/components/signal-priority-popover.js +172 -7
- package/dist/components/signal-priority-popover.js.map +1 -1
- package/dist/index.d.ts +3 -9
- package/dist/index.js +0 -5
- package/dist/index.js.map +1 -1
- package/dist/prototype/index.d.ts +1 -1
- package/dist/prototype/prototype-accounts-view.d.ts +1 -1
- package/dist/prototype/prototype-admin-view.d.ts +1 -1
- package/dist/prototype/prototype-config.d.ts +1 -1
- package/dist/prototype/prototype-inbox-view.d.ts +1 -1
- package/dist/prototype/prototype-inbox-view.js +4 -1
- package/dist/prototype/prototype-inbox-view.js.map +1 -1
- package/dist/prototype/prototype-insights-view.d.ts +1 -1
- package/dist/prototype/prototype-shell.d.ts +1 -1
- package/dist/{signal-priority-popover-DQ_VuHac.d.ts → signal-priority-popover-DWaAMhPI.d.ts} +26 -2
- package/package.json +3 -1
- package/src/charts/index.ts +0 -1
- package/src/charts/pipeline-overview.tsx +1 -38
- package/src/components/__tests__/wit-636-feedback-states.test.tsx +546 -0
- package/src/components/feedback-primitives.tsx +148 -26
- package/src/components/insights-filter-bar.tsx +4 -13
- package/src/components/metric-card.tsx +0 -82
- package/src/components/score-why-chips.tsx +28 -2
- package/src/components/signal-priority-popover.tsx +194 -3
- package/src/index.ts +1 -6
- package/src/prototype/prototype-config.ts +11 -1
- package/src/prototype/prototype-inbox-view.tsx +3 -0
- package/dist/charts/empty-chart-state.d.ts +0 -11
- package/dist/charts/empty-chart-state.js +0 -70
- package/dist/charts/empty-chart-state.js.map +0 -1
- package/dist/components/days-open-cell.d.ts +0 -16
- package/dist/components/days-open-cell.js +0 -73
- package/dist/components/days-open-cell.js.map +0 -1
- package/dist/components/detail-drawer.d.ts +0 -16
- package/dist/components/detail-drawer.js +0 -45
- package/dist/components/detail-drawer.js.map +0 -1
- package/dist/components/linked-entity-cell.d.ts +0 -14
- package/dist/components/linked-entity-cell.js +0 -96
- package/dist/components/linked-entity-cell.js.map +0 -1
- package/dist/components/pill.d.ts +0 -26
- package/dist/components/pill.js +0 -77
- package/dist/components/pill.js.map +0 -1
- package/dist/components/quick-segment.d.ts +0 -13
- package/dist/components/quick-segment.js +0 -96
- package/dist/components/quick-segment.js.map +0 -1
- package/src/charts/__tests__/insights-charts.test.tsx +0 -62
- package/src/charts/empty-chart-state.tsx +0 -44
- package/src/components/__tests__/insights-primitives.test.tsx +0 -117
- package/src/components/days-open-cell.tsx +0 -50
- package/src/components/detail-drawer.tsx +0 -60
- package/src/components/linked-entity-cell.tsx +0 -74
- package/src/components/pill.tsx +0 -67
- package/src/components/quick-segment.tsx +0 -68
|
@@ -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
|
|
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 gap-3 rounded-md border border-border bg-card p-4 shadow-sm\",\n className\n )}\n >\n <div className=\"flex items-center gap-2\">\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=\"h-8 gap-1.5 text-xs font-normal shadow-none\"\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=\"h-8 text-xs text-destructive hover:text-destructive\"\n onClick={onClearAll}\n >\n Clear All\n </Button>\n </div>\n ) : null}\n </div>\n )\n}\n\nexport { InsightsFilterBar }\n"],"mappings":";AA0DM,SACE,KADF;AAvDN,SAAS,cAAc,iBAAiB,kBAAkB;AAE1D,SAAS,UAAU;AACnB,SAAS,cAAc;AACvB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAkBP,SAAS,mBACP,SACA,QACA;AACA,SAAO,QAAQ,KAAK,CAAC,WAAW;AAnClC;AAoCI,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;AACF,GAA2B;AACzB,QAAM,eAAe,cAAc,mBAAmB,SAAS,MAAM;AAErE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MAEA;AAAA,6BAAC,SAAI,WAAU,2BACb;AAAA,8BAAC,cAAW,WAAU,iCAAgC;AAAA,UACtD,oBAAC,UAAK,WAAU,6CAA4C,sBAE5D;AAAA,WACF;AAAA,QAEC,QAAQ,IAAI,CAAC,WAAW;AAjE/B;AAkEQ,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,WAAU;AAAA,gBAET;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,eAjCiB,OAAO,EAkC1B;AAAA,QAEJ,CAAC;AAAA,QAEA,eACC,oBAAC,SAAI,WAAU,WACb;AAAA,UAAC;AAAA;AAAA,YACC,SAAQ;AAAA,YACR,MAAK;AAAA,YACL,WAAU;AAAA,YACV,SAAS;AAAA,YACV;AAAA;AAAA,QAED,GACF,IACE;AAAA;AAAA;AAAA,EACN;AAEJ;","names":[]}
|
|
@@ -20,19 +20,6 @@ interface MetricCardProps {
|
|
|
20
20
|
showExternalLink?: boolean;
|
|
21
21
|
showInfo?: boolean;
|
|
22
22
|
}
|
|
23
|
-
interface KpiStripItem {
|
|
24
|
-
id?: string;
|
|
25
|
-
label: React.ReactNode;
|
|
26
|
-
value: React.ReactNode;
|
|
27
|
-
unit?: React.ReactNode;
|
|
28
|
-
subtitle?: React.ReactNode;
|
|
29
|
-
change?: MetricCardProps["change"];
|
|
30
|
-
}
|
|
31
|
-
interface KpiStripProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
32
|
-
items: KpiStripItem[];
|
|
33
|
-
columns?: 2 | 3 | 4;
|
|
34
|
-
}
|
|
35
|
-
declare function KpiStrip({ items, columns, className, ...props }: KpiStripProps): React.JSX.Element;
|
|
36
23
|
declare function MetricCard({ title, value, unit, subtitle, change, footerText, dataPoints, showExternalLink, showInfo, }: MetricCardProps): React.JSX.Element;
|
|
37
24
|
|
|
38
|
-
export {
|
|
25
|
+
export { MetricCard, type MetricCardProps, type MetricDataPoint };
|
|
@@ -1,91 +1,6 @@
|
|
|
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
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
33
2
|
import { ArrowUp, ArrowDown, Info, ExternalLink } from "lucide-react";
|
|
34
3
|
import { cn } from "../lib/utils.js";
|
|
35
|
-
function KpiStrip(_a) {
|
|
36
|
-
var _b = _a, { items, columns = 4, className } = _b, props = __objRest(_b, ["items", "columns", "className"]);
|
|
37
|
-
return /* @__PURE__ */ jsx(
|
|
38
|
-
"div",
|
|
39
|
-
__spreadProps(__spreadValues({
|
|
40
|
-
"data-slot": "kpi-strip",
|
|
41
|
-
className: cn(
|
|
42
|
-
"grid gap-3 rounded-xl border border-border bg-card p-3 shadow-sm",
|
|
43
|
-
columns === 2 && "sm:grid-cols-2",
|
|
44
|
-
columns === 3 && "sm:grid-cols-3",
|
|
45
|
-
columns === 4 && "sm:grid-cols-2 lg:grid-cols-4",
|
|
46
|
-
className
|
|
47
|
-
)
|
|
48
|
-
}, props), {
|
|
49
|
-
children: items.map((item, index) => {
|
|
50
|
-
var _a2, _b2;
|
|
51
|
-
const isGoodDirection = item.change ? item.change.isGood !== void 0 ? item.change.isGood : item.change.direction === "up" : false;
|
|
52
|
-
const ChangeIcon = ((_a2 = item.change) == null ? void 0 : _a2.direction) === "down" ? ArrowDown : ArrowUp;
|
|
53
|
-
return /* @__PURE__ */ jsxs(
|
|
54
|
-
"div",
|
|
55
|
-
{
|
|
56
|
-
"data-slot": "kpi-strip-item",
|
|
57
|
-
className: "min-w-0 rounded-lg bg-muted/40 px-3 py-2",
|
|
58
|
-
children: [
|
|
59
|
-
/* @__PURE__ */ jsx("div", { "data-slot": "kpi-strip-label", className: "truncate text-xs font-medium text-muted-foreground", children: item.label }),
|
|
60
|
-
/* @__PURE__ */ jsxs("div", { className: "mt-1 flex items-baseline gap-1", children: [
|
|
61
|
-
/* @__PURE__ */ jsx("span", { "data-slot": "kpi-strip-value", className: "truncate text-2xl font-bold tracking-tight text-foreground", children: item.value }),
|
|
62
|
-
item.unit ? /* @__PURE__ */ jsx("span", { "data-slot": "kpi-strip-unit", className: "text-sm font-semibold text-muted-foreground", children: item.unit }) : null
|
|
63
|
-
] }),
|
|
64
|
-
item.subtitle || item.change ? /* @__PURE__ */ jsxs("div", { className: "mt-1 flex items-center gap-2 text-xs", children: [
|
|
65
|
-
item.change ? /* @__PURE__ */ jsxs(
|
|
66
|
-
"span",
|
|
67
|
-
{
|
|
68
|
-
"data-slot": "kpi-strip-change",
|
|
69
|
-
className: cn(
|
|
70
|
-
"inline-flex items-center gap-0.5 font-semibold",
|
|
71
|
-
isGoodDirection ? "text-emerald-600" : "text-red-600"
|
|
72
|
-
),
|
|
73
|
-
children: [
|
|
74
|
-
/* @__PURE__ */ jsx(ChangeIcon, { className: "h-3 w-3 stroke-[3]" }),
|
|
75
|
-
item.change.value
|
|
76
|
-
]
|
|
77
|
-
}
|
|
78
|
-
) : null,
|
|
79
|
-
item.subtitle ? /* @__PURE__ */ jsx("span", { "data-slot": "kpi-strip-subtitle", className: "truncate text-muted-foreground", children: item.subtitle }) : null
|
|
80
|
-
] }) : null
|
|
81
|
-
]
|
|
82
|
-
},
|
|
83
|
-
(_b2 = item.id) != null ? _b2 : index
|
|
84
|
-
);
|
|
85
|
-
})
|
|
86
|
-
})
|
|
87
|
-
);
|
|
88
|
-
}
|
|
89
4
|
function MetricCard({
|
|
90
5
|
title,
|
|
91
6
|
value,
|
|
@@ -187,7 +102,6 @@ function MetricCard({
|
|
|
187
102
|
] });
|
|
188
103
|
}
|
|
189
104
|
export {
|
|
190
|
-
KpiStrip,
|
|
191
105
|
MetricCard
|
|
192
106
|
};
|
|
193
107
|
//# sourceMappingURL=metric-card.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/metric-card.tsx"],"sourcesContent":["import * as React from \"react\"\nimport { ArrowUp, ArrowDown, Info, ExternalLink } from \"lucide-react\"\nimport { cn } from \"../lib/utils\"\n\nexport interface MetricDataPoint {\n label: string\n value: number | string\n color?: string\n}\n\nexport interface MetricCardProps {\n title: string\n value?: string | number\n unit?: string\n subtitle?: string\n change?: { \n value: string\n direction: \"up\" | \"down\" | \"neutral\"\n isGood?: boolean // if true, up is green. if false, up is red (e.g. churn).\n }\n footerText?: string\n dataPoints?: MetricDataPoint[]\n showExternalLink?: boolean\n showInfo?: boolean\n}\n\nexport interface KpiStripItem {\n id?: string\n label: React.ReactNode\n value: React.ReactNode\n unit?: React.ReactNode\n subtitle?: React.ReactNode\n change?: MetricCardProps[\"change\"]\n}\n\nexport interface KpiStripProps extends React.HTMLAttributes<HTMLDivElement> {\n items: KpiStripItem[]\n columns?: 2 | 3 | 4\n}\n\nexport function KpiStrip({ items, columns = 4, className, ...props }: KpiStripProps) {\n return (\n <div\n data-slot=\"kpi-strip\"\n className={cn(\n \"grid gap-3 rounded-xl border border-border bg-card p-3 shadow-sm\",\n columns === 2 && \"sm:grid-cols-2\",\n columns === 3 && \"sm:grid-cols-3\",\n columns === 4 && \"sm:grid-cols-2 lg:grid-cols-4\",\n className\n )}\n {...props}\n >\n {items.map((item, index) => {\n const isGoodDirection = item.change\n ? item.change.isGood !== undefined\n ? item.change.isGood\n : item.change.direction === \"up\"\n : false\n const ChangeIcon = item.change?.direction === \"down\" ? ArrowDown : ArrowUp\n\n return (\n <div\n key={item.id ?? index}\n data-slot=\"kpi-strip-item\"\n className=\"min-w-0 rounded-lg bg-muted/40 px-3 py-2\"\n >\n <div data-slot=\"kpi-strip-label\" className=\"truncate text-xs font-medium text-muted-foreground\">\n {item.label}\n </div>\n <div className=\"mt-1 flex items-baseline gap-1\">\n <span data-slot=\"kpi-strip-value\" className=\"truncate text-2xl font-bold tracking-tight text-foreground\">\n {item.value}\n </span>\n {item.unit ? (\n <span data-slot=\"kpi-strip-unit\" className=\"text-sm font-semibold text-muted-foreground\">\n {item.unit}\n </span>\n ) : null}\n </div>\n {item.subtitle || item.change ? (\n <div className=\"mt-1 flex items-center gap-2 text-xs\">\n {item.change ? (\n <span\n data-slot=\"kpi-strip-change\"\n className={cn(\n \"inline-flex items-center gap-0.5 font-semibold\",\n isGoodDirection ? \"text-emerald-600\" : \"text-red-600\"\n )}\n >\n <ChangeIcon className=\"h-3 w-3 stroke-[3]\" />\n {item.change.value}\n </span>\n ) : null}\n {item.subtitle ? (\n <span data-slot=\"kpi-strip-subtitle\" className=\"truncate text-muted-foreground\">\n {item.subtitle}\n </span>\n ) : null}\n </div>\n ) : null}\n </div>\n )\n })}\n </div>\n )\n}\n\nexport function MetricCard({\n title,\n value,\n unit,\n subtitle,\n change,\n footerText,\n dataPoints,\n showExternalLink,\n showInfo = true,\n}: MetricCardProps) {\n // SVG Donut Chart logic for variants with dataPoints\n const renderDonut = () => {\n if (!dataPoints || dataPoints.length === 0 || value === undefined) return null\n\n // Simple pseudo-donut chart logic assuming specific colors from the image\n // In a real prod environment we'd use recharts/visx, but for this standalone component\n // we can draw an SVG circle with stroke-dasharray based on the data\n const size = 80\n const strokeWidth = 12\n const radius = (size - strokeWidth) / 2\n const circumference = 2 * Math.PI * radius\n \n // Calculate total to distribute the circle\n const total = dataPoints.reduce((sum, dp) => sum + (typeof dp.value === 'number' ? dp.value : 0), 0)\n let currentOffset = 0\n\n return (\n <div className=\"relative\" style={{ width: size, height: size }}>\n <svg width={size} height={size} className=\"transform -rotate-90\">\n {dataPoints.map((dp, i) => {\n const val = typeof dp.value === 'number' ? dp.value : 0\n const percentage = val / total\n const strokeLength = percentage * circumference\n const offset = currentOffset\n currentOffset += strokeLength\n\n // Fallback colors matching the image's teal/green palette\n const colors = [\"#166534\", \"#22c55e\", \"#6ee7b7\", \"#ccfbf1\", \"#f1f5f9\"]\n const color = dp.color || colors[i % colors.length]\n\n return (\n <circle\n key={dp.label}\n cx={size / 2}\n cy={size / 2}\n r={radius}\n fill=\"none\"\n stroke={color}\n strokeWidth={strokeWidth}\n strokeDasharray={`${Math.max(strokeLength - 2, 0)} ${circumference}`}\n strokeDashoffset={-offset}\n className=\"transition-all duration-300\"\n />\n )\n })}\n </svg>\n <div className=\"absolute inset-0 flex items-center justify-center flex-col\">\n <span className=\"text-xl font-bold text-foreground leading-none\">{value}</span>\n </div>\n </div>\n )\n }\n\n return (\n <div className=\"flex flex-col rounded-xl border border-border bg-card p-5 shadow-sm h-full w-full\">\n <div className={cn(\"flex justify-between items-start\", title ? \"mb-4\" : \"mb-4\")}>\n {title ? (\n <h3 className=\"font-semibold text-sm text-foreground/80\">{title}</h3>\n ) : (\n <div className=\"flex flex-col\">\n <div className=\"flex items-baseline gap-1\">\n <span className=\"text-3xl font-bold tracking-tight text-foreground\">{value}</span>\n {unit && <span className=\"text-2xl font-bold tracking-tight text-foreground\">{unit}</span>}\n </div>\n {subtitle && (\n <p className=\"text-sm font-medium text-muted-foreground mt-2\">{subtitle}</p>\n )}\n </div>\n )}\n <div className=\"flex items-center gap-1.5 text-muted-foreground shrink-0 mt-0.5\">\n {showExternalLink && <ExternalLink className=\"w-3.5 h-3.5 cursor-pointer hover:text-foreground transition-colors\" />}\n {showInfo && <Info className=\"w-3.5 h-3.5 cursor-pointer hover:text-foreground transition-colors\" />}\n </div>\n </div>\n\n <div className=\"flex-1 flex flex-col min-w-0\">\n {dataPoints && dataPoints.length > 0 ? (\n // Donut Chart Variant\n <div className=\"flex items-center gap-4 mt-2 mb-6\">\n <div className=\"shrink-0\">\n {renderDonut()}\n </div>\n <div className=\"flex flex-col gap-2 flex-1 min-w-0\">\n {dataPoints.slice(0, 5).map((dp, i) => {\n const colors = [\"bg-[#166534]\", \"bg-[#22c55e]\", \"bg-[#6ee7b7]\", \"bg-[#ccfbf1]\", \"bg-[#f1f5f9]\"]\n return (\n <div key={dp.label} className=\"flex items-center justify-between gap-2 text-[11px] font-medium min-w-0\">\n <div className=\"flex items-center gap-1.5 text-muted-foreground min-w-0\">\n <div className={cn(\"w-1.5 h-1.5 rounded-full shrink-0\", dp.color ? \"\" : colors[i % colors.length])} style={dp.color ? { backgroundColor: dp.color } : {}} />\n <span className=\"whitespace-nowrap\">{dp.label}</span>\n </div>\n <span className=\"text-foreground font-semibold shrink-0\">{dp.value}</span>\n </div>\n )\n })}\n </div>\n </div>\n ) : title && (\n // Standard Big Number Variant (only if title exists)\n <div className=\"mb-6\">\n <div className=\"flex items-baseline gap-1\">\n <span className=\"text-4xl font-bold tracking-tight text-foreground\">{value}</span>\n {unit && <span className=\"text-2xl font-bold tracking-tight text-foreground\">{unit}</span>}\n </div>\n {subtitle && (\n <p className=\"text-sm font-medium text-muted-foreground mt-1\">{subtitle}</p>\n )}\n </div>\n )}\n\n {/* Footer section (Change indicator & extra text) */}\n <div className=\"mt-auto flex flex-col gap-1.5\">\n {change && (\n <div className=\"flex items-center gap-1\">\n {(() => {\n // Determine color based on isGood property\n // By default, up is green (good), down is red (bad)\n const isGoodDirection = change.isGood !== undefined \n ? change.isGood \n : change.direction === \"up\";\n \n const colorClass = isGoodDirection ? \"text-emerald-600\" : \"text-red-600\";\n const Icon = change.direction === \"down\" ? ArrowDown : ArrowUp;\n \n return (\n <span className={cn(\"text-xs font-semibold flex items-center gap-0.5\", colorClass)}>\n <Icon className=\"w-3 h-3 stroke-[3]\" />\n {change.value}\n </span>\n )\n })()}\n </div>\n )}\n {footerText && (\n <span className=\"text-[11px] text-muted-foreground font-medium\">{footerText}</span>\n )}\n </div>\n </div>\n </div>\n )\n}\n"],"mappings":"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","names":["_a","_b"]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/metric-card.tsx"],"sourcesContent":["import * as React from \"react\"\nimport { ArrowUp, ArrowDown, Info, ExternalLink } from \"lucide-react\"\nimport { cn } from \"../lib/utils\"\n\nexport interface MetricDataPoint {\n label: string\n value: number | string\n color?: string\n}\n\nexport interface MetricCardProps {\n title: string\n value?: string | number\n unit?: string\n subtitle?: string\n change?: { \n value: string\n direction: \"up\" | \"down\" | \"neutral\"\n isGood?: boolean // if true, up is green. if false, up is red (e.g. churn).\n }\n footerText?: string\n dataPoints?: MetricDataPoint[]\n showExternalLink?: boolean\n showInfo?: boolean\n}\n\nexport function MetricCard({\n title,\n value,\n unit,\n subtitle,\n change,\n footerText,\n dataPoints,\n showExternalLink,\n showInfo = true,\n}: MetricCardProps) {\n // SVG Donut Chart logic for variants with dataPoints\n const renderDonut = () => {\n if (!dataPoints || dataPoints.length === 0 || value === undefined) return null\n\n // Simple pseudo-donut chart logic assuming specific colors from the image\n // In a real prod environment we'd use recharts/visx, but for this standalone component\n // we can draw an SVG circle with stroke-dasharray based on the data\n const size = 80\n const strokeWidth = 12\n const radius = (size - strokeWidth) / 2\n const circumference = 2 * Math.PI * radius\n \n // Calculate total to distribute the circle\n const total = dataPoints.reduce((sum, dp) => sum + (typeof dp.value === 'number' ? dp.value : 0), 0)\n let currentOffset = 0\n\n return (\n <div className=\"relative\" style={{ width: size, height: size }}>\n <svg width={size} height={size} className=\"transform -rotate-90\">\n {dataPoints.map((dp, i) => {\n const val = typeof dp.value === 'number' ? dp.value : 0\n const percentage = val / total\n const strokeLength = percentage * circumference\n const offset = currentOffset\n currentOffset += strokeLength\n\n // Fallback colors matching the image's teal/green palette\n const colors = [\"#166534\", \"#22c55e\", \"#6ee7b7\", \"#ccfbf1\", \"#f1f5f9\"]\n const color = dp.color || colors[i % colors.length]\n\n return (\n <circle\n key={dp.label}\n cx={size / 2}\n cy={size / 2}\n r={radius}\n fill=\"none\"\n stroke={color}\n strokeWidth={strokeWidth}\n strokeDasharray={`${Math.max(strokeLength - 2, 0)} ${circumference}`}\n strokeDashoffset={-offset}\n className=\"transition-all duration-300\"\n />\n )\n })}\n </svg>\n <div className=\"absolute inset-0 flex items-center justify-center flex-col\">\n <span className=\"text-xl font-bold text-foreground leading-none\">{value}</span>\n </div>\n </div>\n )\n }\n\n return (\n <div className=\"flex flex-col rounded-xl border border-border bg-card p-5 shadow-sm h-full w-full\">\n <div className={cn(\"flex justify-between items-start\", title ? \"mb-4\" : \"mb-4\")}>\n {title ? (\n <h3 className=\"font-semibold text-sm text-foreground/80\">{title}</h3>\n ) : (\n <div className=\"flex flex-col\">\n <div className=\"flex items-baseline gap-1\">\n <span className=\"text-3xl font-bold tracking-tight text-foreground\">{value}</span>\n {unit && <span className=\"text-2xl font-bold tracking-tight text-foreground\">{unit}</span>}\n </div>\n {subtitle && (\n <p className=\"text-sm font-medium text-muted-foreground mt-2\">{subtitle}</p>\n )}\n </div>\n )}\n <div className=\"flex items-center gap-1.5 text-muted-foreground shrink-0 mt-0.5\">\n {showExternalLink && <ExternalLink className=\"w-3.5 h-3.5 cursor-pointer hover:text-foreground transition-colors\" />}\n {showInfo && <Info className=\"w-3.5 h-3.5 cursor-pointer hover:text-foreground transition-colors\" />}\n </div>\n </div>\n\n <div className=\"flex-1 flex flex-col min-w-0\">\n {dataPoints && dataPoints.length > 0 ? (\n // Donut Chart Variant\n <div className=\"flex items-center gap-4 mt-2 mb-6\">\n <div className=\"shrink-0\">\n {renderDonut()}\n </div>\n <div className=\"flex flex-col gap-2 flex-1 min-w-0\">\n {dataPoints.slice(0, 5).map((dp, i) => {\n const colors = [\"bg-[#166534]\", \"bg-[#22c55e]\", \"bg-[#6ee7b7]\", \"bg-[#ccfbf1]\", \"bg-[#f1f5f9]\"]\n return (\n <div key={dp.label} className=\"flex items-center justify-between gap-2 text-[11px] font-medium min-w-0\">\n <div className=\"flex items-center gap-1.5 text-muted-foreground min-w-0\">\n <div className={cn(\"w-1.5 h-1.5 rounded-full shrink-0\", dp.color ? \"\" : colors[i % colors.length])} style={dp.color ? { backgroundColor: dp.color } : {}} />\n <span className=\"whitespace-nowrap\">{dp.label}</span>\n </div>\n <span className=\"text-foreground font-semibold shrink-0\">{dp.value}</span>\n </div>\n )\n })}\n </div>\n </div>\n ) : title && (\n // Standard Big Number Variant (only if title exists)\n <div className=\"mb-6\">\n <div className=\"flex items-baseline gap-1\">\n <span className=\"text-4xl font-bold tracking-tight text-foreground\">{value}</span>\n {unit && <span className=\"text-2xl font-bold tracking-tight text-foreground\">{unit}</span>}\n </div>\n {subtitle && (\n <p className=\"text-sm font-medium text-muted-foreground mt-1\">{subtitle}</p>\n )}\n </div>\n )}\n\n {/* Footer section (Change indicator & extra text) */}\n <div className=\"mt-auto flex flex-col gap-1.5\">\n {change && (\n <div className=\"flex items-center gap-1\">\n {(() => {\n // Determine color based on isGood property\n // By default, up is green (good), down is red (bad)\n const isGoodDirection = change.isGood !== undefined \n ? change.isGood \n : change.direction === \"up\";\n \n const colorClass = isGoodDirection ? \"text-emerald-600\" : \"text-red-600\";\n const Icon = change.direction === \"down\" ? ArrowDown : ArrowUp;\n \n return (\n <span className={cn(\"text-xs font-semibold flex items-center gap-0.5\", colorClass)}>\n <Icon className=\"w-3 h-3 stroke-[3]\" />\n {change.value}\n </span>\n )\n })()}\n </div>\n )}\n {footerText && (\n <span className=\"text-[11px] text-muted-foreground font-medium\">{footerText}</span>\n )}\n </div>\n </div>\n </div>\n )\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { Q as QueueItem, l as SignalScoreData, o as SignalScoreUrgencyLabel } from '../signal-priority-popover-
|
|
2
|
+
import { Q as QueueItem, l as SignalScoreData, o as SignalScoreUrgencyLabel } from '../signal-priority-popover-DWaAMhPI.js';
|
|
3
3
|
import './feedback-primitives.js';
|
|
4
4
|
import './quick-action-sidebar-nav.js';
|
|
5
5
|
import './quick-action-modal.js';
|
|
@@ -165,6 +165,7 @@ function StructuredSignalRow({ item, bucketKey, signal, tone, onOpenSignalBucket
|
|
|
165
165
|
const IconComponent = resolveIcon(signal.signalTypeName);
|
|
166
166
|
const toneClass = tone ? (_a = SIGNAL_TONE_CLASSES[tone]) != null ? _a : DEFAULT_TONE_CLASS : DEFAULT_TONE_CLASS;
|
|
167
167
|
const isCombined = signal.signalTypeName === "combined_signal" && signal.components && signal.components.length > 0;
|
|
168
|
+
const hasBalance = Boolean(signal.currentBalance || signal.balanceContext);
|
|
168
169
|
const rowContent = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
169
170
|
/* @__PURE__ */ jsx("div", { className: cn("flex h-5 w-5 shrink-0 items-center justify-center rounded", toneClass), children: /* @__PURE__ */ jsx(IconComponent, { className: "h-3 w-3" }) }),
|
|
170
171
|
/* @__PURE__ */ jsx("div", { className: "min-w-0", children: isCombined ? /* @__PURE__ */ jsx(CombinedSignalMiniChips, { components: signal.components }) : /* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-1.5", children: [
|
|
@@ -173,7 +174,24 @@ function StructuredSignalRow({ item, bucketKey, signal, tone, onOpenSignalBucket
|
|
|
173
174
|
] }) }),
|
|
174
175
|
/* @__PURE__ */ jsx("div", { className: "min-w-0", children: /* @__PURE__ */ jsx("span", { className: "block truncate text-xs text-muted-foreground", children: slotValue(signal.counterparty) }) }),
|
|
175
176
|
/* @__PURE__ */ jsx("span", { className: "shrink-0 text-[11px] text-muted-foreground/70", children: slotValue(signal.time) }),
|
|
176
|
-
/* @__PURE__ */ jsx(ChevronRight, { className: "h-3 w-3 shrink-0 text-muted-foreground/60 transition-transform group-hover:translate-x-0.5 group-hover:text-foreground/50" })
|
|
177
|
+
/* @__PURE__ */ jsx(ChevronRight, { className: "h-3 w-3 shrink-0 text-muted-foreground/60 transition-transform group-hover:translate-x-0.5 group-hover:text-foreground/50" }),
|
|
178
|
+
hasBalance && /* @__PURE__ */ jsxs(
|
|
179
|
+
"div",
|
|
180
|
+
{
|
|
181
|
+
className: "col-span-full mt-0.5 text-[10px] text-muted-foreground/70",
|
|
182
|
+
"data-testid": "balance-context-strip",
|
|
183
|
+
children: [
|
|
184
|
+
signal.currentBalance && /* @__PURE__ */ jsxs("span", { children: [
|
|
185
|
+
"Current balance ",
|
|
186
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium text-muted-foreground", children: signal.currentBalance })
|
|
187
|
+
] }),
|
|
188
|
+
signal.balanceContext && /* @__PURE__ */ jsxs("span", { children: [
|
|
189
|
+
signal.currentBalance ? " \xB7 " : "",
|
|
190
|
+
signal.balanceContext
|
|
191
|
+
] })
|
|
192
|
+
]
|
|
193
|
+
}
|
|
194
|
+
)
|
|
177
195
|
] });
|
|
178
196
|
if (signal.id && onOpenSignalBucket) {
|
|
179
197
|
return /* @__PURE__ */ jsx(
|
|
@@ -230,7 +248,7 @@ function hasStructuredData(signal) {
|
|
|
230
248
|
signal.primaryValue || signal.qualifier || signal.counterparty || signal.components && signal.components.length > 0
|
|
231
249
|
);
|
|
232
250
|
}
|
|
233
|
-
function WhyCard({ bucket, signals, item, panelId, onOpenSignalBucket, onBucketFeedback }) {
|
|
251
|
+
function WhyCard({ bucket, signals, item, panelId, onOpenSignalBucket, onBucketFeedback, initialBucketFeedback }) {
|
|
234
252
|
var _a;
|
|
235
253
|
const [showAll, setShowAll] = React.useState(false);
|
|
236
254
|
const [bucketFeedback, setBucketFeedback] = React.useState(null);
|
|
@@ -299,7 +317,9 @@ function WhyCard({ bucket, signals, item, panelId, onOpenSignalBucket, onBucketF
|
|
|
299
317
|
onSubmit: (data) => onBucketFeedback(bucket.key, data),
|
|
300
318
|
negativeChips: BUCKET_NEGATIVE_CHIPS,
|
|
301
319
|
negativePrompt: "Was this bucket useful?",
|
|
302
|
-
positivePrompt: "Thanks! What was useful about this bucket?"
|
|
320
|
+
positivePrompt: "Thanks! What was useful about this bucket?",
|
|
321
|
+
initialFeedback: initialBucketFeedback,
|
|
322
|
+
feedbackKey: bucket.key
|
|
303
323
|
}
|
|
304
324
|
) })
|
|
305
325
|
]
|
|
@@ -312,7 +332,7 @@ function ScoreWhyChips({
|
|
|
312
332
|
onOpenSignalBucket,
|
|
313
333
|
className
|
|
314
334
|
}) {
|
|
315
|
-
var _a;
|
|
335
|
+
var _a, _b;
|
|
316
336
|
const [selectedBucketKey, setSelectedBucketKey] = React.useState(null);
|
|
317
337
|
React.useEffect(() => {
|
|
318
338
|
setSelectedBucketKey(null);
|
|
@@ -352,7 +372,8 @@ function ScoreWhyChips({
|
|
|
352
372
|
item,
|
|
353
373
|
panelId: selectedPanelId,
|
|
354
374
|
onOpenSignalBucket,
|
|
355
|
-
onBucketFeedback: signalData.onBucketFeedback
|
|
375
|
+
onBucketFeedback: signalData.onBucketFeedback,
|
|
376
|
+
initialBucketFeedback: (_b = signalData.initialBucketFeedback) == null ? void 0 : _b[selectedBucket.key]
|
|
356
377
|
}
|
|
357
378
|
)
|
|
358
379
|
] });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/score-why-chips.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport {\n ChevronDown,\n ChevronUp,\n ChevronRight,\n X,\n TrendingDown,\n ArrowUpRight,\n Radar,\n ArrowDownLeft,\n GitMerge,\n Activity,\n} from \"lucide-react\"\nimport type { LucideIcon } from \"lucide-react\"\nimport { FeedbackFooter } from \"./feedback-primitives\"\nimport type { FeedbackChipTree, FeedbackSubmitData } from \"./feedback-primitives\"\nimport { cn } from \"../lib/utils\"\nimport type {\n QueueItem,\n SignalScoreData,\n SignalScoreExplanationBucket,\n SignalScoreExplanationSignal,\n SignalScoreUrgencyLabel,\n} from \"../prototype/prototype-config\"\n\n// ---------------------------------------------------------------------------\n// Constants & helpers\n// ---------------------------------------------------------------------------\n\nexport function getSignalScoreUrgencyLabel(\n score: number,\n providedLabel?: SignalScoreUrgencyLabel,\n): SignalScoreUrgencyLabel {\n if (providedLabel) return providedLabel\n if (score >= 80) return \"Urgent\"\n if (score >= 60) return \"High\"\n if (score >= 35) return \"Medium\"\n return \"Low\"\n}\n\nexport function scoreRangeForUrgency(label: SignalScoreUrgencyLabel): string {\n switch (label) {\n case \"Urgent\":\n return \"80-100\"\n case \"High\":\n return \"60-79\"\n case \"Medium\":\n return \"35-59\"\n case \"Low\":\n return \"0-34\"\n }\n}\n\nfunction makeDomId(...parts: Array<string | undefined>): string {\n return parts\n .filter((part): part is string => Boolean(part))\n .join(\"-\")\n .replace(/[^A-Za-z0-9_-]+/g, \"-\")\n}\n\nfunction bucketHasSignalRows(bucket: SignalScoreExplanationBucket): boolean {\n return (\n (bucket.signals?.length ?? 0) > 0 ||\n (bucket.signalIds?.length ?? 0) > 0 ||\n Boolean(bucket.primarySignalId)\n )\n}\n\nfunction getSignalScoreBuckets(signalData: SignalScoreData): SignalScoreExplanationBucket[] {\n return (signalData.explanationBuckets ?? []).filter(\n (bucket) => bucket.kind !== \"factor\" && bucketHasSignalRows(bucket),\n )\n}\n\nfunction getBucketSignals(bucket: SignalScoreExplanationBucket): SignalScoreExplanationSignal[] {\n if (bucket.signals && bucket.signals.length > 0) return bucket.signals\n\n const signalIds = bucket.signalIds && bucket.signalIds.length > 0 ? bucket.signalIds : bucket.primarySignalId ? [bucket.primarySignalId] : []\n const uniqueSignalIds = Array.from(new Set(signalIds))\n\n return uniqueSignalIds.map((signalId) => ({\n id: signalId,\n label: `${bucket.label} signal`,\n }))\n}\n\n// ---------------------------------------------------------------------------\n// Signal type icon map - keyed by signal type name (Tailwind v4 source scanned)\n// ---------------------------------------------------------------------------\n\nconst SIGNAL_TYPE_ICONS: Record<string, LucideIcon> = {\n treasury_liquidation: TrendingDown,\n cumulative_treasury_outflow: ArrowUpRight,\n test_transaction: Radar,\n micro_deposit: ArrowDownLeft,\n combined_signal: GitMerge,\n}\n\nfunction resolveIcon(iconName?: string): LucideIcon {\n if (!iconName) return Activity\n return SIGNAL_TYPE_ICONS[iconName] ?? Activity\n}\n\n// ---------------------------------------------------------------------------\n// Static tone class maps (REQUIRED for Tailwind v4 source scanning)\n// ---------------------------------------------------------------------------\n\n/** Shared tone-to-class map. Re-exported for signal-priority-popover. */\nexport const SIGNAL_TONE_CLASSES: Record<string, string> = {\n alert: \"bg-red-50 text-red-600\",\n warn: \"bg-amber-50 text-amber-600\",\n info: \"bg-blue-50 text-blue-600\",\n}\n\n/** Default tone for missing/unknown tone values */\nexport const DEFAULT_TONE_CLASS = \"bg-muted text-muted-foreground\"\n\n// ---------------------------------------------------------------------------\n// Em-dash fallback for missing slot data\n// ---------------------------------------------------------------------------\n\nfunction slotValue(value: string | null | undefined): string {\n return value && value.trim().length > 0 ? value : \"\"\n}\n\n// ---------------------------------------------------------------------------\n// Bucket feedback chip config\n// ---------------------------------------------------------------------------\n\nconst BUCKET_NEGATIVE_CHIPS: FeedbackChipTree[] = [\n {\n label: \"Not relevant for this account\",\n subPrompt: \"Why isn't it relevant?\",\n subChips: [\n \"Business as usual for this account\",\n \"Account in maintenance mode\",\n \"Wrong contact for this signal\",\n \"Other\",\n ],\n },\n { label: \"Bad timing\" },\n {\n label: \"Inaccurate data\",\n subPrompt: \"Which field?\",\n subChips: [\"Balance figures\", \"Counterparty\", \"Timestamp\", \"Other\"],\n },\n { label: \"Wrong account\" },\n { label: \"Already handled\" },\n { label: \"Other\" },\n]\n\n// ---------------------------------------------------------------------------\n// Default visible row count for long lists\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_VISIBLE_ROWS = 8\n\n// ---------------------------------------------------------------------------\n// WhyPill - Bucket toggle button with icon, count badge, chevron, close\n// ---------------------------------------------------------------------------\n\ninterface WhyPillProps {\n bucket: SignalScoreExplanationBucket\n isSelected: boolean\n signalCount: number\n panelId: string\n onToggle: () => void\n onClose: () => void\n}\n\nfunction WhyPill({ bucket, isSelected, signalCount, panelId, onToggle, onClose }: WhyPillProps) {\n const IconComponent = resolveIcon(bucket.icon)\n\n const sharedClasses = cn(\n \"inline-flex items-center text-[11px] font-semibold transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n isSelected\n ? \"border-border bg-muted text-foreground\"\n : \"border-border bg-background text-muted-foreground hover:bg-muted/60 hover:text-foreground\",\n )\n\n return (\n <div className=\"inline-flex h-[26px] items-stretch\">\n <button\n type=\"button\"\n onClick={onToggle}\n aria-expanded={isSelected}\n aria-controls={panelId}\n className={cn(\n sharedClasses,\n \"gap-1.5 rounded-lg border px-2.5 py-1\",\n isSelected && \"rounded-b-none rounded-r-none border-r-0\",\n )}\n >\n <IconComponent className=\"h-3 w-3 shrink-0\" />\n {bucket.label}\n {signalCount > 1 && (\n <span className={cn(\"rounded-full px-1.5 py-0 text-[10px]\", isSelected ? \"bg-background/60\" : \"bg-muted\")}>\n x{signalCount}\n </span>\n )}\n {isSelected ? (\n <ChevronUp className=\"h-3 w-3 shrink-0\" />\n ) : (\n <ChevronDown className=\"h-3 w-3 shrink-0\" />\n )}\n </button>\n {isSelected && (\n <button\n type=\"button\"\n aria-label={`Close ${bucket.label}`}\n onClick={onClose}\n className={cn(\n sharedClasses,\n \"rounded-lg rounded-b-none rounded-l-none border border-l-0 border-border px-1.5 py-1 hover:bg-background/60\",\n )}\n >\n <X className=\"h-3 w-3\" />\n </button>\n )}\n </div>\n )\n}\n\n// ---------------------------------------------------------------------------\n// CombinedSignalMiniChips - renders component type chips for combined signals\n// ---------------------------------------------------------------------------\n\ninterface CombinedSignalMiniChipsProps {\n components: Array<{ type: string; count: number }>\n}\n\nfunction CombinedSignalMiniChips({ components }: CombinedSignalMiniChipsProps) {\n return (\n <div className=\"flex flex-wrap items-center gap-1\">\n {components.map((comp, idx) => {\n const CompIcon = resolveIcon(comp.type)\n return (\n <React.Fragment key={comp.type}>\n {idx > 0 && <span className=\"text-[10px] text-muted-foreground/60\">+</span>}\n <span className=\"inline-flex items-center gap-0.5 rounded bg-muted px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground\">\n <CompIcon className=\"h-2.5 w-2.5 shrink-0\" />\n {comp.type.replace(/_/g, \" \")} x{comp.count}\n </span>\n </React.Fragment>\n )\n })}\n </div>\n )\n}\n\n// ---------------------------------------------------------------------------\n// StructuredSignalRow - CSS grid slot grammar signal row\n// ---------------------------------------------------------------------------\n\ninterface StructuredSignalRowProps {\n item: QueueItem\n bucketKey: string\n signal: SignalScoreExplanationSignal\n tone?: \"alert\" | \"warn\" | \"info\"\n onOpenSignalBucket?: ScoreWhyChipsProps[\"onOpenSignalBucket\"]\n}\n\nfunction StructuredSignalRow({ item, bucketKey, signal, tone, onOpenSignalBucket }: StructuredSignalRowProps) {\n const IconComponent = resolveIcon(signal.signalTypeName)\n const toneClass = tone ? (SIGNAL_TONE_CLASSES[tone] ?? DEFAULT_TONE_CLASS) : DEFAULT_TONE_CLASS\n const isCombined = signal.signalTypeName === \"combined_signal\" && signal.components && signal.components.length > 0\n\n const rowContent = (\n <>\n {/* Slot 1: Icon */}\n <div className={cn(\"flex h-5 w-5 shrink-0 items-center justify-center rounded\", toneClass)}>\n <IconComponent className=\"h-3 w-3\" />\n </div>\n\n {/* Slot 2: Primary value + qualifier */}\n <div className=\"min-w-0\">\n {isCombined ? (\n <CombinedSignalMiniChips components={signal.components!} />\n ) : (\n <div className=\"flex items-baseline gap-1.5\">\n <span className=\"text-sm font-semibold tabular-nums text-foreground\">\n {slotValue(signal.primaryValue)}\n </span>\n <span className=\"text-xs text-muted-foreground\">\n {slotValue(signal.qualifier)}\n </span>\n </div>\n )}\n </div>\n\n {/* Slot 3: Counterparty */}\n <div className=\"min-w-0\">\n <span className=\"block truncate text-xs text-muted-foreground\">\n {slotValue(signal.counterparty)}\n </span>\n </div>\n\n {/* Slot 4: Time */}\n <span className=\"shrink-0 text-[11px] text-muted-foreground/70\">\n {slotValue(signal.time)}\n </span>\n\n {/* Slot 5: Chevron */}\n <ChevronRight className=\"h-3 w-3 shrink-0 text-muted-foreground/60 transition-transform group-hover:translate-x-0.5 group-hover:text-foreground/50\" />\n </>\n )\n\n if (signal.id && onOpenSignalBucket) {\n return (\n <button\n type=\"button\"\n className=\"group grid w-full cursor-pointer items-center gap-x-3 gap-y-1 rounded-md px-3 py-2 text-left transition-colors hover:bg-muted/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n style={{ gridTemplateColumns: \"20px minmax(0,1fr) minmax(0,1fr) auto 16px\" }}\n onClick={() => onOpenSignalBucket({ item, bucketKey, signalId: signal.id! })}\n >\n {rowContent}\n </button>\n )\n }\n\n return (\n <div\n className=\"grid w-full items-center gap-x-3 gap-y-1 rounded-md px-3 py-2\"\n style={{ gridTemplateColumns: \"20px minmax(0,1fr) minmax(0,1fr) auto 16px\" }}\n >\n {rowContent}\n </div>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Legacy SignalRow (for signals without structured data)\n// ---------------------------------------------------------------------------\n\ninterface SignalRowProps {\n item: QueueItem\n bucketKey: string\n signal: SignalScoreExplanationSignal\n onOpenSignalBucket?: ScoreWhyChipsProps[\"onOpenSignalBucket\"]\n}\n\nfunction LegacySignalRow({ item, bucketKey, signal, onOpenSignalBucket }: SignalRowProps) {\n const isClickable = !!(signal.id && onOpenSignalBucket)\n const rowContent = (\n <>\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"min-w-0 flex-1\">\n <p className=\"font-medium text-foreground\">{signal.label}</p>\n {signal.description ? <p className=\"mt-1 leading-relaxed text-muted-foreground\">{signal.description}</p> : null}\n {(signal.source || signal.metric) && (\n <div className=\"mt-1.5 flex flex-wrap gap-1.5 text-[11px] text-muted-foreground/80\">\n {signal.source ? <span className=\"rounded-full bg-muted px-2 py-0.5\">{signal.source}</span> : null}\n {signal.metric ? <span className=\"rounded-full bg-muted px-2 py-0.5\">{signal.metric}</span> : null}\n </div>\n )}\n </div>\n <div className=\"flex shrink-0 items-center gap-2\">\n {signal.time ? <span className=\"text-[11px] text-muted-foreground/70\">{signal.time}</span> : null}\n {isClickable && (\n <ChevronRight className=\"h-3 w-3 text-muted-foreground/60 transition-transform group-hover:translate-x-0.5 group-hover:text-foreground/50\" />\n )}\n </div>\n </div>\n </>\n )\n\n if (isClickable) {\n return (\n <button\n type=\"button\"\n className=\"group w-full cursor-pointer rounded-md bg-background/80 p-3 text-left text-xs transition-colors hover:bg-muted/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n onClick={() => onOpenSignalBucket!({ item, bucketKey, signalId: signal.id! })}\n >\n {rowContent}\n </button>\n )\n }\n\n return <div className=\"rounded-md bg-background/80 p-3 text-xs\">{rowContent}</div>\n}\n\n/**\n * Determine whether a signal has structured slot data.\n * If it has primaryValue, counterparty, qualifier, or components, use the structured row.\n */\nfunction hasStructuredData(signal: SignalScoreExplanationSignal): boolean {\n return Boolean(\n signal.primaryValue ||\n signal.qualifier ||\n signal.counterparty ||\n (signal.components && signal.components.length > 0),\n )\n}\n\n// ---------------------------------------------------------------------------\n// WhyCard - Expanded panel under a pill\n// ---------------------------------------------------------------------------\n\ninterface WhyCardProps {\n bucket: SignalScoreExplanationBucket\n signals: SignalScoreExplanationSignal[]\n item: QueueItem\n panelId: string\n onOpenSignalBucket?: ScoreWhyChipsProps[\"onOpenSignalBucket\"]\n onBucketFeedback?: (bucketKey: string, data: FeedbackSubmitData) => void\n}\n\nfunction WhyCard({ bucket, signals, item, panelId, onOpenSignalBucket, onBucketFeedback }: WhyCardProps) {\n const [showAll, setShowAll] = React.useState(false)\n const [bucketFeedback, setBucketFeedback] = React.useState<\"positive\" | \"negative\" | null>(null)\n const totalCount = bucket.signalCount ?? signals.length\n const visibleSignals = showAll ? signals : signals.slice(0, DEFAULT_VISIBLE_ROWS)\n const hiddenCount = signals.length - DEFAULT_VISIBLE_ROWS\n\n // Determine whether to use structured rows (any signal has structured data)\n const useStructured = signals.some(hasStructuredData)\n\n return (\n <div\n id={panelId}\n className=\"rounded-lg rounded-t-none border border-t-0 border-border bg-muted/20 p-3\"\n role=\"region\"\n aria-label={`${bucket.label} details`}\n >\n {/* Card header */}\n <div className=\"mb-2 flex items-center justify-between\">\n <span className=\"text-[10px] font-bold uppercase tracking-wider text-muted-foreground\">\n {totalCount} signal{totalCount !== 1 ? \"s\" : \"\"} – {bucket.label}\n </span>\n </div>\n\n {/* Signal rows */}\n {visibleSignals.length > 0 ? (\n <ul className=\"divide-y divide-border/30\" aria-label=\"Matching signals\">\n {visibleSignals.map((signal, index) => (\n <li key={signal.id ?? `${bucket.key}-signal-${index}`}>\n {useStructured ? (\n <StructuredSignalRow\n item={item}\n bucketKey={bucket.key}\n signal={signal}\n tone={bucket.tone}\n onOpenSignalBucket={onOpenSignalBucket}\n />\n ) : (\n <LegacySignalRow\n item={item}\n bucketKey={bucket.key}\n signal={signal}\n onOpenSignalBucket={onOpenSignalBucket}\n />\n )}\n </li>\n ))}\n </ul>\n ) : bucket.evidence && bucket.evidence.length > 0 ? (\n <ul className=\"mt-3 space-y-1.5\" aria-label=\"Matching signals\">\n {bucket.evidence.map((evidence, index) => (\n <li key={`${bucket.key}-evidence-${index}`} className=\"flex gap-2 text-xs text-muted-foreground\">\n <span className=\"mt-1.5 h-1 w-1 shrink-0 rounded-full bg-primary\" />\n <span className=\"leading-relaxed\">{evidence}</span>\n </li>\n ))}\n </ul>\n ) : null}\n\n {/* \"Show N more\" button */}\n {!showAll && hiddenCount > 0 && (\n <button\n type=\"button\"\n onClick={() => setShowAll(true)}\n className=\"mt-2 flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground\"\n >\n <ChevronDown className=\"h-3 w-3\" />\n Show {hiddenCount} more\n </button>\n )}\n\n {/* Bucket feedback footer */}\n {onBucketFeedback && (\n <div className=\"mt-3 border-t border-border/40 pt-3\">\n <FeedbackFooter\n feedback={bucketFeedback}\n onFeedbackChange={setBucketFeedback}\n onSubmit={(data) => onBucketFeedback(bucket.key, data)}\n negativeChips={BUCKET_NEGATIVE_CHIPS}\n negativePrompt=\"Was this bucket useful?\"\n positivePrompt=\"Thanks! What was useful about this bucket?\"\n />\n </div>\n )}\n </div>\n )\n}\n\n// ---------------------------------------------------------------------------\n// ScoreWhyChips - Main export\n// ---------------------------------------------------------------------------\n\nexport interface ScoreWhyChipsProps {\n item: QueueItem\n signalData: SignalScoreData\n onOpenSignalBucket?: (args: { item: QueueItem; bucketKey: string; signalId: string }) => void\n className?: string\n}\n\nexport function ScoreWhyChips({\n item,\n signalData,\n onOpenSignalBucket,\n className,\n}: ScoreWhyChipsProps) {\n const [selectedBucketKey, setSelectedBucketKey] = React.useState<string | null>(null)\n\n React.useEffect(() => {\n setSelectedBucketKey(null)\n }, [item.id])\n\n const reactId = React.useId()\n const idPrefix = makeDomId(\"score-why\", reactId, item.id)\n const buckets = React.useMemo(() => getSignalScoreBuckets(signalData), [signalData])\n const selectedBucket = buckets.find((bucket) => bucket.key === selectedBucketKey) ?? null\n const selectedBucketSignals = selectedBucket ? getBucketSignals(selectedBucket) : []\n const selectedPanelId = selectedBucket ? `${idPrefix}-panel-${makeDomId(selectedBucket.key)}` : undefined\n\n if (buckets.length === 0) return null\n\n return (\n <div className={cn(\"mt-4\", className)}>\n <div className=\"mb-2 flex items-center gap-2\">\n <span className=\"text-[10px] font-bold uppercase tracking-wider text-muted-foreground\">Why</span>\n </div>\n <div className=\"flex flex-wrap gap-1.5\">\n {buckets.map((bucket) => {\n const isSelected = selectedBucketKey === bucket.key\n const panelId = `${idPrefix}-panel-${makeDomId(bucket.key)}`\n const signals = getBucketSignals(bucket)\n const signalCount = bucket.signalCount ?? signals.length\n return (\n <div key={bucket.key} className=\"flex flex-col\">\n <WhyPill\n bucket={bucket}\n isSelected={isSelected}\n signalCount={signalCount}\n panelId={panelId}\n onToggle={() => setSelectedBucketKey((prev) => (prev === bucket.key ? null : bucket.key))}\n onClose={() => setSelectedBucketKey(null)}\n />\n </div>\n )\n })}\n </div>\n\n {selectedBucket && selectedPanelId && (\n <WhyCard\n bucket={selectedBucket}\n signals={selectedBucketSignals}\n item={item}\n panelId={selectedPanelId}\n onOpenSignalBucket={onOpenSignalBucket}\n onBucketFeedback={signalData.onBucketFeedback}\n />\n )}\n </div>\n )\n}\n"],"mappings":"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","names":["_a"]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/score-why-chips.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport {\n ChevronDown,\n ChevronUp,\n ChevronRight,\n X,\n TrendingDown,\n ArrowUpRight,\n Radar,\n ArrowDownLeft,\n GitMerge,\n Activity,\n} from \"lucide-react\"\nimport type { LucideIcon } from \"lucide-react\"\nimport { FeedbackFooter } from \"./feedback-primitives\"\nimport type { FeedbackChipTree, FeedbackSubmitData, PersistedFeedbackData } from \"./feedback-primitives\"\nimport { cn } from \"../lib/utils\"\nimport type {\n QueueItem,\n SignalScoreData,\n SignalScoreExplanationBucket,\n SignalScoreExplanationSignal,\n SignalScoreUrgencyLabel,\n} from \"../prototype/prototype-config\"\n\n// ---------------------------------------------------------------------------\n// Constants & helpers\n// ---------------------------------------------------------------------------\n\nexport function getSignalScoreUrgencyLabel(\n score: number,\n providedLabel?: SignalScoreUrgencyLabel,\n): SignalScoreUrgencyLabel {\n if (providedLabel) return providedLabel\n if (score >= 80) return \"Urgent\"\n if (score >= 60) return \"High\"\n if (score >= 35) return \"Medium\"\n return \"Low\"\n}\n\nexport function scoreRangeForUrgency(label: SignalScoreUrgencyLabel): string {\n switch (label) {\n case \"Urgent\":\n return \"80-100\"\n case \"High\":\n return \"60-79\"\n case \"Medium\":\n return \"35-59\"\n case \"Low\":\n return \"0-34\"\n }\n}\n\nfunction makeDomId(...parts: Array<string | undefined>): string {\n return parts\n .filter((part): part is string => Boolean(part))\n .join(\"-\")\n .replace(/[^A-Za-z0-9_-]+/g, \"-\")\n}\n\nfunction bucketHasSignalRows(bucket: SignalScoreExplanationBucket): boolean {\n return (\n (bucket.signals?.length ?? 0) > 0 ||\n (bucket.signalIds?.length ?? 0) > 0 ||\n Boolean(bucket.primarySignalId)\n )\n}\n\nfunction getSignalScoreBuckets(signalData: SignalScoreData): SignalScoreExplanationBucket[] {\n return (signalData.explanationBuckets ?? []).filter(\n (bucket) => bucket.kind !== \"factor\" && bucketHasSignalRows(bucket),\n )\n}\n\nfunction getBucketSignals(bucket: SignalScoreExplanationBucket): SignalScoreExplanationSignal[] {\n if (bucket.signals && bucket.signals.length > 0) return bucket.signals\n\n const signalIds = bucket.signalIds && bucket.signalIds.length > 0 ? bucket.signalIds : bucket.primarySignalId ? [bucket.primarySignalId] : []\n const uniqueSignalIds = Array.from(new Set(signalIds))\n\n return uniqueSignalIds.map((signalId) => ({\n id: signalId,\n label: `${bucket.label} signal`,\n }))\n}\n\n// ---------------------------------------------------------------------------\n// Signal type icon map - keyed by signal type name (Tailwind v4 source scanned)\n// ---------------------------------------------------------------------------\n\nconst SIGNAL_TYPE_ICONS: Record<string, LucideIcon> = {\n treasury_liquidation: TrendingDown,\n cumulative_treasury_outflow: ArrowUpRight,\n test_transaction: Radar,\n micro_deposit: ArrowDownLeft,\n combined_signal: GitMerge,\n}\n\nfunction resolveIcon(iconName?: string): LucideIcon {\n if (!iconName) return Activity\n return SIGNAL_TYPE_ICONS[iconName] ?? Activity\n}\n\n// ---------------------------------------------------------------------------\n// Static tone class maps (REQUIRED for Tailwind v4 source scanning)\n// ---------------------------------------------------------------------------\n\n/** Shared tone-to-class map. Re-exported for signal-priority-popover. */\nexport const SIGNAL_TONE_CLASSES: Record<string, string> = {\n alert: \"bg-red-50 text-red-600\",\n warn: \"bg-amber-50 text-amber-600\",\n info: \"bg-blue-50 text-blue-600\",\n}\n\n/** Default tone for missing/unknown tone values */\nexport const DEFAULT_TONE_CLASS = \"bg-muted text-muted-foreground\"\n\n// ---------------------------------------------------------------------------\n// Em-dash fallback for missing slot data\n// ---------------------------------------------------------------------------\n\nfunction slotValue(value: string | null | undefined): string {\n return value && value.trim().length > 0 ? value : \"\"\n}\n\n// ---------------------------------------------------------------------------\n// Bucket feedback chip config\n// ---------------------------------------------------------------------------\n\nconst BUCKET_NEGATIVE_CHIPS: FeedbackChipTree[] = [\n {\n label: \"Not relevant for this account\",\n subPrompt: \"Why isn't it relevant?\",\n subChips: [\n \"Business as usual for this account\",\n \"Account in maintenance mode\",\n \"Wrong contact for this signal\",\n \"Other\",\n ],\n },\n { label: \"Bad timing\" },\n {\n label: \"Inaccurate data\",\n subPrompt: \"Which field?\",\n subChips: [\"Balance figures\", \"Counterparty\", \"Timestamp\", \"Other\"],\n },\n { label: \"Wrong account\" },\n { label: \"Already handled\" },\n { label: \"Other\" },\n]\n\n// ---------------------------------------------------------------------------\n// Default visible row count for long lists\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_VISIBLE_ROWS = 8\n\n// ---------------------------------------------------------------------------\n// WhyPill - Bucket toggle button with icon, count badge, chevron, close\n// ---------------------------------------------------------------------------\n\ninterface WhyPillProps {\n bucket: SignalScoreExplanationBucket\n isSelected: boolean\n signalCount: number\n panelId: string\n onToggle: () => void\n onClose: () => void\n}\n\nfunction WhyPill({ bucket, isSelected, signalCount, panelId, onToggle, onClose }: WhyPillProps) {\n const IconComponent = resolveIcon(bucket.icon)\n\n const sharedClasses = cn(\n \"inline-flex items-center text-[11px] font-semibold transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n isSelected\n ? \"border-border bg-muted text-foreground\"\n : \"border-border bg-background text-muted-foreground hover:bg-muted/60 hover:text-foreground\",\n )\n\n return (\n <div className=\"inline-flex h-[26px] items-stretch\">\n <button\n type=\"button\"\n onClick={onToggle}\n aria-expanded={isSelected}\n aria-controls={panelId}\n className={cn(\n sharedClasses,\n \"gap-1.5 rounded-lg border px-2.5 py-1\",\n isSelected && \"rounded-b-none rounded-r-none border-r-0\",\n )}\n >\n <IconComponent className=\"h-3 w-3 shrink-0\" />\n {bucket.label}\n {signalCount > 1 && (\n <span className={cn(\"rounded-full px-1.5 py-0 text-[10px]\", isSelected ? \"bg-background/60\" : \"bg-muted\")}>\n x{signalCount}\n </span>\n )}\n {isSelected ? (\n <ChevronUp className=\"h-3 w-3 shrink-0\" />\n ) : (\n <ChevronDown className=\"h-3 w-3 shrink-0\" />\n )}\n </button>\n {isSelected && (\n <button\n type=\"button\"\n aria-label={`Close ${bucket.label}`}\n onClick={onClose}\n className={cn(\n sharedClasses,\n \"rounded-lg rounded-b-none rounded-l-none border border-l-0 border-border px-1.5 py-1 hover:bg-background/60\",\n )}\n >\n <X className=\"h-3 w-3\" />\n </button>\n )}\n </div>\n )\n}\n\n// ---------------------------------------------------------------------------\n// CombinedSignalMiniChips - renders component type chips for combined signals\n// ---------------------------------------------------------------------------\n\ninterface CombinedSignalMiniChipsProps {\n components: Array<{ type: string; count: number }>\n}\n\nfunction CombinedSignalMiniChips({ components }: CombinedSignalMiniChipsProps) {\n return (\n <div className=\"flex flex-wrap items-center gap-1\">\n {components.map((comp, idx) => {\n const CompIcon = resolveIcon(comp.type)\n return (\n <React.Fragment key={comp.type}>\n {idx > 0 && <span className=\"text-[10px] text-muted-foreground/60\">+</span>}\n <span className=\"inline-flex items-center gap-0.5 rounded bg-muted px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground\">\n <CompIcon className=\"h-2.5 w-2.5 shrink-0\" />\n {comp.type.replace(/_/g, \" \")} x{comp.count}\n </span>\n </React.Fragment>\n )\n })}\n </div>\n )\n}\n\n// ---------------------------------------------------------------------------\n// StructuredSignalRow - CSS grid slot grammar signal row\n// ---------------------------------------------------------------------------\n\ninterface StructuredSignalRowProps {\n item: QueueItem\n bucketKey: string\n signal: SignalScoreExplanationSignal\n tone?: \"alert\" | \"warn\" | \"info\"\n onOpenSignalBucket?: ScoreWhyChipsProps[\"onOpenSignalBucket\"]\n}\n\nfunction StructuredSignalRow({ item, bucketKey, signal, tone, onOpenSignalBucket }: StructuredSignalRowProps) {\n const IconComponent = resolveIcon(signal.signalTypeName)\n const toneClass = tone ? (SIGNAL_TONE_CLASSES[tone] ?? DEFAULT_TONE_CLASS) : DEFAULT_TONE_CLASS\n const isCombined = signal.signalTypeName === \"combined_signal\" && signal.components && signal.components.length > 0\n const hasBalance = Boolean(signal.currentBalance || signal.balanceContext)\n\n const rowContent = (\n <>\n {/* Slot 1: Icon */}\n <div className={cn(\"flex h-5 w-5 shrink-0 items-center justify-center rounded\", toneClass)}>\n <IconComponent className=\"h-3 w-3\" />\n </div>\n\n {/* Slot 2: Primary value + qualifier */}\n <div className=\"min-w-0\">\n {isCombined ? (\n <CombinedSignalMiniChips components={signal.components!} />\n ) : (\n <div className=\"flex items-baseline gap-1.5\">\n <span className=\"text-sm font-semibold tabular-nums text-foreground\">\n {slotValue(signal.primaryValue)}\n </span>\n <span className=\"text-xs text-muted-foreground\">\n {slotValue(signal.qualifier)}\n </span>\n </div>\n )}\n </div>\n\n {/* Slot 3: Counterparty */}\n <div className=\"min-w-0\">\n <span className=\"block truncate text-xs text-muted-foreground\">\n {slotValue(signal.counterparty)}\n </span>\n </div>\n\n {/* Slot 4: Time */}\n <span className=\"shrink-0 text-[11px] text-muted-foreground/70\">\n {slotValue(signal.time)}\n </span>\n\n {/* Slot 5: Chevron */}\n <ChevronRight className=\"h-3 w-3 shrink-0 text-muted-foreground/60 transition-transform group-hover:translate-x-0.5 group-hover:text-foreground/50\" />\n\n {/* Balance context strip — spans full row below grid columns */}\n {hasBalance && (\n <div\n className=\"col-span-full mt-0.5 text-[10px] text-muted-foreground/70\"\n data-testid=\"balance-context-strip\"\n >\n {signal.currentBalance && (\n <span>\n Current balance <span className=\"font-medium text-muted-foreground\">{signal.currentBalance}</span>\n </span>\n )}\n {signal.balanceContext && (\n <span>\n {signal.currentBalance ? \" · \" : \"\"}\n {signal.balanceContext}\n </span>\n )}\n </div>\n )}\n </>\n )\n\n if (signal.id && onOpenSignalBucket) {\n return (\n <button\n type=\"button\"\n className=\"group grid w-full cursor-pointer items-center gap-x-3 gap-y-1 rounded-md px-3 py-2 text-left transition-colors hover:bg-muted/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n style={{ gridTemplateColumns: \"20px minmax(0,1fr) minmax(0,1fr) auto 16px\" }}\n onClick={() => onOpenSignalBucket({ item, bucketKey, signalId: signal.id! })}\n >\n {rowContent}\n </button>\n )\n }\n\n return (\n <div\n className=\"grid w-full items-center gap-x-3 gap-y-1 rounded-md px-3 py-2\"\n style={{ gridTemplateColumns: \"20px minmax(0,1fr) minmax(0,1fr) auto 16px\" }}\n >\n {rowContent}\n </div>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Legacy SignalRow (for signals without structured data)\n// ---------------------------------------------------------------------------\n\ninterface SignalRowProps {\n item: QueueItem\n bucketKey: string\n signal: SignalScoreExplanationSignal\n onOpenSignalBucket?: ScoreWhyChipsProps[\"onOpenSignalBucket\"]\n}\n\nfunction LegacySignalRow({ item, bucketKey, signal, onOpenSignalBucket }: SignalRowProps) {\n const isClickable = !!(signal.id && onOpenSignalBucket)\n const rowContent = (\n <>\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"min-w-0 flex-1\">\n <p className=\"font-medium text-foreground\">{signal.label}</p>\n {signal.description ? <p className=\"mt-1 leading-relaxed text-muted-foreground\">{signal.description}</p> : null}\n {(signal.source || signal.metric) && (\n <div className=\"mt-1.5 flex flex-wrap gap-1.5 text-[11px] text-muted-foreground/80\">\n {signal.source ? <span className=\"rounded-full bg-muted px-2 py-0.5\">{signal.source}</span> : null}\n {signal.metric ? <span className=\"rounded-full bg-muted px-2 py-0.5\">{signal.metric}</span> : null}\n </div>\n )}\n </div>\n <div className=\"flex shrink-0 items-center gap-2\">\n {signal.time ? <span className=\"text-[11px] text-muted-foreground/70\">{signal.time}</span> : null}\n {isClickable && (\n <ChevronRight className=\"h-3 w-3 text-muted-foreground/60 transition-transform group-hover:translate-x-0.5 group-hover:text-foreground/50\" />\n )}\n </div>\n </div>\n </>\n )\n\n if (isClickable) {\n return (\n <button\n type=\"button\"\n className=\"group w-full cursor-pointer rounded-md bg-background/80 p-3 text-left text-xs transition-colors hover:bg-muted/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n onClick={() => onOpenSignalBucket!({ item, bucketKey, signalId: signal.id! })}\n >\n {rowContent}\n </button>\n )\n }\n\n return <div className=\"rounded-md bg-background/80 p-3 text-xs\">{rowContent}</div>\n}\n\n/**\n * Determine whether a signal has structured slot data.\n * If it has primaryValue, counterparty, qualifier, or components, use the structured row.\n */\nfunction hasStructuredData(signal: SignalScoreExplanationSignal): boolean {\n return Boolean(\n signal.primaryValue ||\n signal.qualifier ||\n signal.counterparty ||\n (signal.components && signal.components.length > 0),\n )\n}\n\n// ---------------------------------------------------------------------------\n// WhyCard - Expanded panel under a pill\n// ---------------------------------------------------------------------------\n\ninterface WhyCardProps {\n bucket: SignalScoreExplanationBucket\n signals: SignalScoreExplanationSignal[]\n item: QueueItem\n panelId: string\n onOpenSignalBucket?: ScoreWhyChipsProps[\"onOpenSignalBucket\"]\n onBucketFeedback?: (bucketKey: string, data: FeedbackSubmitData) => void\n /** Persisted bucket-level feedback to hydrate from. */\n initialBucketFeedback?: PersistedFeedbackData | null\n}\n\nfunction WhyCard({ bucket, signals, item, panelId, onOpenSignalBucket, onBucketFeedback, initialBucketFeedback }: WhyCardProps) {\n const [showAll, setShowAll] = React.useState(false)\n const [bucketFeedback, setBucketFeedback] = React.useState<\"positive\" | \"negative\" | null>(null)\n const totalCount = bucket.signalCount ?? signals.length\n const visibleSignals = showAll ? signals : signals.slice(0, DEFAULT_VISIBLE_ROWS)\n const hiddenCount = signals.length - DEFAULT_VISIBLE_ROWS\n\n // Determine whether to use structured rows (any signal has structured data)\n const useStructured = signals.some(hasStructuredData)\n\n return (\n <div\n id={panelId}\n className=\"rounded-lg rounded-t-none border border-t-0 border-border bg-muted/20 p-3\"\n role=\"region\"\n aria-label={`${bucket.label} details`}\n >\n {/* Card header */}\n <div className=\"mb-2 flex items-center justify-between\">\n <span className=\"text-[10px] font-bold uppercase tracking-wider text-muted-foreground\">\n {totalCount} signal{totalCount !== 1 ? \"s\" : \"\"} – {bucket.label}\n </span>\n </div>\n\n {/* Signal rows */}\n {visibleSignals.length > 0 ? (\n <ul className=\"divide-y divide-border/30\" aria-label=\"Matching signals\">\n {visibleSignals.map((signal, index) => (\n <li key={signal.id ?? `${bucket.key}-signal-${index}`}>\n {useStructured ? (\n <StructuredSignalRow\n item={item}\n bucketKey={bucket.key}\n signal={signal}\n tone={bucket.tone}\n onOpenSignalBucket={onOpenSignalBucket}\n />\n ) : (\n <LegacySignalRow\n item={item}\n bucketKey={bucket.key}\n signal={signal}\n onOpenSignalBucket={onOpenSignalBucket}\n />\n )}\n </li>\n ))}\n </ul>\n ) : bucket.evidence && bucket.evidence.length > 0 ? (\n <ul className=\"mt-3 space-y-1.5\" aria-label=\"Matching signals\">\n {bucket.evidence.map((evidence, index) => (\n <li key={`${bucket.key}-evidence-${index}`} className=\"flex gap-2 text-xs text-muted-foreground\">\n <span className=\"mt-1.5 h-1 w-1 shrink-0 rounded-full bg-primary\" />\n <span className=\"leading-relaxed\">{evidence}</span>\n </li>\n ))}\n </ul>\n ) : null}\n\n {/* \"Show N more\" button */}\n {!showAll && hiddenCount > 0 && (\n <button\n type=\"button\"\n onClick={() => setShowAll(true)}\n className=\"mt-2 flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground\"\n >\n <ChevronDown className=\"h-3 w-3\" />\n Show {hiddenCount} more\n </button>\n )}\n\n {/* Bucket feedback footer */}\n {onBucketFeedback && (\n <div className=\"mt-3 border-t border-border/40 pt-3\">\n <FeedbackFooter\n feedback={bucketFeedback}\n onFeedbackChange={setBucketFeedback}\n onSubmit={(data) => onBucketFeedback(bucket.key, data)}\n negativeChips={BUCKET_NEGATIVE_CHIPS}\n negativePrompt=\"Was this bucket useful?\"\n positivePrompt=\"Thanks! What was useful about this bucket?\"\n initialFeedback={initialBucketFeedback}\n feedbackKey={bucket.key}\n />\n </div>\n )}\n </div>\n )\n}\n\n// ---------------------------------------------------------------------------\n// ScoreWhyChips - Main export\n// ---------------------------------------------------------------------------\n\nexport interface ScoreWhyChipsProps {\n item: QueueItem\n signalData: SignalScoreData\n onOpenSignalBucket?: (args: { item: QueueItem; bucketKey: string; signalId: string }) => void\n className?: string\n}\n\nexport function ScoreWhyChips({\n item,\n signalData,\n onOpenSignalBucket,\n className,\n}: ScoreWhyChipsProps) {\n const [selectedBucketKey, setSelectedBucketKey] = React.useState<string | null>(null)\n\n React.useEffect(() => {\n setSelectedBucketKey(null)\n }, [item.id])\n\n const reactId = React.useId()\n const idPrefix = makeDomId(\"score-why\", reactId, item.id)\n const buckets = React.useMemo(() => getSignalScoreBuckets(signalData), [signalData])\n const selectedBucket = buckets.find((bucket) => bucket.key === selectedBucketKey) ?? null\n const selectedBucketSignals = selectedBucket ? getBucketSignals(selectedBucket) : []\n const selectedPanelId = selectedBucket ? `${idPrefix}-panel-${makeDomId(selectedBucket.key)}` : undefined\n\n if (buckets.length === 0) return null\n\n return (\n <div className={cn(\"mt-4\", className)}>\n <div className=\"mb-2 flex items-center gap-2\">\n <span className=\"text-[10px] font-bold uppercase tracking-wider text-muted-foreground\">Why</span>\n </div>\n <div className=\"flex flex-wrap gap-1.5\">\n {buckets.map((bucket) => {\n const isSelected = selectedBucketKey === bucket.key\n const panelId = `${idPrefix}-panel-${makeDomId(bucket.key)}`\n const signals = getBucketSignals(bucket)\n const signalCount = bucket.signalCount ?? signals.length\n return (\n <div key={bucket.key} className=\"flex flex-col\">\n <WhyPill\n bucket={bucket}\n isSelected={isSelected}\n signalCount={signalCount}\n panelId={panelId}\n onToggle={() => setSelectedBucketKey((prev) => (prev === bucket.key ? null : bucket.key))}\n onClose={() => setSelectedBucketKey(null)}\n />\n </div>\n )\n })}\n </div>\n\n {selectedBucket && selectedPanelId && (\n <WhyCard\n bucket={selectedBucket}\n signals={selectedBucketSignals}\n item={item}\n panelId={selectedPanelId}\n onOpenSignalBucket={onOpenSignalBucket}\n onBucketFeedback={signalData.onBucketFeedback}\n initialBucketFeedback={signalData.initialBucketFeedback?.[selectedBucket.key]}\n />\n )}\n </div>\n )\n}\n"],"mappings":";AAmMQ,SA4EJ,UA5EI,KAGE,YAHF;AAjMR,YAAY,WAAW;AACvB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,SAAS,sBAAsB;AAE/B,SAAS,UAAU;AAaZ,SAAS,2BACd,OACA,eACyB;AACzB,MAAI,cAAe,QAAO;AAC1B,MAAI,SAAS,GAAI,QAAO;AACxB,MAAI,SAAS,GAAI,QAAO;AACxB,MAAI,SAAS,GAAI,QAAO;AACxB,SAAO;AACT;AAEO,SAAS,qBAAqB,OAAwC;AAC3E,UAAQ,OAAO;AAAA,IACb,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,EACX;AACF;AAEA,SAAS,aAAa,OAA0C;AAC9D,SAAO,MACJ,OAAO,CAAC,SAAyB,QAAQ,IAAI,CAAC,EAC9C,KAAK,GAAG,EACR,QAAQ,oBAAoB,GAAG;AACpC;AAEA,SAAS,oBAAoB,QAA+C;AA9D5E;AA+DE,WACG,kBAAO,YAAP,mBAAgB,WAAhB,YAA0B,KAAK,OAC/B,kBAAO,cAAP,mBAAkB,WAAlB,YAA4B,KAAK,KAClC,QAAQ,OAAO,eAAe;AAElC;AAEA,SAAS,sBAAsB,YAA6D;AAtE5F;AAuEE,WAAQ,gBAAW,uBAAX,YAAiC,CAAC,GAAG;AAAA,IAC3C,CAAC,WAAW,OAAO,SAAS,YAAY,oBAAoB,MAAM;AAAA,EACpE;AACF;AAEA,SAAS,iBAAiB,QAAsE;AAC9F,MAAI,OAAO,WAAW,OAAO,QAAQ,SAAS,EAAG,QAAO,OAAO;AAE/D,QAAM,YAAY,OAAO,aAAa,OAAO,UAAU,SAAS,IAAI,OAAO,YAAY,OAAO,kBAAkB,CAAC,OAAO,eAAe,IAAI,CAAC;AAC5I,QAAM,kBAAkB,MAAM,KAAK,IAAI,IAAI,SAAS,CAAC;AAErD,SAAO,gBAAgB,IAAI,CAAC,cAAc;AAAA,IACxC,IAAI;AAAA,IACJ,OAAO,GAAG,OAAO,KAAK;AAAA,EACxB,EAAE;AACJ;AAMA,MAAM,oBAAgD;AAAA,EACpD,sBAAsB;AAAA,EACtB,6BAA6B;AAAA,EAC7B,kBAAkB;AAAA,EAClB,eAAe;AAAA,EACf,iBAAiB;AACnB;AAEA,SAAS,YAAY,UAA+B;AApGpD;AAqGE,MAAI,CAAC,SAAU,QAAO;AACtB,UAAO,uBAAkB,QAAQ,MAA1B,YAA+B;AACxC;AAOO,MAAM,sBAA8C;AAAA,EACzD,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AACR;AAGO,MAAM,qBAAqB;AAMlC,SAAS,UAAU,OAA0C;AAC3D,SAAO,SAAS,MAAM,KAAK,EAAE,SAAS,IAAI,QAAQ;AACpD;AAMA,MAAM,wBAA4C;AAAA,EAChD;AAAA,IACE,OAAO;AAAA,IACP,WAAW;AAAA,IACX,UAAU;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA,EAAE,OAAO,aAAa;AAAA,EACtB;AAAA,IACE,OAAO;AAAA,IACP,WAAW;AAAA,IACX,UAAU,CAAC,mBAAmB,gBAAgB,aAAa,OAAO;AAAA,EACpE;AAAA,EACA,EAAE,OAAO,gBAAgB;AAAA,EACzB,EAAE,OAAO,kBAAkB;AAAA,EAC3B,EAAE,OAAO,QAAQ;AACnB;AAMA,MAAM,uBAAuB;AAe7B,SAAS,QAAQ,EAAE,QAAQ,YAAY,aAAa,SAAS,UAAU,QAAQ,GAAiB;AAC9F,QAAM,gBAAgB,YAAY,OAAO,IAAI;AAE7C,QAAM,gBAAgB;AAAA,IACpB;AAAA,IACA,aACI,2CACA;AAAA,EACN;AAEA,SACE,qBAAC,SAAI,WAAU,sCACb;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS;AAAA,QACT,iBAAe;AAAA,QACf,iBAAe;AAAA,QACf,WAAW;AAAA,UACT;AAAA,UACA;AAAA,UACA,cAAc;AAAA,QAChB;AAAA,QAEA;AAAA,8BAAC,iBAAc,WAAU,oBAAmB;AAAA,UAC3C,OAAO;AAAA,UACP,cAAc,KACb,qBAAC,UAAK,WAAW,GAAG,wCAAwC,aAAa,qBAAqB,UAAU,GAAG;AAAA;AAAA,YACvG;AAAA,aACJ;AAAA,UAED,aACC,oBAAC,aAAU,WAAU,oBAAmB,IAExC,oBAAC,eAAY,WAAU,oBAAmB;AAAA;AAAA;AAAA,IAE9C;AAAA,IACC,cACC;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,cAAY,SAAS,OAAO,KAAK;AAAA,QACjC,SAAS;AAAA,QACT,WAAW;AAAA,UACT;AAAA,UACA;AAAA,QACF;AAAA,QAEA,8BAAC,KAAE,WAAU,WAAU;AAAA;AAAA,IACzB;AAAA,KAEJ;AAEJ;AAUA,SAAS,wBAAwB,EAAE,WAAW,GAAiC;AAC7E,SACE,oBAAC,SAAI,WAAU,qCACZ,qBAAW,IAAI,CAAC,MAAM,QAAQ;AAC7B,UAAM,WAAW,YAAY,KAAK,IAAI;AACtC,WACE,qBAAC,MAAM,UAAN,EACE;AAAA,YAAM,KAAK,oBAAC,UAAK,WAAU,wCAAuC,eAAC;AAAA,MACpE,qBAAC,UAAK,WAAU,iHACd;AAAA,4BAAC,YAAS,WAAU,wBAAuB;AAAA,QAC1C,KAAK,KAAK,QAAQ,MAAM,GAAG;AAAA,QAAE;AAAA,QAAG,KAAK;AAAA,SACxC;AAAA,SALmB,KAAK,IAM1B;AAAA,EAEJ,CAAC,GACH;AAEJ;AAcA,SAAS,oBAAoB,EAAE,MAAM,WAAW,QAAQ,MAAM,mBAAmB,GAA6B;AAxQ9G;AAyQE,QAAM,gBAAgB,YAAY,OAAO,cAAc;AACvD,QAAM,YAAY,QAAQ,yBAAoB,IAAI,MAAxB,YAA6B,qBAAsB;AAC7E,QAAM,aAAa,OAAO,mBAAmB,qBAAqB,OAAO,cAAc,OAAO,WAAW,SAAS;AAClH,QAAM,aAAa,QAAQ,OAAO,kBAAkB,OAAO,cAAc;AAEzE,QAAM,aACJ,iCAEE;AAAA,wBAAC,SAAI,WAAW,GAAG,6DAA6D,SAAS,GACvF,8BAAC,iBAAc,WAAU,WAAU,GACrC;AAAA,IAGA,oBAAC,SAAI,WAAU,WACZ,uBACC,oBAAC,2BAAwB,YAAY,OAAO,YAAa,IAEzD,qBAAC,SAAI,WAAU,+BACb;AAAA,0BAAC,UAAK,WAAU,sDACb,oBAAU,OAAO,YAAY,GAChC;AAAA,MACA,oBAAC,UAAK,WAAU,iCACb,oBAAU,OAAO,SAAS,GAC7B;AAAA,OACF,GAEJ;AAAA,IAGA,oBAAC,SAAI,WAAU,WACb,8BAAC,UAAK,WAAU,gDACb,oBAAU,OAAO,YAAY,GAChC,GACF;AAAA,IAGA,oBAAC,UAAK,WAAU,iDACb,oBAAU,OAAO,IAAI,GACxB;AAAA,IAGA,oBAAC,gBAAa,WAAU,6HAA4H;AAAA,IAGnJ,cACC;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,eAAY;AAAA,QAEX;AAAA,iBAAO,kBACN,qBAAC,UAAK;AAAA;AAAA,YACY,oBAAC,UAAK,WAAU,qCAAqC,iBAAO,gBAAe;AAAA,aAC7F;AAAA,UAED,OAAO,kBACN,qBAAC,UACE;AAAA,mBAAO,iBAAiB,WAAQ;AAAA,YAChC,OAAO;AAAA,aACV;AAAA;AAAA;AAAA,IAEJ;AAAA,KAEJ;AAGF,MAAI,OAAO,MAAM,oBAAoB;AACnC,WACE;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,WAAU;AAAA,QACV,OAAO,EAAE,qBAAqB,6CAA6C;AAAA,QAC3E,SAAS,MAAM,mBAAmB,EAAE,MAAM,WAAW,UAAU,OAAO,GAAI,CAAC;AAAA,QAE1E;AAAA;AAAA,IACH;AAAA,EAEJ;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,OAAO,EAAE,qBAAqB,6CAA6C;AAAA,MAE1E;AAAA;AAAA,EACH;AAEJ;AAaA,SAAS,gBAAgB,EAAE,MAAM,WAAW,QAAQ,mBAAmB,GAAmB;AACxF,QAAM,cAAc,CAAC,EAAE,OAAO,MAAM;AACpC,QAAM,aACJ,gCACE,+BAAC,SAAI,WAAU,0CACb;AAAA,yBAAC,SAAI,WAAU,kBACb;AAAA,0BAAC,OAAE,WAAU,+BAA+B,iBAAO,OAAM;AAAA,MACxD,OAAO,cAAc,oBAAC,OAAE,WAAU,8CAA8C,iBAAO,aAAY,IAAO;AAAA,OACzG,OAAO,UAAU,OAAO,WACxB,qBAAC,SAAI,WAAU,sEACZ;AAAA,eAAO,SAAS,oBAAC,UAAK,WAAU,qCAAqC,iBAAO,QAAO,IAAU;AAAA,QAC7F,OAAO,SAAS,oBAAC,UAAK,WAAU,qCAAqC,iBAAO,QAAO,IAAU;AAAA,SAChG;AAAA,OAEJ;AAAA,IACA,qBAAC,SAAI,WAAU,oCACZ;AAAA,aAAO,OAAO,oBAAC,UAAK,WAAU,wCAAwC,iBAAO,MAAK,IAAU;AAAA,MAC5F,eACC,oBAAC,gBAAa,WAAU,oHAAmH;AAAA,OAE/I;AAAA,KACF,GACF;AAGF,MAAI,aAAa;AACf,WACE;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,WAAU;AAAA,QACV,SAAS,MAAM,mBAAoB,EAAE,MAAM,WAAW,UAAU,OAAO,GAAI,CAAC;AAAA,QAE3E;AAAA;AAAA,IACH;AAAA,EAEJ;AAEA,SAAO,oBAAC,SAAI,WAAU,2CAA2C,sBAAW;AAC9E;AAMA,SAAS,kBAAkB,QAA+C;AACxE,SAAO;AAAA,IACL,OAAO,gBACP,OAAO,aACP,OAAO,gBACN,OAAO,cAAc,OAAO,WAAW,SAAS;AAAA,EACnD;AACF;AAiBA,SAAS,QAAQ,EAAE,QAAQ,SAAS,MAAM,SAAS,oBAAoB,kBAAkB,sBAAsB,GAAiB;AAhbhI;AAibE,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,KAAK;AAClD,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,MAAM,SAAyC,IAAI;AAC/F,QAAM,cAAa,YAAO,gBAAP,YAAsB,QAAQ;AACjD,QAAM,iBAAiB,UAAU,UAAU,QAAQ,MAAM,GAAG,oBAAoB;AAChF,QAAM,cAAc,QAAQ,SAAS;AAGrC,QAAM,gBAAgB,QAAQ,KAAK,iBAAiB;AAEpD,SACE;AAAA,IAAC;AAAA;AAAA,MACC,IAAI;AAAA,MACJ,WAAU;AAAA,MACV,MAAK;AAAA,MACL,cAAY,GAAG,OAAO,KAAK;AAAA,MAG3B;AAAA,4BAAC,SAAI,WAAU,0CACb,+BAAC,UAAK,WAAU,wEACb;AAAA;AAAA,UAAW;AAAA,UAAQ,eAAe,IAAI,MAAM;AAAA,UAAG;AAAA,UAAU,OAAO;AAAA,WACnE,GACF;AAAA,QAGC,eAAe,SAAS,IACvB,oBAAC,QAAG,WAAU,6BAA4B,cAAW,oBAClD,yBAAe,IAAI,CAAC,QAAQ,UAAO;AA3c9C,cAAAA;AA4cY,qCAAC,QACE,0BACC;AAAA,YAAC;AAAA;AAAA,cACC;AAAA,cACA,WAAW,OAAO;AAAA,cAClB;AAAA,cACA,MAAM,OAAO;AAAA,cACb;AAAA;AAAA,UACF,IAEA;AAAA,YAAC;AAAA;AAAA,cACC;AAAA,cACA,WAAW,OAAO;AAAA,cAClB;AAAA,cACA;AAAA;AAAA,UACF,MAfKA,MAAA,OAAO,OAAP,OAAAA,MAAa,GAAG,OAAO,GAAG,WAAW,KAAK,EAiBnD;AAAA,SACD,GACH,IACE,OAAO,YAAY,OAAO,SAAS,SAAS,IAC9C,oBAAC,QAAG,WAAU,oBAAmB,cAAW,oBACzC,iBAAO,SAAS,IAAI,CAAC,UAAU,UAC9B,qBAAC,QAA2C,WAAU,4CACpD;AAAA,8BAAC,UAAK,WAAU,mDAAkD;AAAA,UAClE,oBAAC,UAAK,WAAU,mBAAmB,oBAAS;AAAA,aAFrC,GAAG,OAAO,GAAG,aAAa,KAAK,EAGxC,CACD,GACH,IACE;AAAA,QAGH,CAAC,WAAW,cAAc,KACzB;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAS,MAAM,WAAW,IAAI;AAAA,YAC9B,WAAU;AAAA,YAEV;AAAA,kCAAC,eAAY,WAAU,WAAU;AAAA,cAAE;AAAA,cAC7B;AAAA,cAAY;AAAA;AAAA;AAAA,QACpB;AAAA,QAID,oBACC,oBAAC,SAAI,WAAU,uCACb;AAAA,UAAC;AAAA;AAAA,YACC,UAAU;AAAA,YACV,kBAAkB;AAAA,YAClB,UAAU,CAAC,SAAS,iBAAiB,OAAO,KAAK,IAAI;AAAA,YACrD,eAAe;AAAA,YACf,gBAAe;AAAA,YACf,gBAAe;AAAA,YACf,iBAAiB;AAAA,YACjB,aAAa,OAAO;AAAA;AAAA,QACtB,GACF;AAAA;AAAA;AAAA,EAEJ;AAEJ;AAaO,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAuB;AA1hBvB;AA2hBE,QAAM,CAAC,mBAAmB,oBAAoB,IAAI,MAAM,SAAwB,IAAI;AAEpF,QAAM,UAAU,MAAM;AACpB,yBAAqB,IAAI;AAAA,EAC3B,GAAG,CAAC,KAAK,EAAE,CAAC;AAEZ,QAAM,UAAU,MAAM,MAAM;AAC5B,QAAM,WAAW,UAAU,aAAa,SAAS,KAAK,EAAE;AACxD,QAAM,UAAU,MAAM,QAAQ,MAAM,sBAAsB,UAAU,GAAG,CAAC,UAAU,CAAC;AACnF,QAAM,kBAAiB,aAAQ,KAAK,CAAC,WAAW,OAAO,QAAQ,iBAAiB,MAAzD,YAA8D;AACrF,QAAM,wBAAwB,iBAAiB,iBAAiB,cAAc,IAAI,CAAC;AACnF,QAAM,kBAAkB,iBAAiB,GAAG,QAAQ,UAAU,UAAU,eAAe,GAAG,CAAC,KAAK;AAEhG,MAAI,QAAQ,WAAW,EAAG,QAAO;AAEjC,SACE,qBAAC,SAAI,WAAW,GAAG,QAAQ,SAAS,GAClC;AAAA,wBAAC,SAAI,WAAU,gCACb,8BAAC,UAAK,WAAU,wEAAuE,iBAAG,GAC5F;AAAA,IACA,oBAAC,SAAI,WAAU,0BACZ,kBAAQ,IAAI,CAAC,WAAW;AAhjBjC,UAAAA;AAijBU,YAAM,aAAa,sBAAsB,OAAO;AAChD,YAAM,UAAU,GAAG,QAAQ,UAAU,UAAU,OAAO,GAAG,CAAC;AAC1D,YAAM,UAAU,iBAAiB,MAAM;AACvC,YAAM,eAAcA,MAAA,OAAO,gBAAP,OAAAA,MAAsB,QAAQ;AAClD,aACE,oBAAC,SAAqB,WAAU,iBAC9B;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,UAAU,MAAM,qBAAqB,CAAC,SAAU,SAAS,OAAO,MAAM,OAAO,OAAO,GAAI;AAAA,UACxF,SAAS,MAAM,qBAAqB,IAAI;AAAA;AAAA,MAC1C,KARQ,OAAO,GASjB;AAAA,IAEJ,CAAC,GACH;AAAA,IAEC,kBAAkB,mBACjB;AAAA,MAAC;AAAA;AAAA,QACC,QAAQ;AAAA,QACR,SAAS;AAAA,QACT;AAAA,QACA,SAAS;AAAA,QACT;AAAA,QACA,kBAAkB,WAAW;AAAA,QAC7B,wBAAuB,gBAAW,0BAAX,mBAAmC,eAAe;AAAA;AAAA,IAC3E;AAAA,KAEJ;AAEJ;","names":["_a"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import 'react';
|
|
2
2
|
import './feedback-primitives.js';
|
|
3
|
-
export { P as PriorityFactor, S as SignalPriorityPopover, k as SignalPriorityPopoverProps } from '../signal-priority-popover-
|
|
3
|
+
export { P as PriorityFactor, S as SignalPriorityPopover, k as SignalPriorityPopoverProps } from '../signal-priority-popover-DWaAMhPI.js';
|
|
4
4
|
import './quick-action-sidebar-nav.js';
|
|
5
5
|
import './quick-action-modal.js';
|
|
6
6
|
import './score-breakdown.js';
|