@handled-ai/design-system 0.17.0 → 0.17.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.
Files changed (51) hide show
  1. package/dist/components/badge.d.ts +1 -1
  2. package/dist/components/button.d.ts +1 -1
  3. package/dist/components/feedback-primitives.d.ts +66 -0
  4. package/dist/components/feedback-primitives.js +295 -0
  5. package/dist/components/feedback-primitives.js.map +1 -0
  6. package/dist/components/score-why-chips.d.ts +8 -17
  7. package/dist/components/score-why-chips.js +266 -180
  8. package/dist/components/score-why-chips.js.map +1 -1
  9. package/dist/components/signal-priority-popover.d.ts +17 -0
  10. package/dist/components/signal-priority-popover.js +247 -0
  11. package/dist/components/signal-priority-popover.js.map +1 -0
  12. package/dist/components/tabs.d.ts +1 -1
  13. package/dist/components/user-display.d.ts +22 -0
  14. package/dist/components/user-display.js +138 -0
  15. package/dist/components/user-display.js.map +1 -0
  16. package/dist/components/user-pill.d.ts +3 -0
  17. package/dist/components/user-pill.js +5 -0
  18. package/dist/components/user-pill.js.map +1 -0
  19. package/dist/index.d.ts +6 -3
  20. package/dist/index.js +12 -0
  21. package/dist/index.js.map +1 -1
  22. package/dist/lib/user-display.d.ts +31 -0
  23. package/dist/lib/user-display.js +57 -0
  24. package/dist/lib/user-display.js.map +1 -0
  25. package/dist/prototype/index.d.ts +2 -1
  26. package/dist/prototype/prototype-accounts-view.d.ts +2 -1
  27. package/dist/prototype/prototype-admin-view.d.ts +2 -1
  28. package/dist/prototype/prototype-config.d.ts +15 -328
  29. package/dist/prototype/prototype-inbox-view.d.ts +8 -3
  30. package/dist/prototype/prototype-inbox-view.js +24 -13
  31. package/dist/prototype/prototype-inbox-view.js.map +1 -1
  32. package/dist/prototype/prototype-insights-view.d.ts +2 -1
  33. package/dist/prototype/prototype-shell.d.ts +2 -1
  34. package/dist/signal-priority-popover-DQ_VuHac.d.ts +390 -0
  35. package/package.json +1 -1
  36. package/src/components/__tests__/contextual-quick-action-launcher.test.tsx +99 -188
  37. package/src/components/__tests__/feedback-primitives.test.tsx +509 -0
  38. package/src/components/__tests__/score-why-chips.test.tsx +540 -0
  39. package/src/components/__tests__/signal-priority-popover.test.tsx +312 -0
  40. package/src/components/feedback-primitives.tsx +424 -0
  41. package/src/components/score-why-chips.tsx +413 -203
  42. package/src/components/signal-priority-popover.tsx +359 -0
  43. package/src/components/user-display.tsx +96 -0
  44. package/src/components/user-pill.tsx +1 -0
  45. package/src/index.ts +6 -0
  46. package/src/lib/__tests__/user-display.test.ts +43 -0
  47. package/src/lib/user-display.ts +88 -0
  48. package/src/prototype/__tests__/detail-view-score-why.test.tsx +33 -29
  49. package/src/prototype/__tests__/detail-view-title-slots.test.tsx +65 -0
  50. package/src/prototype/prototype-config.ts +28 -0
  51. package/src/prototype/prototype-inbox-view.tsx +25 -11
@@ -3,8 +3,19 @@
3
3
  "use client";
4
4
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
5
5
  import * as React from "react";
6
- import { Info } from "lucide-react";
7
- import { ScoreBreakdown } from "./score-breakdown.js";
6
+ import {
7
+ ChevronDown,
8
+ ChevronUp,
9
+ ChevronRight,
10
+ X,
11
+ TrendingDown,
12
+ ArrowUpRight,
13
+ Radar,
14
+ ArrowDownLeft,
15
+ GitMerge,
16
+ Activity
17
+ } from "lucide-react";
18
+ import { FeedbackFooter } from "./feedback-primitives.js";
8
19
  import { cn } from "../lib/utils.js";
9
20
  function getSignalScoreUrgencyLabel(score, providedLabel) {
10
21
  if (providedLabel) return providedLabel;
@@ -13,25 +24,6 @@ function getSignalScoreUrgencyLabel(score, providedLabel) {
13
24
  if (score >= 35) return "Medium";
14
25
  return "Low";
15
26
  }
16
- function getUrgencyChipClass(label) {
17
- switch (label) {
18
- case "Urgent":
19
- return "border-red-200 bg-red-50 text-red-700 hover:bg-red-100 dark:border-red-900/50 dark:bg-red-950/30 dark:text-red-300";
20
- case "High":
21
- return "border-orange-200 bg-orange-50 text-orange-700 hover:bg-orange-100 dark:border-orange-900/50 dark:bg-orange-950/30 dark:text-orange-300";
22
- case "Medium":
23
- return "border-amber-200 bg-amber-50 text-amber-700 hover:bg-amber-100 dark:border-amber-900/50 dark:bg-amber-950/30 dark:text-amber-300";
24
- case "Low":
25
- return "border-emerald-200 bg-emerald-50 text-emerald-700 hover:bg-emerald-100 dark:border-emerald-900/50 dark:bg-emerald-950/30 dark:text-emerald-300";
26
- }
27
- }
28
- function classificationForScore(score) {
29
- if (score == null) return void 0;
30
- if (score >= 80) return "Urgent";
31
- if (score >= 60) return "High";
32
- if (score >= 35) return "Medium";
33
- return "Low";
34
- }
35
27
  function scoreRangeForUrgency(label) {
36
28
  switch (label) {
37
29
  case "Urgent":
@@ -47,18 +39,6 @@ function scoreRangeForUrgency(label) {
47
39
  function makeDomId(...parts) {
48
40
  return parts.filter((part) => Boolean(part)).join("-").replace(/[^A-Za-z0-9_-]+/g, "-");
49
41
  }
50
- function scoreFactorToPriorityBucket(factor) {
51
- var _a, _b, _c;
52
- return {
53
- key: factor.key,
54
- label: factor.label,
55
- kind: "factor",
56
- score: (_a = factor.score) != null ? _a : void 0,
57
- classification: (_c = factor.risk) != null ? _c : classificationForScore((_b = factor.score) != null ? _b : void 0),
58
- rationale: factor.why,
59
- factorKeys: [factor.key]
60
- };
61
- }
62
42
  function bucketHasSignalRows(bucket) {
63
43
  var _a, _b, _c, _d;
64
44
  return ((_b = (_a = bucket.signals) == null ? void 0 : _a.length) != null ? _b : 0) > 0 || ((_d = (_c = bucket.signalIds) == null ? void 0 : _c.length) != null ? _d : 0) > 0 || Boolean(bucket.primarySignalId);
@@ -78,119 +58,253 @@ function getBucketSignals(bucket) {
78
58
  label: `${bucket.label} signal`
79
59
  }));
80
60
  }
81
- function getPriorityBuckets(signalData) {
82
- if (signalData.explanationBuckets !== void 0) return signalData.explanationBuckets;
83
- return signalData.factors.map(scoreFactorToPriorityBucket);
61
+ const SIGNAL_TYPE_ICONS = {
62
+ treasury_liquidation: TrendingDown,
63
+ cumulative_treasury_outflow: ArrowUpRight,
64
+ test_transaction: Radar,
65
+ micro_deposit: ArrowDownLeft,
66
+ combined_signal: GitMerge
67
+ };
68
+ function resolveIcon(iconName) {
69
+ var _a;
70
+ if (!iconName) return Activity;
71
+ return (_a = SIGNAL_TYPE_ICONS[iconName]) != null ? _a : Activity;
84
72
  }
85
- function isSameExplanation(a, b) {
86
- return Boolean(a && b && a.trim() === b.trim());
73
+ const SIGNAL_TONE_CLASSES = {
74
+ alert: "bg-red-50 text-red-600",
75
+ warn: "bg-amber-50 text-amber-600",
76
+ info: "bg-blue-50 text-blue-600"
77
+ };
78
+ const DEFAULT_TONE_CLASS = "bg-muted text-muted-foreground";
79
+ function slotValue(value) {
80
+ return value && value.trim().length > 0 ? value : "";
87
81
  }
88
- function SignalPriorityChip({
89
- score,
90
- urgencyLabel: providedLabel,
91
- isOpen,
92
- controlsId,
93
- onClick,
94
- className
95
- }) {
96
- const urgencyLabel = getSignalScoreUrgencyLabel(score, providedLabel);
97
- return /* @__PURE__ */ jsxs(
98
- "button",
99
- {
100
- type: "button",
101
- onClick,
102
- "aria-expanded": isOpen,
103
- "aria-controls": controlsId,
104
- className: cn(
105
- "inline-flex items-center gap-1 rounded-md border px-2.5 py-1 text-xs font-semibold transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
106
- getUrgencyChipClass(urgencyLabel),
107
- className
108
- ),
109
- children: [
110
- urgencyLabel,
111
- " Priority",
112
- /* @__PURE__ */ jsx(Info, { className: "h-3 w-3" })
113
- ]
114
- }
82
+ const BUCKET_NEGATIVE_CHIPS = [
83
+ {
84
+ label: "Not relevant for this account",
85
+ subPrompt: "Why isn't it relevant?",
86
+ subChips: [
87
+ "Business as usual for this account",
88
+ "Account in maintenance mode",
89
+ "Wrong contact for this signal",
90
+ "Other"
91
+ ]
92
+ },
93
+ { label: "Bad timing" },
94
+ {
95
+ label: "Inaccurate data",
96
+ subPrompt: "Which field?",
97
+ subChips: ["Balance figures", "Counterparty", "Timestamp", "Other"]
98
+ },
99
+ { label: "Wrong account" },
100
+ { label: "Already handled" },
101
+ { label: "Other" }
102
+ ];
103
+ const DEFAULT_VISIBLE_ROWS = 8;
104
+ function WhyPill({ bucket, isSelected, signalCount, panelId, onToggle, onClose }) {
105
+ const IconComponent = resolveIcon(bucket.icon);
106
+ const sharedClasses = cn(
107
+ "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",
108
+ isSelected ? "border-border bg-muted text-foreground" : "border-border bg-background text-muted-foreground hover:bg-muted/60 hover:text-foreground"
115
109
  );
116
- }
117
- function SignalPriorityPanel({ signalData, className, id }) {
118
- var _a, _b, _c;
119
- const urgencyLabel = getSignalScoreUrgencyLabel(signalData.score, signalData.urgencyLabel);
120
- const buckets = React.useMemo(() => getPriorityBuckets(signalData), [signalData]);
121
- const topBucketRationale = (_a = buckets.find((bucket) => bucket.rationale)) == null ? void 0 : _a.rationale;
122
- const primaryUrgencyExplanation = (_c = (_b = signalData.urgencyExplanation) != null ? _b : signalData.whyNow) != null ? _c : topBucketRationale;
123
- const whyNowSection = isSameExplanation(signalData.whyNow, primaryUrgencyExplanation) ? void 0 : signalData.whyNow;
124
- const topFactorSection = isSameExplanation(topBucketRationale, primaryUrgencyExplanation) || isSameExplanation(topBucketRationale, whyNowSection) ? void 0 : topBucketRationale;
125
- const scoreRange = scoreRangeForUrgency(urgencyLabel);
126
- return /* @__PURE__ */ jsxs("div", { id, className: cn("rounded-lg border border-border bg-muted/20 p-3 text-xs", className), role: "region", "aria-label": "Priority explanation", children: [
127
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-start justify-between gap-3", children: [
128
- /* @__PURE__ */ jsxs("div", { children: [
129
- /* @__PURE__ */ jsxs("p", { className: "font-semibold text-foreground", children: [
130
- "Why this is ",
131
- urgencyLabel.toLowerCase(),
132
- " priority"
133
- ] }),
134
- primaryUrgencyExplanation ? /* @__PURE__ */ jsx("p", { className: "mt-1 leading-relaxed text-muted-foreground", children: primaryUrgencyExplanation }) : null
135
- ] }),
136
- /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 flex-wrap gap-1.5 text-[11px] text-muted-foreground", children: [
137
- /* @__PURE__ */ jsxs("span", { className: "rounded-full bg-background px-2 py-0.5", children: [
138
- "Score ",
139
- signalData.score,
140
- "/100"
141
- ] }),
142
- /* @__PURE__ */ jsxs("span", { className: "rounded-full bg-background px-2 py-0.5", children: [
143
- urgencyLabel,
144
- " range: ",
145
- scoreRange
146
- ] })
147
- ] })
148
- ] }),
149
- (whyNowSection || topFactorSection) && /* @__PURE__ */ jsxs("div", { className: "mt-3 grid gap-2 text-xs text-muted-foreground sm:grid-cols-2", children: [
150
- whyNowSection ? /* @__PURE__ */ jsxs("div", { className: "rounded-md bg-background/80 p-2", children: [
151
- /* @__PURE__ */ jsx("p", { className: "font-medium text-foreground", children: "Why now" }),
152
- /* @__PURE__ */ jsx("p", { className: "mt-0.5 leading-relaxed", children: whyNowSection })
153
- ] }) : null,
154
- topFactorSection ? /* @__PURE__ */ jsxs("div", { className: "rounded-md bg-background/80 p-2", children: [
155
- /* @__PURE__ */ jsx("p", { className: "font-medium text-foreground", children: "Top factor" }),
156
- /* @__PURE__ */ jsx("p", { className: "mt-0.5 leading-relaxed", children: topFactorSection })
157
- ] }) : null
158
- ] }),
159
- signalData.factors.length > 0 ? /* @__PURE__ */ jsx(
160
- ScoreBreakdown,
110
+ return /* @__PURE__ */ jsxs("div", { className: "inline-flex h-[26px] items-stretch", children: [
111
+ /* @__PURE__ */ jsxs(
112
+ "button",
113
+ {
114
+ type: "button",
115
+ onClick: onToggle,
116
+ "aria-expanded": isSelected,
117
+ "aria-controls": panelId,
118
+ className: cn(
119
+ sharedClasses,
120
+ "gap-1.5 rounded-lg border px-2.5 py-1",
121
+ isSelected && "rounded-b-none rounded-r-none border-r-0"
122
+ ),
123
+ children: [
124
+ /* @__PURE__ */ jsx(IconComponent, { className: "h-3 w-3 shrink-0" }),
125
+ bucket.label,
126
+ signalCount > 1 && /* @__PURE__ */ jsxs("span", { className: cn("rounded-full px-1.5 py-0 text-[10px]", isSelected ? "bg-background/60" : "bg-muted"), children: [
127
+ "x",
128
+ signalCount
129
+ ] }),
130
+ isSelected ? /* @__PURE__ */ jsx(ChevronUp, { className: "h-3 w-3 shrink-0" }) : /* @__PURE__ */ jsx(ChevronDown, { className: "h-3 w-3 shrink-0" })
131
+ ]
132
+ }
133
+ ),
134
+ isSelected && /* @__PURE__ */ jsx(
135
+ "button",
161
136
  {
162
- className: "mt-3",
163
- factors: signalData.factors,
164
- onFactorFeedback: signalData.onFactorFeedback,
165
- initialFeedback: signalData.initialFactorFeedback
137
+ type: "button",
138
+ "aria-label": `Close ${bucket.label}`,
139
+ onClick: onClose,
140
+ className: cn(
141
+ sharedClasses,
142
+ "rounded-lg rounded-b-none rounded-l-none border border-l-0 border-border px-1.5 py-1 hover:bg-background/60"
143
+ ),
144
+ children: /* @__PURE__ */ jsx(X, { className: "h-3 w-3" })
166
145
  }
167
- ) : null
146
+ )
168
147
  ] });
169
148
  }
170
- function SignalRow({ item, bucketKey, signal, onOpenSignalBucket }) {
149
+ function CombinedSignalMiniChips({ components }) {
150
+ return /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-1", children: components.map((comp, idx) => {
151
+ const CompIcon = resolveIcon(comp.type);
152
+ return /* @__PURE__ */ jsxs(React.Fragment, { children: [
153
+ idx > 0 && /* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground/60", children: "+" }),
154
+ /* @__PURE__ */ jsxs("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", children: [
155
+ /* @__PURE__ */ jsx(CompIcon, { className: "h-2.5 w-2.5 shrink-0" }),
156
+ comp.type.replace(/_/g, " "),
157
+ " x",
158
+ comp.count
159
+ ] })
160
+ ] }, comp.type);
161
+ }) });
162
+ }
163
+ function StructuredSignalRow({ item, bucketKey, signal, tone, onOpenSignalBucket }) {
164
+ var _a;
165
+ const IconComponent = resolveIcon(signal.signalTypeName);
166
+ const toneClass = tone ? (_a = SIGNAL_TONE_CLASSES[tone]) != null ? _a : DEFAULT_TONE_CLASS : DEFAULT_TONE_CLASS;
167
+ const isCombined = signal.signalTypeName === "combined_signal" && signal.components && signal.components.length > 0;
171
168
  const rowContent = /* @__PURE__ */ jsxs(Fragment, { children: [
172
- /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2", children: [
169
+ /* @__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
+ /* @__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: [
171
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold tabular-nums text-foreground", children: slotValue(signal.primaryValue) }),
172
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: slotValue(signal.qualifier) })
173
+ ] }) }),
174
+ /* @__PURE__ */ jsx("div", { className: "min-w-0", children: /* @__PURE__ */ jsx("span", { className: "block truncate text-xs text-muted-foreground", children: slotValue(signal.counterparty) }) }),
175
+ /* @__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
+ ] });
178
+ if (signal.id && onOpenSignalBucket) {
179
+ return /* @__PURE__ */ jsx(
180
+ "button",
181
+ {
182
+ type: "button",
183
+ 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",
184
+ style: { gridTemplateColumns: "20px minmax(0,1fr) minmax(0,1fr) auto 16px" },
185
+ onClick: () => onOpenSignalBucket({ item, bucketKey, signalId: signal.id }),
186
+ children: rowContent
187
+ }
188
+ );
189
+ }
190
+ return /* @__PURE__ */ jsx(
191
+ "div",
192
+ {
193
+ className: "grid w-full items-center gap-x-3 gap-y-1 rounded-md px-3 py-2",
194
+ style: { gridTemplateColumns: "20px minmax(0,1fr) minmax(0,1fr) auto 16px" },
195
+ children: rowContent
196
+ }
197
+ );
198
+ }
199
+ function LegacySignalRow({ item, bucketKey, signal, onOpenSignalBucket }) {
200
+ const isClickable = !!(signal.id && onOpenSignalBucket);
201
+ const rowContent = /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2", children: [
202
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
173
203
  /* @__PURE__ */ jsx("p", { className: "font-medium text-foreground", children: signal.label }),
174
- signal.time ? /* @__PURE__ */ jsx("span", { className: "shrink-0 text-[11px] text-muted-foreground/70", children: signal.time }) : null
204
+ signal.description ? /* @__PURE__ */ jsx("p", { className: "mt-1 leading-relaxed text-muted-foreground", children: signal.description }) : null,
205
+ (signal.source || signal.metric) && /* @__PURE__ */ jsxs("div", { className: "mt-1.5 flex flex-wrap gap-1.5 text-[11px] text-muted-foreground/80", children: [
206
+ signal.source ? /* @__PURE__ */ jsx("span", { className: "rounded-full bg-muted px-2 py-0.5", children: signal.source }) : null,
207
+ signal.metric ? /* @__PURE__ */ jsx("span", { className: "rounded-full bg-muted px-2 py-0.5", children: signal.metric }) : null
208
+ ] })
175
209
  ] }),
176
- signal.description ? /* @__PURE__ */ jsx("p", { className: "mt-1 leading-relaxed text-muted-foreground", children: signal.description }) : null,
177
- (signal.source || signal.metric) && /* @__PURE__ */ jsxs("div", { className: "mt-1.5 flex flex-wrap gap-1.5 text-[11px] text-muted-foreground/80", children: [
178
- signal.source ? /* @__PURE__ */ jsx("span", { className: "rounded-full bg-muted px-2 py-0.5", children: signal.source }) : null,
179
- signal.metric ? /* @__PURE__ */ jsx("span", { className: "rounded-full bg-muted px-2 py-0.5", children: signal.metric }) : null
210
+ /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [
211
+ signal.time ? /* @__PURE__ */ jsx("span", { className: "text-[11px] text-muted-foreground/70", children: signal.time }) : null,
212
+ isClickable && /* @__PURE__ */ jsx(ChevronRight, { className: "h-3 w-3 text-muted-foreground/60 transition-transform group-hover:translate-x-0.5 group-hover:text-foreground/50" })
180
213
  ] })
181
- ] });
182
- if (signal.id && onOpenSignalBucket) {
214
+ ] }) });
215
+ if (isClickable) {
183
216
  return /* @__PURE__ */ jsx(
184
217
  "button",
185
218
  {
186
219
  type: "button",
187
- className: "w-full rounded-md bg-background/80 p-2 text-left text-xs transition-colors hover:bg-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
220
+ 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",
188
221
  onClick: () => onOpenSignalBucket({ item, bucketKey, signalId: signal.id }),
189
222
  children: rowContent
190
223
  }
191
224
  );
192
225
  }
193
- return /* @__PURE__ */ jsx("div", { className: "rounded-md bg-background/80 p-2 text-xs", children: rowContent });
226
+ return /* @__PURE__ */ jsx("div", { className: "rounded-md bg-background/80 p-3 text-xs", children: rowContent });
227
+ }
228
+ function hasStructuredData(signal) {
229
+ return Boolean(
230
+ signal.primaryValue || signal.qualifier || signal.counterparty || signal.components && signal.components.length > 0
231
+ );
232
+ }
233
+ function WhyCard({ bucket, signals, item, panelId, onOpenSignalBucket, onBucketFeedback }) {
234
+ var _a;
235
+ const [showAll, setShowAll] = React.useState(false);
236
+ const [bucketFeedback, setBucketFeedback] = React.useState(null);
237
+ const totalCount = (_a = bucket.signalCount) != null ? _a : signals.length;
238
+ const visibleSignals = showAll ? signals : signals.slice(0, DEFAULT_VISIBLE_ROWS);
239
+ const hiddenCount = signals.length - DEFAULT_VISIBLE_ROWS;
240
+ const useStructured = signals.some(hasStructuredData);
241
+ return /* @__PURE__ */ jsxs(
242
+ "div",
243
+ {
244
+ id: panelId,
245
+ className: "rounded-lg rounded-t-none border border-t-0 border-border bg-muted/20 p-3",
246
+ role: "region",
247
+ "aria-label": `${bucket.label} details`,
248
+ children: [
249
+ /* @__PURE__ */ jsx("div", { className: "mb-2 flex items-center justify-between", children: /* @__PURE__ */ jsxs("span", { className: "text-[10px] font-bold uppercase tracking-wider text-muted-foreground", children: [
250
+ totalCount,
251
+ " signal",
252
+ totalCount !== 1 ? "s" : "",
253
+ " \u2013 ",
254
+ bucket.label
255
+ ] }) }),
256
+ visibleSignals.length > 0 ? /* @__PURE__ */ jsx("ul", { className: "divide-y divide-border/30", "aria-label": "Matching signals", children: visibleSignals.map((signal, index) => {
257
+ var _a2;
258
+ return /* @__PURE__ */ jsx("li", { children: useStructured ? /* @__PURE__ */ jsx(
259
+ StructuredSignalRow,
260
+ {
261
+ item,
262
+ bucketKey: bucket.key,
263
+ signal,
264
+ tone: bucket.tone,
265
+ onOpenSignalBucket
266
+ }
267
+ ) : /* @__PURE__ */ jsx(
268
+ LegacySignalRow,
269
+ {
270
+ item,
271
+ bucketKey: bucket.key,
272
+ signal,
273
+ onOpenSignalBucket
274
+ }
275
+ ) }, (_a2 = signal.id) != null ? _a2 : `${bucket.key}-signal-${index}`);
276
+ }) }) : bucket.evidence && bucket.evidence.length > 0 ? /* @__PURE__ */ jsx("ul", { className: "mt-3 space-y-1.5", "aria-label": "Matching signals", children: bucket.evidence.map((evidence, index) => /* @__PURE__ */ jsxs("li", { className: "flex gap-2 text-xs text-muted-foreground", children: [
277
+ /* @__PURE__ */ jsx("span", { className: "mt-1.5 h-1 w-1 shrink-0 rounded-full bg-primary" }),
278
+ /* @__PURE__ */ jsx("span", { className: "leading-relaxed", children: evidence })
279
+ ] }, `${bucket.key}-evidence-${index}`)) }) : null,
280
+ !showAll && hiddenCount > 0 && /* @__PURE__ */ jsxs(
281
+ "button",
282
+ {
283
+ type: "button",
284
+ onClick: () => setShowAll(true),
285
+ className: "mt-2 flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground",
286
+ children: [
287
+ /* @__PURE__ */ jsx(ChevronDown, { className: "h-3 w-3" }),
288
+ "Show ",
289
+ hiddenCount,
290
+ " more"
291
+ ]
292
+ }
293
+ ),
294
+ onBucketFeedback && /* @__PURE__ */ jsx("div", { className: "mt-3 border-t border-border/40 pt-3", children: /* @__PURE__ */ jsx(
295
+ FeedbackFooter,
296
+ {
297
+ feedback: bucketFeedback,
298
+ onFeedbackChange: setBucketFeedback,
299
+ onSubmit: (data) => onBucketFeedback(bucket.key, data),
300
+ negativeChips: BUCKET_NEGATIVE_CHIPS,
301
+ negativePrompt: "Was this bucket useful?",
302
+ positivePrompt: "Thanks! What was useful about this bucket?"
303
+ }
304
+ ) })
305
+ ]
306
+ }
307
+ );
194
308
  }
195
309
  function ScoreWhyChips({
196
310
  item,
@@ -198,7 +312,7 @@ function ScoreWhyChips({
198
312
  onOpenSignalBucket,
199
313
  className
200
314
  }) {
201
- var _a, _b;
315
+ var _a;
202
316
  const [selectedBucketKey, setSelectedBucketKey] = React.useState(null);
203
317
  React.useEffect(() => {
204
318
  setSelectedBucketKey(null);
@@ -213,69 +327,41 @@ function ScoreWhyChips({
213
327
  return /* @__PURE__ */ jsxs("div", { className: cn("mt-4", className), children: [
214
328
  /* @__PURE__ */ jsx("div", { className: "mb-2 flex items-center gap-2", children: /* @__PURE__ */ jsx("span", { className: "text-[10px] font-bold uppercase tracking-wider text-muted-foreground", children: "Why" }) }),
215
329
  /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: buckets.map((bucket) => {
330
+ var _a2;
216
331
  const isSelected = selectedBucketKey === bucket.key;
217
332
  const panelId = `${idPrefix}-panel-${makeDomId(bucket.key)}`;
218
- return /* @__PURE__ */ jsxs(
219
- "button",
333
+ const signals = getBucketSignals(bucket);
334
+ const signalCount = (_a2 = bucket.signalCount) != null ? _a2 : signals.length;
335
+ return /* @__PURE__ */ jsx("div", { className: "flex flex-col", children: /* @__PURE__ */ jsx(
336
+ WhyPill,
220
337
  {
221
- type: "button",
222
- onClick: () => setSelectedBucketKey((prev) => prev === bucket.key ? null : bucket.key),
223
- "aria-expanded": isSelected,
224
- "aria-controls": panelId,
225
- className: cn(
226
- "inline-flex items-center gap-1.5 rounded-full border px-2.5 py-1 text-[11px] font-semibold transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
227
- isSelected ? "border-foreground/30 bg-foreground text-background" : "border-border bg-background text-muted-foreground hover:bg-muted/60 hover:text-foreground"
228
- ),
229
- children: [
230
- bucket.label,
231
- bucket.signalCount && bucket.signalCount > 1 ? /* @__PURE__ */ jsxs("span", { className: cn("rounded-full px-1.5 py-0 text-[10px]", isSelected ? "bg-background/20" : "bg-muted"), children: [
232
- "\xD7",
233
- bucket.signalCount
234
- ] }) : null
235
- ]
236
- },
237
- bucket.key
238
- );
338
+ bucket,
339
+ isSelected,
340
+ signalCount,
341
+ panelId,
342
+ onToggle: () => setSelectedBucketKey((prev) => prev === bucket.key ? null : bucket.key),
343
+ onClose: () => setSelectedBucketKey(null)
344
+ }
345
+ ) }, bucket.key);
239
346
  }) }),
240
- selectedBucket && /* @__PURE__ */ jsxs(
241
- "div",
347
+ selectedBucket && selectedPanelId && /* @__PURE__ */ jsx(
348
+ WhyCard,
242
349
  {
243
- id: selectedPanelId,
244
- className: "mt-3 rounded-lg border border-border bg-muted/20 p-3",
245
- role: "region",
246
- "aria-label": `${selectedBucket.label} details`,
247
- children: [
248
- /* @__PURE__ */ jsx("div", { className: "flex items-start justify-between gap-3", children: /* @__PURE__ */ jsxs("div", { children: [
249
- /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-foreground", children: selectedBucket.label }),
250
- /* @__PURE__ */ jsx("div", { className: "mt-1 flex flex-wrap gap-1.5 text-[11px] text-muted-foreground", children: /* @__PURE__ */ jsxs("span", { className: "rounded-full bg-background px-2 py-0.5", children: [
251
- (_b = selectedBucket.signalCount) != null ? _b : selectedBucketSignals.length,
252
- " signals"
253
- ] }) })
254
- ] }) }),
255
- selectedBucketSignals.length > 0 ? /* @__PURE__ */ jsx("ul", { className: "mt-3 space-y-2", "aria-label": "Matching signals", children: selectedBucketSignals.map((signal, index) => {
256
- var _a2;
257
- return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
258
- SignalRow,
259
- {
260
- item,
261
- bucketKey: selectedBucket.key,
262
- signal,
263
- onOpenSignalBucket
264
- }
265
- ) }, (_a2 = signal.id) != null ? _a2 : `${selectedBucket.key}-signal-${index}`);
266
- }) }) : selectedBucket.evidence && selectedBucket.evidence.length > 0 ? /* @__PURE__ */ jsx("ul", { className: "mt-3 space-y-1.5", "aria-label": "Matching signals", children: selectedBucket.evidence.map((evidence, index) => /* @__PURE__ */ jsxs("li", { className: "flex gap-2 text-xs text-muted-foreground", children: [
267
- /* @__PURE__ */ jsx("span", { className: "mt-1.5 h-1 w-1 shrink-0 rounded-full bg-primary" }),
268
- /* @__PURE__ */ jsx("span", { className: "leading-relaxed", children: evidence })
269
- ] }, `${selectedBucket.key}-evidence-${index}`)) }) : null
270
- ]
350
+ bucket: selectedBucket,
351
+ signals: selectedBucketSignals,
352
+ item,
353
+ panelId: selectedPanelId,
354
+ onOpenSignalBucket,
355
+ onBucketFeedback: signalData.onBucketFeedback
271
356
  }
272
357
  )
273
358
  ] });
274
359
  }
275
360
  export {
361
+ DEFAULT_TONE_CLASS,
362
+ SIGNAL_TONE_CLASSES,
276
363
  ScoreWhyChips,
277
- SignalPriorityChip,
278
- SignalPriorityPanel,
279
- getSignalScoreUrgencyLabel
364
+ getSignalScoreUrgencyLabel,
365
+ scoreRangeForUrgency
280
366
  };
281
367
  //# sourceMappingURL=score-why-chips.js.map