@handled-ai/design-system 0.18.50 → 0.18.52

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 (36) hide show
  1. package/dist/components/badge.d.ts +1 -1
  2. package/dist/components/button.d.ts +1 -1
  3. package/dist/components/data-table-filter.d.ts +21 -6
  4. package/dist/components/data-table-filter.js +134 -9
  5. package/dist/components/data-table-filter.js.map +1 -1
  6. package/dist/components/pill.d.ts +1 -1
  7. package/dist/components/score-why-chips.d.ts +1 -1
  8. package/dist/components/signal-feedback-inline.d.ts +28 -12
  9. package/dist/components/signal-feedback-inline.js +146 -10
  10. package/dist/components/signal-feedback-inline.js.map +1 -1
  11. package/dist/components/signal-priority-popover.d.ts +1 -1
  12. package/dist/components/signal-priority-popover.js +7 -15
  13. package/dist/components/signal-priority-popover.js.map +1 -1
  14. package/dist/components/tabs.d.ts +1 -1
  15. package/dist/index.d.ts +3 -3
  16. package/dist/prototype/index.d.ts +1 -1
  17. package/dist/prototype/prototype-accounts-view.d.ts +1 -1
  18. package/dist/prototype/prototype-admin-view.d.ts +1 -1
  19. package/dist/prototype/prototype-config.d.ts +1 -1
  20. package/dist/prototype/prototype-inbox-view.d.ts +3 -3
  21. package/dist/prototype/prototype-inbox-view.js +1 -2
  22. package/dist/prototype/prototype-inbox-view.js.map +1 -1
  23. package/dist/prototype/prototype-insights-view.d.ts +1 -1
  24. package/dist/prototype/prototype-shell.d.ts +1 -1
  25. package/dist/{signal-priority-popover-Cl98xw1n.d.ts → signal-priority-popover-QJngMAj7.d.ts} +4 -9
  26. package/package.json +1 -1
  27. package/src/components/__tests__/case-panel-why.test.tsx +126 -0
  28. package/src/components/__tests__/data-table-filter.test.tsx +130 -0
  29. package/src/components/__tests__/signal-priority-popover.test.tsx +4 -27
  30. package/src/components/data-table-filter.tsx +160 -9
  31. package/src/components/signal-feedback-inline.tsx +181 -20
  32. package/src/components/signal-priority-popover.tsx +6 -16
  33. package/src/prototype/__tests__/detail-view-opportunity-preview.test.tsx +90 -0
  34. package/src/prototype/__tests__/detail-view-score-why.test.tsx +0 -32
  35. package/src/prototype/prototype-config.ts +3 -5
  36. package/src/prototype/prototype-inbox-view.tsx +3 -4
@@ -73,6 +73,32 @@ const approveReasons = [
73
73
 
74
74
  type ApprovalState = "pending" | "confirming" | "creating" | "approving-feedback" | "dismissing" | "approved" | "dismissed" | "auto-approved"
75
75
 
76
+ interface OpportunityPreviewOption {
77
+ value: string
78
+ label: string
79
+ }
80
+
81
+ interface OpportunityPreview {
82
+ name: string
83
+ stage: string
84
+ closeDate: string
85
+ closeDateValue?: string
86
+ amount: string
87
+ /** Raw draft input value. Numeric values render as currency in the editable field. */
88
+ amountValue?: string | number | null
89
+ accountName: string
90
+ description?: string | null
91
+ churnType?: string | null
92
+ churnTypeOptions?: Array<string | OpportunityPreviewOption>
93
+ }
94
+
95
+ interface OpportunityDraft {
96
+ closeDate: string
97
+ amount: string
98
+ description: string
99
+ churnType: string
100
+ }
101
+
76
102
  interface SignalApprovalLabels {
77
103
  approveButton?: string
78
104
  dismissButton?: string
@@ -106,9 +132,9 @@ interface SignalApprovalContextValue {
106
132
  labels: Required<SignalApprovalLabels>
107
133
  hideApproveButton?: boolean
108
134
  approveButtonIconUrl?: string
109
- opportunityPreview?: RootProps['opportunityPreview']
135
+ opportunityPreview?: OpportunityPreview
110
136
  requestingApproval: boolean
111
- approve: () => void
137
+ approve: (draft?: OpportunityDraft) => void
112
138
  submitApproveFeedback: (reasons: string[], detail: string) => void
113
139
  skipApproveFeedback: () => void
114
140
  dismiss: (reasons: string[], detail: string, subReason?: string) => void
@@ -137,13 +163,7 @@ interface RootProps {
137
163
  /** Optional icon URL for the approve button. Renders an img instead of CirclePlus when provided. */
138
164
  approveButtonIconUrl?: string
139
165
  /** Optional structured preview data shown in the confirmation dialog. */
140
- opportunityPreview?: {
141
- name: string
142
- stage: string
143
- closeDate: string
144
- amount: string
145
- accountName: string
146
- }
166
+ opportunityPreview?: OpportunityPreview
147
167
  /**
148
168
  * Async callback fired when the user clicks the approve button, BEFORE
149
169
  * transitioning to the "confirming" state. While the promise is pending,
@@ -160,7 +180,7 @@ interface RootProps {
160
180
  * "creating" loading state while the promise is pending. On `true` it
161
181
  * transitions to the feedback step; on `false` it reverts to "pending".
162
182
  */
163
- onApprove?: () => void | Promise<boolean>
183
+ onApprove?: (draft?: OpportunityDraft) => void | Promise<boolean>
164
184
  onApproveFeedback?: (reasons: string[], detail: string) => void
165
185
  onDismiss?: (reasons: string[], detail: string, subReason?: string) => void
166
186
  }
@@ -174,6 +194,7 @@ function Root({ children, companyName, opportunityUrl, scheduledTime, initialApp
174
194
  // an async onApprove promise is still in flight).
175
195
  const mountedRef = React.useRef(true)
176
196
  React.useEffect(() => {
197
+ mountedRef.current = true
177
198
  return () => { mountedRef.current = false }
178
199
  }, [])
179
200
 
@@ -205,8 +226,8 @@ function Root({ children, companyName, opportunityUrl, scheduledTime, initialApp
205
226
  setApprovalState("pending")
206
227
  }, [])
207
228
 
208
- const approve = React.useCallback(() => {
209
- const result = onApprove?.()
229
+ const approve = React.useCallback((draft?: OpportunityDraft) => {
230
+ const result = onApprove?.(draft)
210
231
  // If the callback returns a Promise, show a loading state and wait for it.
211
232
  if (result && typeof (result as Promise<boolean>).then === "function") {
212
233
  setApprovalState("creating")
@@ -417,6 +438,49 @@ function SubmittedFeedback({
417
438
  )
418
439
  }
419
440
 
441
+ function optionValue(option: string | OpportunityPreviewOption): string {
442
+ return typeof option === "string" ? option : option.value
443
+ }
444
+
445
+ function optionLabel(option: string | OpportunityPreviewOption): string {
446
+ return typeof option === "string" ? option : option.label
447
+ }
448
+
449
+ function formatAmountDraftValue(value: string | number | null | undefined): string {
450
+ if (value == null || value === "") return ""
451
+ if (typeof value === "number") {
452
+ return new Intl.NumberFormat("en-US", {
453
+ style: "currency",
454
+ currency: "USD",
455
+ maximumFractionDigits: 0,
456
+ }).format(value)
457
+ }
458
+ return value
459
+ }
460
+
461
+ function buildOpportunityDraft(preview?: OpportunityPreview): OpportunityDraft {
462
+ return {
463
+ closeDate: preview?.closeDateValue ?? preview?.closeDate ?? "",
464
+ amount: preview?.amountValue === undefined
465
+ ? preview?.amount ?? ""
466
+ : formatAmountDraftValue(preview.amountValue),
467
+ description: preview?.description ?? "",
468
+ churnType: preview?.churnType ?? "",
469
+ }
470
+ }
471
+
472
+ function hasEditableOpportunityPreview(preview?: OpportunityPreview): boolean {
473
+ return !!preview && isValidDateInput(preview.closeDateValue ?? preview.closeDate)
474
+ }
475
+
476
+ function isValidDateInput(value: string): boolean {
477
+ const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value)
478
+ if (!match) return false
479
+ const parsed = new Date(`${value}T00:00:00Z`)
480
+ if (Number.isNaN(parsed.getTime())) return false
481
+ return parsed.toISOString().slice(0, 10) === value
482
+ }
483
+
420
484
  function Actions() {
421
485
  const { approvalState, companyName, opportunityUrl, scheduledTime, labels, hideApproveButton, approveButtonIconUrl, opportunityPreview, requestingApproval, approve, submitApproveFeedback, skipApproveFeedback, dismiss, requestApproval, requestDismiss, cancel } =
422
486
  useSignalApproval()
@@ -426,6 +490,16 @@ function Actions() {
426
490
  const [detailText, setDetailText] = React.useState("")
427
491
  const [submittedFeedback, setSubmittedFeedback] = React.useState<{ reasons: string[]; detail: string; subReason?: string } | null>(null)
428
492
  const [isEditing, setIsEditing] = React.useState(false)
493
+ const [opportunityDraft, setOpportunityDraft] = React.useState<OpportunityDraft>(() => buildOpportunityDraft(opportunityPreview))
494
+
495
+ React.useEffect(() => {
496
+ if (approvalState === "confirming") {
497
+ setOpportunityDraft(buildOpportunityDraft(opportunityPreview))
498
+ }
499
+ }, [approvalState, opportunityPreview])
500
+
501
+ const churnTypeOptions = opportunityPreview?.churnTypeOptions ?? []
502
+ const hasChurnTypeOptions = churnTypeOptions.length > 0
429
503
 
430
504
  const topNode = dismissReasonTree.find((n) => n.label === selectedTopReason)
431
505
  const hasSubOptions = !!(topNode?.subOptions && topNode.subOptions.length > 0)
@@ -438,6 +512,8 @@ function Actions() {
438
512
  (!needsText || detailText.trim().length > 0)
439
513
 
440
514
  const canSubmitApprove = selectedReasons.length > 0 || detailText.trim().length > 0
515
+ const isEditableOpportunityPreview = hasEditableOpportunityPreview(opportunityPreview)
516
+ const canConfirmOpportunity = !isEditableOpportunityPreview || isValidDateInput(opportunityDraft.closeDate)
441
517
 
442
518
  const selectTopReason = (label: string) => {
443
519
  if (selectedTopReason === label) {
@@ -739,8 +815,8 @@ function Actions() {
739
815
  <p className="text-sm text-foreground">
740
816
  {labels.confirmPrompt} <strong>{companyName}</strong>. Confirm?
741
817
  </p>
742
- {opportunityPreview && (
743
- <div className="mt-3 space-y-1.5 border-t border-border/50 pt-3">
818
+ {opportunityPreview && !isEditableOpportunityPreview && (
819
+ <div className="mt-3 space-y-2 border-t border-border/50 pt-3">
744
820
  {[
745
821
  { label: "Opportunity", value: opportunityPreview.name },
746
822
  { label: "Account", value: opportunityPreview.accountName },
@@ -748,19 +824,105 @@ function Actions() {
748
824
  { label: "Close Date", value: opportunityPreview.closeDate },
749
825
  { label: "Amount", value: opportunityPreview.amount },
750
826
  ].map(({ label, value }) => (
751
- <div key={label} className="flex items-center justify-between text-xs">
827
+ <div key={label} className="flex items-center justify-between gap-3 text-xs">
828
+ <span className="text-muted-foreground">{label}</span>
829
+ <span className="text-right font-medium text-foreground">{value}</span>
830
+ </div>
831
+ ))}
832
+ </div>
833
+ )}
834
+ {opportunityPreview && isEditableOpportunityPreview && (
835
+ <div className="mt-3 space-y-3 border-t border-border/50 pt-3">
836
+ {[
837
+ { label: "Opportunity", value: opportunityPreview.name },
838
+ { label: "Account", value: opportunityPreview.accountName },
839
+ { label: "Stage", value: opportunityPreview.stage },
840
+ ].map(({ label, value }) => (
841
+ <div key={label} className="flex items-center justify-between gap-3 text-xs">
752
842
  <span className="text-muted-foreground">{label}</span>
753
- <span className="font-medium text-foreground">{value}</span>
843
+ <span className="text-right font-medium text-foreground">{value}</span>
754
844
  </div>
755
845
  ))}
846
+
847
+ <div className="grid gap-2 sm:grid-cols-2">
848
+ <label className="space-y-1 text-xs">
849
+ <span className="font-medium text-muted-foreground">Close Date</span>
850
+ <input
851
+ type="date"
852
+ value={opportunityDraft.closeDate}
853
+ onChange={(event) => setOpportunityDraft((draft) => ({ ...draft, closeDate: event.target.value }))}
854
+ aria-invalid={!canConfirmOpportunity}
855
+ className="h-8 w-full rounded-md border border-border bg-background px-2 text-xs text-foreground focus:outline-none focus:ring-1 focus:ring-ring"
856
+ />
857
+ {!canConfirmOpportunity && (
858
+ <span className="text-[11px] text-red-600">Enter a valid close date.</span>
859
+ )}
860
+ </label>
861
+
862
+ <label className="space-y-1 text-xs">
863
+ <span className="font-medium text-muted-foreground">Amount</span>
864
+ <input
865
+ type="text"
866
+ inputMode="decimal"
867
+ value={opportunityDraft.amount}
868
+ onChange={(event) => setOpportunityDraft((draft) => ({ ...draft, amount: event.target.value }))}
869
+ placeholder={opportunityPreview.amount}
870
+ className="h-8 w-full rounded-md border border-border bg-background px-2 text-xs text-foreground placeholder:text-muted-foreground/60 focus:outline-none focus:ring-1 focus:ring-ring"
871
+ />
872
+ </label>
873
+ </div>
874
+
875
+ <label className="space-y-1 text-xs">
876
+ <span className="font-medium text-muted-foreground">Churn Type</span>
877
+ {hasChurnTypeOptions ? (
878
+ <select
879
+ value={opportunityDraft.churnType}
880
+ onChange={(event) => setOpportunityDraft((draft) => ({ ...draft, churnType: event.target.value }))}
881
+ className="h-8 w-full rounded-md border border-border bg-background px-2 text-xs text-foreground focus:outline-none focus:ring-1 focus:ring-ring"
882
+ >
883
+ <option value="">No churn type</option>
884
+ {churnTypeOptions.map((option) => (
885
+ <option key={optionValue(option)} value={optionValue(option)}>
886
+ {optionLabel(option)}
887
+ </option>
888
+ ))}
889
+ </select>
890
+ ) : (
891
+ <input
892
+ type="text"
893
+ value={opportunityDraft.churnType}
894
+ onChange={(event) => setOpportunityDraft((draft) => ({ ...draft, churnType: event.target.value }))}
895
+ placeholder="No churn type"
896
+ className="h-8 w-full rounded-md border border-border bg-background px-2 text-xs text-foreground placeholder:text-muted-foreground/60 focus:outline-none focus:ring-1 focus:ring-ring"
897
+ />
898
+ )}
899
+ </label>
900
+
901
+ <label className="space-y-1 text-xs">
902
+ <span className="font-medium text-muted-foreground">Description</span>
903
+ <textarea
904
+ value={opportunityDraft.description}
905
+ onChange={(event) => setOpportunityDraft((draft) => ({ ...draft, description: event.target.value }))}
906
+ rows={3}
907
+ placeholder="Add a short description"
908
+ className="w-full resize-none rounded-md border border-border bg-background px-2 py-1.5 text-xs text-foreground placeholder:text-muted-foreground/60 focus:outline-none focus:ring-1 focus:ring-ring"
909
+ />
910
+ </label>
756
911
  </div>
757
912
  )}
758
913
  </div>
759
914
  <div className="flex items-center gap-2">
760
915
  <button
761
916
  type="button"
762
- onClick={approve}
763
- className="inline-flex h-7 items-center gap-1.5 rounded-md bg-foreground px-3 text-xs font-semibold text-background transition-colors hover:bg-foreground/90"
917
+ onClick={() => {
918
+ if (!opportunityPreview) {
919
+ approve()
920
+ return
921
+ }
922
+ approve(isEditableOpportunityPreview ? opportunityDraft : undefined)
923
+ }}
924
+ disabled={!canConfirmOpportunity}
925
+ className="inline-flex h-7 items-center gap-1.5 rounded-md bg-foreground px-3 text-xs font-semibold text-background transition-colors hover:bg-foreground/90 disabled:cursor-not-allowed disabled:bg-muted disabled:text-muted-foreground"
764
926
  >
765
927
  <Check className="h-3 w-3" />
766
928
  Confirm
@@ -861,5 +1023,4 @@ export {
861
1023
  Gate as SignalApprovalGate,
862
1024
  }
863
1025
  export const SignalApproval = { Root, Actions, Gate }
864
- export type OpportunityPreview = NonNullable<RootProps['opportunityPreview']>
865
- export type { ApprovalState, SignalApprovalLabels, SignalApprovalContextValue, RootProps as SignalApprovalRootProps }
1026
+ export type { ApprovalState, OpportunityPreview, OpportunityDraft, SignalApprovalLabels, SignalApprovalContextValue, RootProps as SignalApprovalRootProps }
@@ -50,12 +50,8 @@ export interface PriorityFactor {
50
50
  rationale: string
51
51
  }
52
52
 
53
- export type SignalPriorityScoreDisplay = "label" | "number"
54
-
55
53
  export interface SignalPriorityPopoverProps {
56
54
  score: number
57
- /** Controls whether the overall score number is shown in the popover header. @default "number" */
58
- scoreDisplay?: SignalPriorityScoreDisplay
59
55
  urgencyLabel?: SignalScoreUrgencyLabel
60
56
  /** Synthesis sentence displayed in the popover head. */
61
57
  urgencyExplanation?: string
@@ -286,7 +282,6 @@ export function SignalPriorityPopover({
286
282
  initialFactorFeedback,
287
283
  onFactorFeedback,
288
284
  initialPriorityFeedback,
289
- scoreDisplay = "number",
290
285
  }: SignalPriorityPopoverProps) {
291
286
  const urgencyLabel = providedLabel ?? getUrgencyLevel(score)
292
287
  const scoreRange = getUrgencyRange(urgencyLabel)
@@ -335,20 +330,15 @@ export function SignalPriorityPopover({
335
330
  data-testid="priority-popover-content"
336
331
  >
337
332
  {/* Head section */}
338
- <div className="p-4 pb-3" data-testid="priority-popover-header">
333
+ <div className="p-4 pb-3">
339
334
  <div className="flex items-start justify-between gap-3">
340
335
  <p className="text-sm font-semibold text-foreground">
341
336
  Why this is {urgencyLabel.toLowerCase()} priority
342
337
  </p>
343
- {scoreDisplay === "number" && (
344
- <span
345
- className="text-2xl font-bold tabular-nums text-foreground"
346
- data-testid="priority-overall-score"
347
- >
348
- {score}
349
- <span className="text-sm font-normal text-muted-foreground">/100</span>
350
- </span>
351
- )}
338
+ <span className="text-2xl font-bold tabular-nums text-foreground">
339
+ {score}
340
+ <span className="text-sm font-normal text-muted-foreground">/100</span>
341
+ </span>
352
342
  </div>
353
343
 
354
344
  {/* Band indicator */}
@@ -379,7 +369,7 @@ export function SignalPriorityPopover({
379
369
  </span>
380
370
  <span className="flex items-center gap-1 text-[10px] text-muted-foreground">
381
371
  <Info className="h-3 w-3" />
382
- Priority = weighted signals + calibration
372
+ Score = weighted sum
383
373
  </span>
384
374
  </div>
385
375
 
@@ -0,0 +1,90 @@
1
+ import React from "react"
2
+ import { describe, expect, it } from "vitest"
3
+ import { fireEvent, render, screen } from "@testing-library/react"
4
+
5
+ import { DetailView, type DetailViewProps } from "../prototype-inbox-view"
6
+ import type { QueueItem, SignalScoreData } from "../prototype-config"
7
+
8
+ const baseItem: QueueItem = {
9
+ id: "case-1",
10
+ title: "WIT-825 Opportunity Preview Fixture",
11
+ details: "Some details",
12
+ statusColor: "green",
13
+ time: "2h ago",
14
+ company: "WIT-825 Fixture Account",
15
+ tag1: "churn_risk",
16
+ }
17
+
18
+ function makeSignalScore(overrides: Partial<SignalScoreData> = {}): SignalScoreData {
19
+ return {
20
+ score: 82,
21
+ factors: [],
22
+ whyNow: "Strong signals detected.",
23
+ evidence: ["Evidence line 1"],
24
+ confidence: 80,
25
+ signalBrief: "Signals indicate a potential opportunity.",
26
+ ...overrides,
27
+ }
28
+ }
29
+
30
+ function baseProps(overrides: Partial<DetailViewProps> = {}): DetailViewProps {
31
+ return {
32
+ item: baseItem,
33
+ sections: { signalBrief: true, suggestedActions: false, timeline: false },
34
+ getSignalScore: () => makeSignalScore(),
35
+ buildSuggestedActions: () => [],
36
+ buildSourceItems: () => [],
37
+ accountContacts: [],
38
+ emailSignature: "",
39
+ iconMap: {},
40
+ signalLabels: {
41
+ approveButton: "Create Opportunity",
42
+ dismissButton: "Not Helpful",
43
+ },
44
+ ...overrides,
45
+ }
46
+ }
47
+
48
+ describe("DetailView opportunity approval preview", () => {
49
+ it("keeps the approval flow mounted and shows editable preview fields after async request approval in StrictMode", async () => {
50
+ function DetailViewPreviewHarness() {
51
+ const [opportunityPreview, setOpportunityPreview] = React.useState<DetailViewProps["opportunityPreview"]>()
52
+
53
+ return (
54
+ <DetailView
55
+ {...baseProps({
56
+ opportunityPreview,
57
+ onRequestApproval: async () => {
58
+ setOpportunityPreview({
59
+ name: "Churn Risk - WIT-825 Fixture Account",
60
+ accountName: "WIT-825 Fixture Account",
61
+ stage: "Prospecting",
62
+ closeDate: "Jun 30, 2026",
63
+ closeDateValue: "2026-06-30",
64
+ amount: "$75,000",
65
+ amountValue: 75000,
66
+ description: "Initial description",
67
+ churnType: "Churn Risk",
68
+ churnTypeOptions: ["Churn Risk", "Win Back"],
69
+ })
70
+ },
71
+ })}
72
+ />
73
+ )
74
+ }
75
+
76
+ render(
77
+ <React.StrictMode>
78
+ <DetailViewPreviewHarness />
79
+ </React.StrictMode>,
80
+ )
81
+
82
+ fireEvent.click(screen.getByRole("button", { name: /create opportunity/i }))
83
+
84
+ expect((await screen.findByLabelText("Close Date") as HTMLInputElement).value).toBe("2026-06-30")
85
+ expect((screen.getByLabelText("Amount") as HTMLInputElement).value).toBe("$75,000")
86
+ expect((screen.getByLabelText("Churn Type") as HTMLSelectElement).value).toBe("Churn Risk")
87
+ expect((screen.getByLabelText("Description") as HTMLTextAreaElement).value).toBe("Initial description")
88
+ expect((screen.getByRole("button", { name: /confirm/i }) as HTMLButtonElement).disabled).toBe(false)
89
+ })
90
+ })
@@ -89,38 +89,6 @@ describe("DetailView corrected compact score WHY UX", () => {
89
89
  expect(screen.queryByText("How's this score?")).toBeNull();
90
90
  });
91
91
 
92
-
93
- it("passes priorityScoreDisplay through to the priority popover", () => {
94
- render(
95
- <DetailView
96
- {...baseProps({
97
- getSignalScore: () =>
98
- makeSignalScore({
99
- urgencyLabel: "High",
100
- priorityScoreDisplay: "label",
101
- priorityFactors: [
102
- {
103
- key: "treasury",
104
- label: "Treasury activity",
105
- icon: "wallet",
106
- tone: "alert",
107
- direction: "raises",
108
- score: 85,
109
- rationale: "Full liquidation detected.",
110
- },
111
- ],
112
- }),
113
- })}
114
- />,
115
- );
116
-
117
- fireEvent.click(screen.getByRole("button", { name: /high priority/i }));
118
-
119
- expect(screen.queryByTestId("priority-overall-score")).toBeNull();
120
- expect(screen.getByTestId("priority-popover-header").textContent).not.toContain("82/100");
121
- expect(screen.getByTestId("factor-row-treasury").textContent).toContain("85/100");
122
- });
123
-
124
92
  it("keeps signal WHY chips collapsed by default and excludes factor-only buckets", () => {
125
93
  render(
126
94
  <DetailView
@@ -13,9 +13,9 @@ import type {
13
13
  PipelineStageTiming,
14
14
  } from "../charts/pipeline-overview"
15
15
  import type { TimelineEvent } from "../components/timeline-activity"
16
- import type { ApprovalState } from "../components/signal-feedback-inline"
16
+ import type { ApprovalState, OpportunityDraft } from "../components/signal-feedback-inline"
17
17
  import type { LucideIcon } from "lucide-react"
18
- import type { PriorityFactor, SignalPriorityScoreDisplay } from "../components/signal-priority-popover"
18
+ import type { PriorityFactor } from "../components/signal-priority-popover"
19
19
  import type { FeedbackChipTree, FeedbackSubmitData, PersistedFeedbackData } from "../components/feedback-primitives"
20
20
 
21
21
  // ---------------------------------------------------------------------------
@@ -109,8 +109,6 @@ export interface SignalScoreData {
109
109
  confidence: number
110
110
  urgencyLabel?: SignalScoreUrgencyLabel
111
111
  urgencyExplanation?: string
112
- /** Controls whether the priority popover header shows the raw overall score number. @default "number" */
113
- priorityScoreDisplay?: SignalPriorityScoreDisplay
114
112
  explanationBuckets?: SignalScoreExplanationBucket[]
115
113
  onFactorFeedback?: (factorKey: string, type: "up" | "down" | null, detail?: string) => void
116
114
  /** @deprecated The compact score UX no longer renders score-level thumbs by default. */
@@ -182,7 +180,7 @@ export interface InboxViewConfig {
182
180
  quickFilterTabs?: Array<{ id: string; label: string; matchValue?: string; count?: number }>
183
181
  hideAccountsButton?: boolean
184
182
  accountDetailsLabel?: string
185
- onSignalApprove?: (item: QueueItem) => void | Promise<boolean>
183
+ onSignalApprove?: (item: QueueItem, draft?: OpportunityDraft) => void | Promise<boolean>
186
184
  getSignalApprovalState?: (item: QueueItem) => ApprovalState | undefined
187
185
  signalLabels?: {
188
186
  approveButton?: string
@@ -38,7 +38,7 @@ import {
38
38
  type InboxFilterCategory,
39
39
  } from "../components/inbox-toolbar"
40
40
  import { GroupedListView, type GroupedListGroup } from "../components/item-list"
41
- import { SignalApproval, type ApprovalState, type OpportunityPreview } from "../components/signal-feedback-inline"
41
+ import { SignalApproval, type ApprovalState, type OpportunityDraft, type OpportunityPreview } from "../components/signal-feedback-inline"
42
42
  import { ScoreWhyChips } from "../components/score-why-chips"
43
43
  import { SignalPriorityPopover } from "../components/signal-priority-popover"
44
44
  import { type SourceDef } from "../components/detail-view"
@@ -149,7 +149,7 @@ export interface DetailViewProps {
149
149
  onSuggestedActionFeedback?: (actionId: number | string, feedback: string, actionTitle?: string) => void
150
150
  /** @deprecated The compact score UX no longer renders score-level thumbs by default. */
151
151
  onScoreFeedback?: (type: "up" | "down", pills: string[], detail: string) => void
152
- onSignalApprove?: (item: QueueItem) => void | Promise<boolean>
152
+ onSignalApprove?: (item: QueueItem, draft?: OpportunityDraft) => void | Promise<boolean>
153
153
  getSignalApprovalState?: (item: QueueItem) => ApprovalState | undefined
154
154
  signalLabels?: InboxViewConfig["signalLabels"]
155
155
  hideApproveButton?: boolean
@@ -475,7 +475,7 @@ export function DetailView({
475
475
  opportunityPreview={opportunityPreview}
476
476
  onRequestApproval={onRequestApproval}
477
477
  initialApprovalState={getSignalApprovalState?.(item)}
478
- onApprove={() => onSignalApprove?.(item)}
478
+ onApprove={(draft) => onSignalApprove?.(item, draft)}
479
479
  onApproveFeedback={(reasons, detail) => {
480
480
  signalData.onApproveFeedback?.(reasons, detail)
481
481
  console.log("Approval feedback:", { taskId: item.id, company: item.company, reasons, detail })
@@ -519,7 +519,6 @@ export function DetailView({
519
519
  score={signalData.score}
520
520
  urgencyLabel={signalData.urgencyLabel}
521
521
  urgencyExplanation={signalData.urgencyExplanation ?? signalData.signalBrief}
522
- scoreDisplay={signalData.priorityScoreDisplay}
523
522
  factors={signalData.priorityFactors ?? []}
524
523
  metaText={undefined}
525
524
  feedbackChips={signalData.priorityFeedbackChips}