@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.
Files changed (73) hide show
  1. package/dist/charts/chart.d.ts +1 -1
  2. package/dist/charts/index.d.ts +0 -1
  3. package/dist/charts/index.js +0 -1
  4. package/dist/charts/index.js.map +1 -1
  5. package/dist/charts/pipeline-overview.d.ts +1 -2
  6. package/dist/charts/pipeline-overview.js +1 -29
  7. package/dist/charts/pipeline-overview.js.map +1 -1
  8. package/dist/components/feedback-primitives.d.ts +21 -2
  9. package/dist/components/feedback-primitives.js +90 -6
  10. package/dist/components/feedback-primitives.js.map +1 -1
  11. package/dist/components/insights-filter-bar.d.ts +1 -2
  12. package/dist/components/insights-filter-bar.js +5 -13
  13. package/dist/components/insights-filter-bar.js.map +1 -1
  14. package/dist/components/metric-card.d.ts +1 -14
  15. package/dist/components/metric-card.js +0 -86
  16. package/dist/components/metric-card.js.map +1 -1
  17. package/dist/components/score-why-chips.d.ts +1 -1
  18. package/dist/components/score-why-chips.js +26 -5
  19. package/dist/components/score-why-chips.js.map +1 -1
  20. package/dist/components/signal-priority-popover.d.ts +1 -1
  21. package/dist/components/signal-priority-popover.js +172 -7
  22. package/dist/components/signal-priority-popover.js.map +1 -1
  23. package/dist/index.d.ts +3 -9
  24. package/dist/index.js +0 -5
  25. package/dist/index.js.map +1 -1
  26. package/dist/prototype/index.d.ts +1 -1
  27. package/dist/prototype/prototype-accounts-view.d.ts +1 -1
  28. package/dist/prototype/prototype-admin-view.d.ts +1 -1
  29. package/dist/prototype/prototype-config.d.ts +1 -1
  30. package/dist/prototype/prototype-inbox-view.d.ts +1 -1
  31. package/dist/prototype/prototype-inbox-view.js +4 -1
  32. package/dist/prototype/prototype-inbox-view.js.map +1 -1
  33. package/dist/prototype/prototype-insights-view.d.ts +1 -1
  34. package/dist/prototype/prototype-shell.d.ts +1 -1
  35. package/dist/{signal-priority-popover-DQ_VuHac.d.ts → signal-priority-popover-DWaAMhPI.d.ts} +26 -2
  36. package/package.json +3 -1
  37. package/src/charts/index.ts +0 -1
  38. package/src/charts/pipeline-overview.tsx +1 -38
  39. package/src/components/__tests__/wit-636-feedback-states.test.tsx +546 -0
  40. package/src/components/feedback-primitives.tsx +148 -26
  41. package/src/components/insights-filter-bar.tsx +4 -13
  42. package/src/components/metric-card.tsx +0 -82
  43. package/src/components/score-why-chips.tsx +28 -2
  44. package/src/components/signal-priority-popover.tsx +194 -3
  45. package/src/index.ts +1 -6
  46. package/src/prototype/prototype-config.ts +11 -1
  47. package/src/prototype/prototype-inbox-view.tsx +3 -0
  48. package/dist/charts/empty-chart-state.d.ts +0 -11
  49. package/dist/charts/empty-chart-state.js +0 -70
  50. package/dist/charts/empty-chart-state.js.map +0 -1
  51. package/dist/components/days-open-cell.d.ts +0 -16
  52. package/dist/components/days-open-cell.js +0 -73
  53. package/dist/components/days-open-cell.js.map +0 -1
  54. package/dist/components/detail-drawer.d.ts +0 -16
  55. package/dist/components/detail-drawer.js +0 -45
  56. package/dist/components/detail-drawer.js.map +0 -1
  57. package/dist/components/linked-entity-cell.d.ts +0 -14
  58. package/dist/components/linked-entity-cell.js +0 -96
  59. package/dist/components/linked-entity-cell.js.map +0 -1
  60. package/dist/components/pill.d.ts +0 -26
  61. package/dist/components/pill.js +0 -77
  62. package/dist/components/pill.js.map +0 -1
  63. package/dist/components/quick-segment.d.ts +0 -13
  64. package/dist/components/quick-segment.js +0 -96
  65. package/dist/components/quick-segment.js.map +0 -1
  66. package/src/charts/__tests__/insights-charts.test.tsx +0 -62
  67. package/src/charts/empty-chart-state.tsx +0 -44
  68. package/src/components/__tests__/insights-primitives.test.tsx +0 -117
  69. package/src/components/days-open-cell.tsx +0 -50
  70. package/src/components/detail-drawer.tsx +0 -60
  71. package/src/components/linked-entity-cell.tsx +0 -74
  72. package/src/components/pill.tsx +0 -67
  73. package/src/components/quick-segment.tsx +0 -68
@@ -1,7 +1,7 @@
1
1
  "use client"
2
2
 
3
3
  import * as React from "react"
4
- import { ThumbsUp, ThumbsDown } from "lucide-react"
4
+ import { ThumbsUp, ThumbsDown, Check, Pencil } from "lucide-react"
5
5
  import { cn } from "../lib/utils"
6
6
 
7
7
  // ---------------------------------------------------------------------------
@@ -25,6 +25,19 @@ export interface FeedbackSubmitData {
25
25
  detail: string
26
26
  }
27
27
 
28
+ /**
29
+ * Persisted feedback data from a previous submission, used to hydrate the
30
+ * footer into its "already submitted" visual state.
31
+ */
32
+ export interface PersistedFeedbackData {
33
+ sentiment: "positive" | "negative"
34
+ reasonTop?: string
35
+ reasonSub?: string
36
+ pills?: string[]
37
+ detail?: string
38
+ ownershipLabel: "Your feedback" | "Team feedback"
39
+ }
40
+
28
41
  /**
29
42
  * Defines a tier-1 chip that may have tier-2 sub-chips.
30
43
  */
@@ -185,6 +198,13 @@ export interface FeedbackFooterProps {
185
198
  negativeChips?: FeedbackChipTree[]
186
199
  positiveChips?: string[]
187
200
  className?: string
201
+ /** Pre-existing feedback to hydrate from (e.g. after page reload). */
202
+ initialFeedback?: PersistedFeedbackData | null
203
+ /** Label shown in the transient confirmation pill after submit. */
204
+ submittedLabel?: string
205
+ /** Stable key for syncing initialFeedback into local state. When this
206
+ * changes, the component resets to the new initialFeedback value. */
207
+ feedbackKey?: string
188
208
  }
189
209
 
190
210
  const SENTIMENT_BUTTON_ACTIVE: Record<"positive" | "negative", string> = {
@@ -205,6 +225,9 @@ export function FeedbackFooter({
205
225
  negativeChips = [],
206
226
  positiveChips = [],
207
227
  className,
228
+ initialFeedback,
229
+ submittedLabel = "Saved",
230
+ feedbackKey,
208
231
  }: FeedbackFooterProps) {
209
232
  const [expanded, setExpanded] = React.useState(false)
210
233
  const [selectedTier1, setSelectedTier1] = React.useState<string | null>(null)
@@ -214,6 +237,43 @@ export function FeedbackFooter({
214
237
  const [activeTreeIndex, setActiveTreeIndex] = React.useState<number | null>(
215
238
  null,
216
239
  )
240
+ /** Transient "Saved" confirmation — shown after successful submit. */
241
+ const [submitted, setSubmitted] = React.useState(false)
242
+ /** Persisted feedback shown as a clickable indicator (survives reload). */
243
+ const [persisted, setPersisted] = React.useState<PersistedFeedbackData | null>(
244
+ initialFeedback ?? null,
245
+ )
246
+ /** Tracks whether the user is actively editing (to guard against prop overwrites). */
247
+ const [isEditing, setIsEditing] = React.useState(false)
248
+ /** Track the last synced feedbackKey to detect key changes. */
249
+ const lastKeyRef = React.useRef<string | undefined>(feedbackKey)
250
+
251
+ // Sync initialFeedback into local state via useEffect keyed on feedbackKey.
252
+ // When feedbackKey changes, reset to new target. Preserve active edits
253
+ // when feedbackKey stays the same.
254
+ React.useEffect(() => {
255
+ const keyChanged = feedbackKey !== lastKeyRef.current
256
+ lastKeyRef.current = feedbackKey
257
+
258
+ if (keyChanged) {
259
+ // Key changed — full reset to new target
260
+ setPersisted(initialFeedback ?? null)
261
+ setSubmitted(false)
262
+ setExpanded(false)
263
+ setIsEditing(false)
264
+ if (initialFeedback) {
265
+ onFeedbackChange(initialFeedback.sentiment)
266
+ } else {
267
+ onFeedbackChange(null)
268
+ }
269
+ } else if (!isEditing) {
270
+ // Same key, not actively editing — safe to sync
271
+ setPersisted(initialFeedback ?? null)
272
+ if (initialFeedback) {
273
+ onFeedbackChange(initialFeedback.sentiment)
274
+ }
275
+ }
276
+ }, [initialFeedback, feedbackKey]) // eslint-disable-line react-hooks/exhaustive-deps -- reads isEditing as guard, not trigger
217
277
 
218
278
  // Reset state when feedback collapses
219
279
  const resetState = React.useCallback(() => {
@@ -223,6 +283,7 @@ export function FeedbackFooter({
223
283
  setAdditionalPills([])
224
284
  setDetailText("")
225
285
  setActiveTreeIndex(null)
286
+ setIsEditing(false)
226
287
  }, [])
227
288
 
228
289
  const handleSentimentClick = React.useCallback(
@@ -231,10 +292,26 @@ export function FeedbackFooter({
231
292
  // Reset chip state when switching sentiment, then expand
232
293
  resetState()
233
294
  setExpanded(true)
295
+ setSubmitted(false)
296
+ setPersisted(null)
297
+ setIsEditing(true)
234
298
  },
235
299
  [onFeedbackChange, resetState],
236
300
  )
237
301
 
302
+ /** Open the persisted indicator for editing. */
303
+ const handlePersistedClick = React.useCallback(() => {
304
+ if (!persisted) return
305
+ onFeedbackChange(persisted.sentiment)
306
+ setSelectedTier1(persisted.reasonTop ?? null)
307
+ setSelectedTier2(persisted.reasonSub ?? null)
308
+ setAdditionalPills(persisted.pills ?? [])
309
+ setDetailText(persisted.detail ?? "")
310
+ setExpanded(true)
311
+ setSubmitted(false)
312
+ setIsEditing(true)
313
+ }, [persisted, onFeedbackChange])
314
+
238
315
  const handleTier1Toggle = React.useCallback(
239
316
  (chipLabel: string) => {
240
317
  if (selectedTier1 === chipLabel) {
@@ -295,7 +372,16 @@ export function FeedbackFooter({
295
372
  pills: additionalPills,
296
373
  detail: detailText,
297
374
  })
298
- resetState()
375
+ // Show transient "Saved" confirmation
376
+ setSubmitted(true)
377
+ // Collapse expansion but keep sentiment visible
378
+ setExpanded(false)
379
+ setSelectedTier1(null)
380
+ setSelectedTier2(null)
381
+ setAdditionalPills([])
382
+ setDetailText("")
383
+ setActiveTreeIndex(null)
384
+ setIsEditing(false)
299
385
  }, [
300
386
  feedback,
301
387
  selectedTier1,
@@ -303,7 +389,6 @@ export function FeedbackFooter({
303
389
  additionalPills,
304
390
  detailText,
305
391
  onSubmit,
306
- resetState,
307
392
  ])
308
393
 
309
394
  const handleCancel = React.useCallback(() => {
@@ -323,38 +408,75 @@ export function FeedbackFooter({
323
408
  const activeTree =
324
409
  activeTreeIndex !== null ? negativeChips[activeTreeIndex] : null
325
410
 
411
+ // Determine if we should show the persisted indicator instead of bare buttons
412
+ const showPersistedIndicator = persisted && !expanded && !submitted
413
+
326
414
  return (
327
415
  <div className={cn("space-y-3", className)}>
328
416
  {/* Sentiment buttons + meta text bar */}
329
417
  <div className="flex items-center justify-between">
330
- <div className="flex items-center gap-3">
418
+ {showPersistedIndicator ? (
419
+ /* Persisted feedback indicator — clickable to reopen editor */
331
420
  <button
332
421
  type="button"
333
- onClick={() => handleSentimentClick("positive")}
334
- className={cn(
335
- "flex gap-1 items-center text-[11px] font-medium rounded-md px-2 py-1 transition-colors",
336
- feedback === "positive"
337
- ? SENTIMENT_BUTTON_ACTIVE.positive
338
- : SENTIMENT_BUTTON_IDLE,
339
- )}
422
+ onClick={handlePersistedClick}
423
+ className="group flex items-center gap-1.5 text-[11px] text-muted-foreground hover:text-foreground transition-colors"
424
+ data-testid="persisted-feedback-indicator"
340
425
  >
341
- <ThumbsUp className="h-[11px] w-[11px]" />
342
- Helpful
343
- </button>
344
- <button
345
- type="button"
346
- onClick={() => handleSentimentClick("negative")}
347
- className={cn(
348
- "flex gap-1 items-center text-[11px] font-medium rounded-md px-2 py-1 transition-colors",
349
- feedback === "negative"
350
- ? SENTIMENT_BUTTON_ACTIVE.negative
351
- : SENTIMENT_BUTTON_IDLE,
426
+ <span className="font-medium">{persisted.ownershipLabel}:</span>
427
+ {persisted.sentiment === "positive" ? (
428
+ <ThumbsUp className="h-[11px] w-[11px]" />
429
+ ) : (
430
+ <ThumbsDown className="h-[11px] w-[11px]" />
352
431
  )}
353
- >
354
- <ThumbsDown className="h-[11px] w-[11px]" />
355
- Not helpful
432
+ {persisted.detail && (
433
+ <span className="max-w-[200px] truncate text-muted-foreground/70">
434
+ {persisted.detail}
435
+ </span>
436
+ )}
437
+ <Pencil className="h-[9px] w-[9px] opacity-0 group-hover:opacity-100 transition-opacity" />
356
438
  </button>
357
- </div>
439
+ ) : (
440
+ <div className="flex items-center gap-3">
441
+ <button
442
+ type="button"
443
+ onClick={() => handleSentimentClick("positive")}
444
+ className={cn(
445
+ "flex gap-1 items-center text-[11px] font-medium rounded-md px-2 py-1 transition-colors",
446
+ feedback === "positive"
447
+ ? SENTIMENT_BUTTON_ACTIVE.positive
448
+ : SENTIMENT_BUTTON_IDLE,
449
+ )}
450
+ >
451
+ <ThumbsUp className="h-[11px] w-[11px]" />
452
+ Helpful
453
+ </button>
454
+ <button
455
+ type="button"
456
+ onClick={() => handleSentimentClick("negative")}
457
+ className={cn(
458
+ "flex gap-1 items-center text-[11px] font-medium rounded-md px-2 py-1 transition-colors",
459
+ feedback === "negative"
460
+ ? SENTIMENT_BUTTON_ACTIVE.negative
461
+ : SENTIMENT_BUTTON_IDLE,
462
+ )}
463
+ >
464
+ <ThumbsDown className="h-[11px] w-[11px]" />
465
+ Not helpful
466
+ </button>
467
+ {/* Transient "Saved" confirmation pill */}
468
+ {submitted && feedback && (
469
+ <span
470
+ className="inline-flex items-center gap-1 text-[11px] font-medium text-emerald-600"
471
+ role="status"
472
+ data-testid="feedback-submitted-pill"
473
+ >
474
+ <Check className="h-[11px] w-[11px]" />
475
+ {submittedLabel}
476
+ </span>
477
+ )}
478
+ </div>
479
+ )}
358
480
  {metaText && (
359
481
  <span className="text-[11px] text-muted-foreground">{metaText}</span>
360
482
  )}
@@ -23,7 +23,6 @@ export interface FilterDefinition {
23
23
 
24
24
  export interface InsightsFilterBarProps {
25
25
  filters: FilterDefinition[]
26
- variant?: "default" | "compact"
27
26
  values: Record<string, string>
28
27
  onChange: (filterId: string, value: string) => void
29
28
  onClearAll?: () => void
@@ -46,7 +45,6 @@ function InsightsFilterBar({
46
45
  onChange,
47
46
  onClearAll,
48
47
  className,
49
- variant = "default",
50
48
  }: InsightsFilterBarProps) {
51
49
  const showClearAll = onClearAll && hasNonDefaultValue(filters, values)
52
50
 
@@ -54,12 +52,11 @@ function InsightsFilterBar({
54
52
  <div
55
53
  data-slot="insights-filter-bar"
56
54
  className={cn(
57
- "flex flex-wrap items-center rounded-md border border-border bg-card shadow-sm",
58
- variant === "compact" ? "gap-2 p-2" : "gap-3 p-4",
55
+ "flex flex-wrap items-center gap-3 rounded-md border border-border bg-card p-4 shadow-sm",
59
56
  className
60
57
  )}
61
58
  >
62
- <div className={cn("flex items-center gap-2", variant === "compact" && "sr-only")}>
59
+ <div className="flex items-center gap-2">
63
60
  <FilterIcon className="h-4 w-4 text-muted-foreground" />
64
61
  <span className="text-sm font-medium text-muted-foreground">
65
62
  Filters:
@@ -83,10 +80,7 @@ function InsightsFilterBar({
83
80
  <Button
84
81
  variant="outline"
85
82
  size="sm"
86
- className={cn(
87
- "gap-1.5 text-xs font-normal shadow-none",
88
- variant === "compact" ? "h-7 px-2" : "h-8"
89
- )}
83
+ className="h-8 gap-1.5 text-xs font-normal shadow-none"
90
84
  >
91
85
  {IconComp ? (
92
86
  <IconComp className="h-3.5 w-3.5 text-muted-foreground" />
@@ -124,10 +118,7 @@ function InsightsFilterBar({
124
118
  <Button
125
119
  variant="ghost"
126
120
  size="sm"
127
- className={cn(
128
- "text-xs text-destructive hover:text-destructive",
129
- variant === "compact" ? "h-7 px-2" : "h-8"
130
- )}
121
+ className="h-8 text-xs text-destructive hover:text-destructive"
131
122
  onClick={onClearAll}
132
123
  >
133
124
  Clear All
@@ -24,88 +24,6 @@ export interface MetricCardProps {
24
24
  showInfo?: boolean
25
25
  }
26
26
 
27
- export interface KpiStripItem {
28
- id?: string
29
- label: React.ReactNode
30
- value: React.ReactNode
31
- unit?: React.ReactNode
32
- subtitle?: React.ReactNode
33
- change?: MetricCardProps["change"]
34
- }
35
-
36
- export interface KpiStripProps extends React.HTMLAttributes<HTMLDivElement> {
37
- items: KpiStripItem[]
38
- columns?: 2 | 3 | 4
39
- }
40
-
41
- export function KpiStrip({ items, columns = 4, className, ...props }: KpiStripProps) {
42
- return (
43
- <div
44
- data-slot="kpi-strip"
45
- className={cn(
46
- "grid gap-3 rounded-xl border border-border bg-card p-3 shadow-sm",
47
- columns === 2 && "sm:grid-cols-2",
48
- columns === 3 && "sm:grid-cols-3",
49
- columns === 4 && "sm:grid-cols-2 lg:grid-cols-4",
50
- className
51
- )}
52
- {...props}
53
- >
54
- {items.map((item, index) => {
55
- const isGoodDirection = item.change
56
- ? item.change.isGood !== undefined
57
- ? item.change.isGood
58
- : item.change.direction === "up"
59
- : false
60
- const ChangeIcon = item.change?.direction === "down" ? ArrowDown : ArrowUp
61
-
62
- return (
63
- <div
64
- key={item.id ?? index}
65
- data-slot="kpi-strip-item"
66
- className="min-w-0 rounded-lg bg-muted/40 px-3 py-2"
67
- >
68
- <div data-slot="kpi-strip-label" className="truncate text-xs font-medium text-muted-foreground">
69
- {item.label}
70
- </div>
71
- <div className="mt-1 flex items-baseline gap-1">
72
- <span data-slot="kpi-strip-value" className="truncate text-2xl font-bold tracking-tight text-foreground">
73
- {item.value}
74
- </span>
75
- {item.unit ? (
76
- <span data-slot="kpi-strip-unit" className="text-sm font-semibold text-muted-foreground">
77
- {item.unit}
78
- </span>
79
- ) : null}
80
- </div>
81
- {item.subtitle || item.change ? (
82
- <div className="mt-1 flex items-center gap-2 text-xs">
83
- {item.change ? (
84
- <span
85
- data-slot="kpi-strip-change"
86
- className={cn(
87
- "inline-flex items-center gap-0.5 font-semibold",
88
- isGoodDirection ? "text-emerald-600" : "text-red-600"
89
- )}
90
- >
91
- <ChangeIcon className="h-3 w-3 stroke-[3]" />
92
- {item.change.value}
93
- </span>
94
- ) : null}
95
- {item.subtitle ? (
96
- <span data-slot="kpi-strip-subtitle" className="truncate text-muted-foreground">
97
- {item.subtitle}
98
- </span>
99
- ) : null}
100
- </div>
101
- ) : null}
102
- </div>
103
- )
104
- })}
105
- </div>
106
- )
107
- }
108
-
109
27
  export function MetricCard({
110
28
  title,
111
29
  value,
@@ -15,7 +15,7 @@ import {
15
15
  } from "lucide-react"
16
16
  import type { LucideIcon } from "lucide-react"
17
17
  import { FeedbackFooter } from "./feedback-primitives"
18
- import type { FeedbackChipTree, FeedbackSubmitData } from "./feedback-primitives"
18
+ import type { FeedbackChipTree, FeedbackSubmitData, PersistedFeedbackData } from "./feedback-primitives"
19
19
  import { cn } from "../lib/utils"
20
20
  import type {
21
21
  QueueItem,
@@ -266,6 +266,7 @@ function StructuredSignalRow({ item, bucketKey, signal, tone, onOpenSignalBucket
266
266
  const IconComponent = resolveIcon(signal.signalTypeName)
267
267
  const toneClass = tone ? (SIGNAL_TONE_CLASSES[tone] ?? DEFAULT_TONE_CLASS) : DEFAULT_TONE_CLASS
268
268
  const isCombined = signal.signalTypeName === "combined_signal" && signal.components && signal.components.length > 0
269
+ const hasBalance = Boolean(signal.currentBalance || signal.balanceContext)
269
270
 
270
271
  const rowContent = (
271
272
  <>
@@ -304,6 +305,26 @@ function StructuredSignalRow({ item, bucketKey, signal, tone, onOpenSignalBucket
304
305
 
305
306
  {/* Slot 5: Chevron */}
306
307
  <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" />
308
+
309
+ {/* Balance context strip — spans full row below grid columns */}
310
+ {hasBalance && (
311
+ <div
312
+ className="col-span-full mt-0.5 text-[10px] text-muted-foreground/70"
313
+ data-testid="balance-context-strip"
314
+ >
315
+ {signal.currentBalance && (
316
+ <span>
317
+ Current balance <span className="font-medium text-muted-foreground">{signal.currentBalance}</span>
318
+ </span>
319
+ )}
320
+ {signal.balanceContext && (
321
+ <span>
322
+ {signal.currentBalance ? " · " : ""}
323
+ {signal.balanceContext}
324
+ </span>
325
+ )}
326
+ </div>
327
+ )}
307
328
  </>
308
329
  )
309
330
 
@@ -405,9 +426,11 @@ interface WhyCardProps {
405
426
  panelId: string
406
427
  onOpenSignalBucket?: ScoreWhyChipsProps["onOpenSignalBucket"]
407
428
  onBucketFeedback?: (bucketKey: string, data: FeedbackSubmitData) => void
429
+ /** Persisted bucket-level feedback to hydrate from. */
430
+ initialBucketFeedback?: PersistedFeedbackData | null
408
431
  }
409
432
 
410
- function WhyCard({ bucket, signals, item, panelId, onOpenSignalBucket, onBucketFeedback }: WhyCardProps) {
433
+ function WhyCard({ bucket, signals, item, panelId, onOpenSignalBucket, onBucketFeedback, initialBucketFeedback }: WhyCardProps) {
411
434
  const [showAll, setShowAll] = React.useState(false)
412
435
  const [bucketFeedback, setBucketFeedback] = React.useState<"positive" | "negative" | null>(null)
413
436
  const totalCount = bucket.signalCount ?? signals.length
@@ -488,6 +511,8 @@ function WhyCard({ bucket, signals, item, panelId, onOpenSignalBucket, onBucketF
488
511
  negativeChips={BUCKET_NEGATIVE_CHIPS}
489
512
  negativePrompt="Was this bucket useful?"
490
513
  positivePrompt="Thanks! What was useful about this bucket?"
514
+ initialFeedback={initialBucketFeedback}
515
+ feedbackKey={bucket.key}
491
516
  />
492
517
  </div>
493
518
  )}
@@ -561,6 +586,7 @@ export function ScoreWhyChips({
561
586
  panelId={selectedPanelId}
562
587
  onOpenSignalBucket={onOpenSignalBucket}
563
588
  onBucketFeedback={signalData.onBucketFeedback}
589
+ initialBucketFeedback={signalData.initialBucketFeedback?.[selectedBucket.key]}
564
590
  />
565
591
  )}
566
592
  </div>