@handled-ai/design-system 0.9.20 → 0.9.22
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/components/badge.d.ts +1 -1
- package/dist/components/button.d.ts +1 -1
- package/dist/components/signal-feedback-inline.d.ts +2 -2
- package/dist/components/signal-feedback-inline.js +216 -113
- package/dist/components/signal-feedback-inline.js.map +1 -1
- package/dist/components/suggested-actions.d.ts +1 -0
- package/dist/components/suggested-actions.js +9 -3
- package/dist/components/suggested-actions.js.map +1 -1
- package/dist/components/tabs.d.ts +1 -1
- package/dist/prototype/prototype-config.d.ts +2 -1
- package/dist/prototype/prototype-inbox-view.d.ts +3 -2
- package/dist/prototype/prototype-inbox-view.js +13 -8
- package/dist/prototype/prototype-inbox-view.js.map +1 -1
- package/package.json +1 -1
- package/src/components/signal-feedback-inline.tsx +256 -97
- package/src/components/suggested-actions.tsx +11 -3
- package/src/prototype/prototype-config.ts +2 -1
- package/src/prototype/prototype-inbox-view.tsx +11 -3
|
@@ -3,13 +3,65 @@
|
|
|
3
3
|
import * as React from "react"
|
|
4
4
|
import { Check, CirclePlus, ExternalLink, Lock, ThumbsDown } from "lucide-react"
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
interface DismissReasonNode {
|
|
7
|
+
label: string
|
|
8
|
+
subOptions?: string[]
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const dismissReasonTree: DismissReasonNode[] = [
|
|
12
|
+
{
|
|
13
|
+
label: "Not relevant for this account",
|
|
14
|
+
subOptions: [
|
|
15
|
+
"Business as usual for this account",
|
|
16
|
+
"Account in maintenance mode",
|
|
17
|
+
"Wrong contact for this signal",
|
|
18
|
+
"Other",
|
|
19
|
+
],
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
label: "Bad timing",
|
|
23
|
+
subOptions: [
|
|
24
|
+
"Too early in the relationship",
|
|
25
|
+
"Too soon after last outreach",
|
|
26
|
+
"Wrong time of year for this account",
|
|
27
|
+
"Other",
|
|
28
|
+
],
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
label: "Inaccurate data",
|
|
32
|
+
subOptions: [
|
|
33
|
+
"Wrong amount or number",
|
|
34
|
+
"Stale data",
|
|
35
|
+
"Account info wrong",
|
|
36
|
+
"Other",
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
label: "Wrong account",
|
|
41
|
+
subOptions: [
|
|
42
|
+
"Different account meant",
|
|
43
|
+
"Account not in scope",
|
|
44
|
+
"Other",
|
|
45
|
+
],
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
label: "Already handled",
|
|
49
|
+
subOptions: [
|
|
50
|
+
"Already in conversation",
|
|
51
|
+
"Already an open Opportunity",
|
|
52
|
+
"Already escalated",
|
|
53
|
+
"Other",
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
label: "Not actionable",
|
|
58
|
+
subOptions: [
|
|
59
|
+
"No clear next step",
|
|
60
|
+
"Outside our remit",
|
|
61
|
+
"Other",
|
|
62
|
+
],
|
|
63
|
+
},
|
|
64
|
+
{ label: "Other" },
|
|
13
65
|
]
|
|
14
66
|
|
|
15
67
|
const approveReasons = [
|
|
@@ -53,7 +105,7 @@ interface SignalApprovalContextValue {
|
|
|
53
105
|
approve: () => void
|
|
54
106
|
submitApproveFeedback: (reasons: string[], detail: string) => void
|
|
55
107
|
skipApproveFeedback: () => void
|
|
56
|
-
dismiss: (reasons: string[], detail: string) => void
|
|
108
|
+
dismiss: (reasons: string[], detail: string, subReason?: string) => void
|
|
57
109
|
requestApproval: () => void
|
|
58
110
|
requestDismiss: () => void
|
|
59
111
|
cancel: () => void
|
|
@@ -78,7 +130,7 @@ interface RootProps {
|
|
|
78
130
|
hideApproveButton?: boolean
|
|
79
131
|
onApprove?: () => void
|
|
80
132
|
onApproveFeedback?: (reasons: string[], detail: string) => void
|
|
81
|
-
onDismiss?: (reasons: string[], detail: string) => void
|
|
133
|
+
onDismiss?: (reasons: string[], detail: string, subReason?: string) => void
|
|
82
134
|
}
|
|
83
135
|
|
|
84
136
|
function Root({ children, companyName, opportunityUrl, scheduledTime, initialApprovalState, labels: labelOverrides, hideApproveButton, onApprove, onApproveFeedback, onDismiss }: RootProps) {
|
|
@@ -115,9 +167,9 @@ function Root({ children, companyName, opportunityUrl, scheduledTime, initialApp
|
|
|
115
167
|
}, [])
|
|
116
168
|
|
|
117
169
|
const dismiss = React.useCallback(
|
|
118
|
-
(reasons: string[], detail: string) => {
|
|
170
|
+
(reasons: string[], detail: string, subReason?: string) => {
|
|
119
171
|
setApprovalState("dismissed")
|
|
120
|
-
onDismiss?.(reasons, detail)
|
|
172
|
+
onDismiss?.(reasons, detail, subReason)
|
|
121
173
|
},
|
|
122
174
|
[onDismiss]
|
|
123
175
|
)
|
|
@@ -131,14 +183,128 @@ function Root({ children, companyName, opportunityUrl, scheduledTime, initialApp
|
|
|
131
183
|
)
|
|
132
184
|
}
|
|
133
185
|
|
|
186
|
+
/** Shared dismiss reason picker used in both the "editing" and "initial dismiss" paths. */
|
|
187
|
+
function DismissReasonPicker({
|
|
188
|
+
selectedTopReason,
|
|
189
|
+
selectedSubReason,
|
|
190
|
+
selectTopReason,
|
|
191
|
+
selectSubReason,
|
|
192
|
+
detailText,
|
|
193
|
+
setDetailText,
|
|
194
|
+
needsText,
|
|
195
|
+
canSubmitDismiss,
|
|
196
|
+
handleDismissSubmit,
|
|
197
|
+
topNode,
|
|
198
|
+
submitLabel,
|
|
199
|
+
onCancel,
|
|
200
|
+
}: {
|
|
201
|
+
selectedTopReason: string | null
|
|
202
|
+
selectedSubReason: string | null
|
|
203
|
+
selectTopReason: (label: string) => void
|
|
204
|
+
selectSubReason: (label: string) => void
|
|
205
|
+
detailText: string
|
|
206
|
+
setDetailText: (value: string) => void
|
|
207
|
+
needsText: boolean
|
|
208
|
+
canSubmitDismiss: boolean
|
|
209
|
+
handleDismissSubmit: () => void
|
|
210
|
+
topNode: DismissReasonNode | undefined
|
|
211
|
+
submitLabel: string
|
|
212
|
+
onCancel: () => void
|
|
213
|
+
}) {
|
|
214
|
+
return (
|
|
215
|
+
<>
|
|
216
|
+
<div className="flex flex-wrap gap-1.5">
|
|
217
|
+
{dismissReasonTree.map((node) => {
|
|
218
|
+
const selected = selectedTopReason === node.label
|
|
219
|
+
return (
|
|
220
|
+
<button
|
|
221
|
+
key={node.label}
|
|
222
|
+
type="button"
|
|
223
|
+
onClick={() => selectTopReason(node.label)}
|
|
224
|
+
className={`rounded-full border px-2.5 py-1 text-[11px] font-medium transition-colors ${
|
|
225
|
+
selected
|
|
226
|
+
? "border-red-200 bg-red-100 text-red-700"
|
|
227
|
+
: "border-border bg-background text-muted-foreground hover:bg-muted/50 hover:text-foreground"
|
|
228
|
+
}`}
|
|
229
|
+
>
|
|
230
|
+
{node.label}
|
|
231
|
+
</button>
|
|
232
|
+
)
|
|
233
|
+
})}
|
|
234
|
+
</div>
|
|
235
|
+
|
|
236
|
+
{topNode?.subOptions && (
|
|
237
|
+
<div className="ml-3 border-l-2 border-muted pl-3">
|
|
238
|
+
<div className="flex flex-wrap gap-1.5">
|
|
239
|
+
{topNode.subOptions.map((sub) => {
|
|
240
|
+
const selected = selectedSubReason === sub
|
|
241
|
+
return (
|
|
242
|
+
<button
|
|
243
|
+
key={sub}
|
|
244
|
+
type="button"
|
|
245
|
+
onClick={() => selectSubReason(sub)}
|
|
246
|
+
className={`rounded-full border px-2.5 py-1 text-[11px] font-medium transition-colors ${
|
|
247
|
+
selected
|
|
248
|
+
? "border-red-200 bg-red-100 text-red-700"
|
|
249
|
+
: "border-border bg-background text-muted-foreground hover:bg-muted/50 hover:text-foreground"
|
|
250
|
+
}`}
|
|
251
|
+
>
|
|
252
|
+
{sub}
|
|
253
|
+
</button>
|
|
254
|
+
)
|
|
255
|
+
})}
|
|
256
|
+
</div>
|
|
257
|
+
</div>
|
|
258
|
+
)}
|
|
259
|
+
|
|
260
|
+
{selectedTopReason && (
|
|
261
|
+
<input
|
|
262
|
+
type="text"
|
|
263
|
+
value={detailText}
|
|
264
|
+
onChange={(e) => setDetailText(e.target.value)}
|
|
265
|
+
onKeyDown={(e) => {
|
|
266
|
+
if (e.key === "Enter" && canSubmitDismiss) handleDismissSubmit()
|
|
267
|
+
}}
|
|
268
|
+
placeholder={needsText ? "Please describe (required)" : "Add context (optional)"}
|
|
269
|
+
className="h-7 w-full rounded-md border border-border bg-muted/20 px-2.5 text-xs text-foreground placeholder:text-muted-foreground/60 focus:outline-none focus:ring-1 focus:ring-ring"
|
|
270
|
+
/>
|
|
271
|
+
)}
|
|
272
|
+
|
|
273
|
+
<div className="flex items-center gap-2">
|
|
274
|
+
<button
|
|
275
|
+
type="button"
|
|
276
|
+
onClick={handleDismissSubmit}
|
|
277
|
+
disabled={!canSubmitDismiss}
|
|
278
|
+
className={`inline-flex h-7 items-center gap-1.5 rounded-md px-3 text-xs font-semibold transition-colors ${
|
|
279
|
+
canSubmitDismiss
|
|
280
|
+
? "bg-foreground text-background hover:bg-foreground/90"
|
|
281
|
+
: "cursor-not-allowed bg-muted text-muted-foreground"
|
|
282
|
+
}`}
|
|
283
|
+
>
|
|
284
|
+
{submitLabel}
|
|
285
|
+
</button>
|
|
286
|
+
<button
|
|
287
|
+
type="button"
|
|
288
|
+
onClick={onCancel}
|
|
289
|
+
className="inline-flex h-7 items-center rounded-md border border-border px-3 text-xs font-medium text-muted-foreground transition-colors hover:bg-muted hover:text-foreground"
|
|
290
|
+
>
|
|
291
|
+
Cancel
|
|
292
|
+
</button>
|
|
293
|
+
</div>
|
|
294
|
+
</>
|
|
295
|
+
)
|
|
296
|
+
}
|
|
297
|
+
|
|
134
298
|
function SubmittedFeedback({
|
|
135
299
|
reasons,
|
|
136
300
|
detail,
|
|
301
|
+
subReason,
|
|
137
302
|
variant,
|
|
138
303
|
onEdit,
|
|
139
304
|
}: {
|
|
140
305
|
reasons: string[]
|
|
141
306
|
detail: string
|
|
307
|
+
subReason?: string
|
|
142
308
|
variant: "approve" | "dismiss"
|
|
143
309
|
onEdit: () => void
|
|
144
310
|
}) {
|
|
@@ -164,6 +330,13 @@ function SubmittedFeedback({
|
|
|
164
330
|
{r}
|
|
165
331
|
</span>
|
|
166
332
|
))}
|
|
333
|
+
{subReason && (
|
|
334
|
+
<span
|
|
335
|
+
className={`rounded-full border px-2 py-0.5 text-[10px] font-medium transition-colors group-hover:opacity-80 ${pillClass}`}
|
|
336
|
+
>
|
|
337
|
+
{subReason}
|
|
338
|
+
</span>
|
|
339
|
+
)}
|
|
167
340
|
</div>
|
|
168
341
|
)}
|
|
169
342
|
{detail && (
|
|
@@ -176,15 +349,41 @@ function SubmittedFeedback({
|
|
|
176
349
|
function Actions() {
|
|
177
350
|
const { approvalState, companyName, opportunityUrl, scheduledTime, labels, hideApproveButton, approve, submitApproveFeedback, skipApproveFeedback, dismiss, requestApproval, requestDismiss, cancel } =
|
|
178
351
|
useSignalApproval()
|
|
352
|
+
const [selectedTopReason, setSelectedTopReason] = React.useState<string | null>(null)
|
|
353
|
+
const [selectedSubReason, setSelectedSubReason] = React.useState<string | null>(null)
|
|
179
354
|
const [selectedReasons, setSelectedReasons] = React.useState<string[]>([])
|
|
180
355
|
const [detailText, setDetailText] = React.useState("")
|
|
181
|
-
const [submittedFeedback, setSubmittedFeedback] = React.useState<{ reasons: string[]; detail: string } | null>(null)
|
|
356
|
+
const [submittedFeedback, setSubmittedFeedback] = React.useState<{ reasons: string[]; detail: string; subReason?: string } | null>(null)
|
|
182
357
|
const [isEditing, setIsEditing] = React.useState(false)
|
|
183
358
|
|
|
184
|
-
const
|
|
185
|
-
const
|
|
359
|
+
const topNode = dismissReasonTree.find((n) => n.label === selectedTopReason)
|
|
360
|
+
const hasSubOptions = !!(topNode?.subOptions && topNode.subOptions.length > 0)
|
|
361
|
+
const isTopOther = selectedTopReason === "Other" && !hasSubOptions
|
|
362
|
+
const isSubOther = selectedSubReason === "Other"
|
|
363
|
+
const needsText = isTopOther || isSubOther
|
|
364
|
+
const canSubmitDismiss =
|
|
365
|
+
selectedTopReason !== null &&
|
|
366
|
+
(!hasSubOptions || selectedSubReason !== null) &&
|
|
367
|
+
(!needsText || detailText.trim().length > 0)
|
|
368
|
+
|
|
186
369
|
const canSubmitApprove = selectedReasons.length > 0 || detailText.trim().length > 0
|
|
187
370
|
|
|
371
|
+
const selectTopReason = (label: string) => {
|
|
372
|
+
if (selectedTopReason === label) {
|
|
373
|
+
setSelectedTopReason(null)
|
|
374
|
+
setSelectedSubReason(null)
|
|
375
|
+
setDetailText("")
|
|
376
|
+
} else {
|
|
377
|
+
setSelectedTopReason(label)
|
|
378
|
+
setSelectedSubReason(null)
|
|
379
|
+
setDetailText("")
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
const selectSubReason = (label: string) => {
|
|
384
|
+
setSelectedSubReason(selectedSubReason === label ? null : label)
|
|
385
|
+
}
|
|
386
|
+
|
|
188
387
|
const toggleReason = (reason: string) => {
|
|
189
388
|
setSelectedReasons((prev) =>
|
|
190
389
|
prev.includes(reason) ? prev.filter((r) => r !== reason) : [...prev, reason]
|
|
@@ -193,6 +392,11 @@ function Actions() {
|
|
|
193
392
|
|
|
194
393
|
const startEditing = () => {
|
|
195
394
|
if (submittedFeedback) {
|
|
395
|
+
setSelectedTopReason(submittedFeedback.reasons[0] ?? null)
|
|
396
|
+
setSelectedSubReason(submittedFeedback.subReason ?? null)
|
|
397
|
+
// Note: selectedReasons is only used by the approve editing path.
|
|
398
|
+
// For dismiss feedback this is harmless but unused — the dismiss path
|
|
399
|
+
// reads selectedTopReason/selectedSubReason instead.
|
|
196
400
|
setSelectedReasons([...submittedFeedback.reasons])
|
|
197
401
|
setDetailText(submittedFeedback.detail)
|
|
198
402
|
}
|
|
@@ -200,11 +404,12 @@ function Actions() {
|
|
|
200
404
|
}
|
|
201
405
|
|
|
202
406
|
const handleDismissSubmit = () => {
|
|
203
|
-
if (!canSubmitDismiss) return
|
|
204
|
-
const fb = { reasons: [
|
|
407
|
+
if (!canSubmitDismiss || !selectedTopReason) return
|
|
408
|
+
const fb = { reasons: [selectedTopReason], detail: detailText.trim(), subReason: selectedSubReason ?? undefined }
|
|
205
409
|
setSubmittedFeedback(fb)
|
|
206
|
-
dismiss(
|
|
207
|
-
|
|
410
|
+
dismiss([selectedTopReason], detailText.trim(), selectedSubReason ?? undefined)
|
|
411
|
+
setSelectedTopReason(null)
|
|
412
|
+
setSelectedSubReason(null)
|
|
208
413
|
setDetailText("")
|
|
209
414
|
setIsEditing(false)
|
|
210
415
|
}
|
|
@@ -219,6 +424,8 @@ function Actions() {
|
|
|
219
424
|
}
|
|
220
425
|
|
|
221
426
|
const handleEditCancel = () => {
|
|
427
|
+
setSelectedTopReason(null)
|
|
428
|
+
setSelectedSubReason(null)
|
|
222
429
|
setSelectedReasons([])
|
|
223
430
|
setDetailText("")
|
|
224
431
|
setIsEditing(false)
|
|
@@ -226,6 +433,8 @@ function Actions() {
|
|
|
226
433
|
|
|
227
434
|
const handleCancel = () => {
|
|
228
435
|
cancel()
|
|
436
|
+
setSelectedTopReason(null)
|
|
437
|
+
setSelectedSubReason(null)
|
|
229
438
|
setSelectedReasons([])
|
|
230
439
|
setDetailText("")
|
|
231
440
|
}
|
|
@@ -403,31 +612,20 @@ function Actions() {
|
|
|
403
612
|
<span>{labels.dismissedStatus}</span>
|
|
404
613
|
</div>
|
|
405
614
|
<p className="text-xs font-medium text-muted-foreground">Edit your feedback</p>
|
|
406
|
-
<
|
|
407
|
-
{
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
className="h-7 w-full rounded-md border border-border bg-muted/20 px-2.5 text-xs text-foreground placeholder:text-muted-foreground/60 focus:outline-none focus:ring-1 focus:ring-ring" />
|
|
421
|
-
<div className="flex items-center gap-2">
|
|
422
|
-
<button type="button" onClick={handleDismissSubmit} disabled={!canSubmitDismiss}
|
|
423
|
-
className={`inline-flex h-7 items-center gap-1.5 rounded-md px-3 text-xs font-semibold transition-colors ${canSubmitDismiss ? "bg-foreground text-background hover:bg-foreground/90" : "cursor-not-allowed bg-muted text-muted-foreground"}`}>
|
|
424
|
-
Save
|
|
425
|
-
</button>
|
|
426
|
-
<button type="button" onClick={handleEditCancel}
|
|
427
|
-
className="inline-flex h-7 items-center rounded-md border border-border px-3 text-xs font-medium text-muted-foreground transition-colors hover:bg-muted hover:text-foreground">
|
|
428
|
-
Cancel
|
|
429
|
-
</button>
|
|
430
|
-
</div>
|
|
615
|
+
<DismissReasonPicker
|
|
616
|
+
selectedTopReason={selectedTopReason}
|
|
617
|
+
selectedSubReason={selectedSubReason}
|
|
618
|
+
selectTopReason={selectTopReason}
|
|
619
|
+
selectSubReason={selectSubReason}
|
|
620
|
+
detailText={detailText}
|
|
621
|
+
setDetailText={setDetailText}
|
|
622
|
+
needsText={needsText}
|
|
623
|
+
canSubmitDismiss={canSubmitDismiss}
|
|
624
|
+
handleDismissSubmit={handleDismissSubmit}
|
|
625
|
+
topNode={topNode}
|
|
626
|
+
submitLabel="Save"
|
|
627
|
+
onCancel={handleEditCancel}
|
|
628
|
+
/>
|
|
431
629
|
</div>
|
|
432
630
|
)
|
|
433
631
|
}
|
|
@@ -442,6 +640,7 @@ function Actions() {
|
|
|
442
640
|
<SubmittedFeedback
|
|
443
641
|
reasons={submittedFeedback.reasons}
|
|
444
642
|
detail={submittedFeedback.detail}
|
|
643
|
+
subReason={submittedFeedback.subReason}
|
|
445
644
|
variant="dismiss"
|
|
446
645
|
onEdit={startEditing}
|
|
447
646
|
/>
|
|
@@ -483,60 +682,20 @@ function Actions() {
|
|
|
483
682
|
return (
|
|
484
683
|
<div className="space-y-3">
|
|
485
684
|
<p className="text-xs font-medium text-muted-foreground">{labels.dismissPrompt}</p>
|
|
486
|
-
<
|
|
487
|
-
{
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
{reason}
|
|
501
|
-
</button>
|
|
502
|
-
)
|
|
503
|
-
})}
|
|
504
|
-
</div>
|
|
505
|
-
|
|
506
|
-
{(selectedReasons.length > 0 || otherSelected) && (
|
|
507
|
-
<input
|
|
508
|
-
type="text"
|
|
509
|
-
value={detailText}
|
|
510
|
-
onChange={(e) => setDetailText(e.target.value)}
|
|
511
|
-
onKeyDown={(e) => {
|
|
512
|
-
if (e.key === "Enter" && canSubmitDismiss) handleDismissSubmit()
|
|
513
|
-
}}
|
|
514
|
-
placeholder={otherSelected ? "Please describe (required)" : "Provide additional feedback..."}
|
|
515
|
-
className="h-7 w-full rounded-md border border-border bg-muted/20 px-2.5 text-xs text-foreground placeholder:text-muted-foreground/60 focus:outline-none focus:ring-1 focus:ring-ring"
|
|
516
|
-
/>
|
|
517
|
-
)}
|
|
518
|
-
|
|
519
|
-
<div className="flex items-center gap-2">
|
|
520
|
-
<button
|
|
521
|
-
type="button"
|
|
522
|
-
onClick={handleDismissSubmit}
|
|
523
|
-
disabled={!canSubmitDismiss}
|
|
524
|
-
className={`inline-flex h-7 items-center gap-1.5 rounded-md px-3 text-xs font-semibold transition-colors ${
|
|
525
|
-
canSubmitDismiss
|
|
526
|
-
? "bg-foreground text-background hover:bg-foreground/90"
|
|
527
|
-
: "cursor-not-allowed bg-muted text-muted-foreground"
|
|
528
|
-
}`}
|
|
529
|
-
>
|
|
530
|
-
Submit
|
|
531
|
-
</button>
|
|
532
|
-
<button
|
|
533
|
-
type="button"
|
|
534
|
-
onClick={handleCancel}
|
|
535
|
-
className="inline-flex h-7 items-center rounded-md border border-border px-3 text-xs font-medium text-muted-foreground transition-colors hover:bg-muted hover:text-foreground"
|
|
536
|
-
>
|
|
537
|
-
Cancel
|
|
538
|
-
</button>
|
|
539
|
-
</div>
|
|
685
|
+
<DismissReasonPicker
|
|
686
|
+
selectedTopReason={selectedTopReason}
|
|
687
|
+
selectedSubReason={selectedSubReason}
|
|
688
|
+
selectTopReason={selectTopReason}
|
|
689
|
+
selectSubReason={selectSubReason}
|
|
690
|
+
detailText={detailText}
|
|
691
|
+
setDetailText={setDetailText}
|
|
692
|
+
needsText={needsText}
|
|
693
|
+
canSubmitDismiss={canSubmitDismiss}
|
|
694
|
+
handleDismissSubmit={handleDismissSubmit}
|
|
695
|
+
topNode={topNode}
|
|
696
|
+
submitLabel="Submit"
|
|
697
|
+
onCancel={handleCancel}
|
|
698
|
+
/>
|
|
540
699
|
</div>
|
|
541
700
|
)
|
|
542
701
|
}
|
|
@@ -167,6 +167,7 @@ export interface SuggestedAction {
|
|
|
167
167
|
callMeta?: SuggestedActionCallMeta
|
|
168
168
|
manualMeta?: SuggestedActionManualMeta
|
|
169
169
|
browserMeta?: SuggestedActionBrowserMeta
|
|
170
|
+
onFeedback?: (type: "up" | "down", pills: string[], detail: string) => void
|
|
170
171
|
}
|
|
171
172
|
|
|
172
173
|
// ---------------------------------------------------------------------------
|
|
@@ -1253,6 +1254,7 @@ function SuggestedActionCard({
|
|
|
1253
1254
|
onOpenRecentActivity,
|
|
1254
1255
|
onMarkComplete,
|
|
1255
1256
|
onDispatchAgent,
|
|
1257
|
+
onFeedback,
|
|
1256
1258
|
iconMap,
|
|
1257
1259
|
sendLabel,
|
|
1258
1260
|
accountDetailsLabel,
|
|
@@ -1269,6 +1271,7 @@ function SuggestedActionCard({
|
|
|
1269
1271
|
onOpenRecentActivity?: () => void
|
|
1270
1272
|
onMarkComplete?: (id: number | string) => void
|
|
1271
1273
|
onDispatchAgent?: (id: number | string, editedContent?: string, settings?: { aiDisclosureEnabled?: boolean; maxDurationMinutes?: string; callRecordingEnabled?: boolean; recordingNoticeEnabled?: boolean }) => void
|
|
1274
|
+
onFeedback?: (type: "up" | "down", pills: string[], detail: string) => void
|
|
1272
1275
|
iconMap?: SuggestedActionsIconMap
|
|
1273
1276
|
sendLabel?: string
|
|
1274
1277
|
accountDetailsLabel?: string
|
|
@@ -1405,10 +1408,14 @@ function SuggestedActionCard({
|
|
|
1405
1408
|
{feedbackOpen && (
|
|
1406
1409
|
<div className="px-5 py-3 border-b border-border/40 animate-in fade-in slide-in-from-top-2 duration-200">
|
|
1407
1410
|
<DraftFeedbackInline
|
|
1408
|
-
onRegenerateRequest={(pills, detail) =>
|
|
1409
|
-
|
|
1411
|
+
onRegenerateRequest={(pills, detail) => {
|
|
1412
|
+
onFeedback?.("down", pills, detail)
|
|
1413
|
+
}}
|
|
1414
|
+
onSubmitFeedback={(type, pills, detail) => {
|
|
1415
|
+
onFeedback?.(type, pills, detail)
|
|
1416
|
+
}}
|
|
1410
1417
|
onDiscardRequest={(pills, detail) => {
|
|
1411
|
-
|
|
1418
|
+
onFeedback?.("down", pills, detail)
|
|
1412
1419
|
onDismiss?.(action.id)
|
|
1413
1420
|
}}
|
|
1414
1421
|
/>
|
|
@@ -1972,6 +1979,7 @@ export function SuggestedActions({
|
|
|
1972
1979
|
onOpenRecentActivity={onOpenRecentActivity}
|
|
1973
1980
|
onMarkComplete={onMarkComplete}
|
|
1974
1981
|
onDispatchAgent={onDispatchAgent}
|
|
1982
|
+
onFeedback={action.onFeedback}
|
|
1975
1983
|
iconMap={iconMap}
|
|
1976
1984
|
sendLabel={sendLabel}
|
|
1977
1985
|
accountDetailsLabel={accountDetailsLabel}
|
|
@@ -44,7 +44,7 @@ export interface SignalScoreData {
|
|
|
44
44
|
confidence: number
|
|
45
45
|
onFactorFeedback?: (factorKey: string, type: "up" | "down" | null, detail?: string) => void
|
|
46
46
|
onApproveFeedback?: (reasons: string[], detail: string) => void
|
|
47
|
-
onDismissFeedback?: (reasons: string[], detail: string) => void
|
|
47
|
+
onDismissFeedback?: (reasons: string[], detail: string, subReason?: string) => void
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
// ---------------------------------------------------------------------------
|
|
@@ -72,6 +72,7 @@ export interface InboxViewConfig {
|
|
|
72
72
|
hideToolbarActions?: boolean
|
|
73
73
|
hideHoverActions?: boolean
|
|
74
74
|
onSuggestedActionFeedback?: (actionId: number | string, feedback: string, actionTitle?: string) => void
|
|
75
|
+
onScoreFeedback?: (type: "up" | "down", pills: string[], detail: string) => void
|
|
75
76
|
buildEntityChips?: (item: QueueItem) => Array<{ id: string; label: string; avatarLetter: string; onClick?: () => void }>
|
|
76
77
|
quickFilterTabs?: Array<{ id: string; label: string; matchValue?: string; count?: number }>
|
|
77
78
|
hideAccountsButton?: boolean
|
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
} from "../components/inbox-toolbar"
|
|
31
31
|
import { GroupedListView, type GroupedListGroup } from "../components/item-list"
|
|
32
32
|
import { SignalApproval, type ApprovalState } from "../components/signal-feedback-inline"
|
|
33
|
+
import { ScoreFeedback } from "../components/score-feedback"
|
|
33
34
|
import { ScoreBreakdown } from "../components/score-breakdown"
|
|
34
35
|
import { Citation, type SourceDef } from "../components/detail-view"
|
|
35
36
|
import {
|
|
@@ -107,6 +108,7 @@ export interface DetailViewProps {
|
|
|
107
108
|
hideApproveButton?: boolean
|
|
108
109
|
signalBriefCopy?: InboxViewConfig["signalBriefCopy"]
|
|
109
110
|
renderDetailExtra?: (item: QueueItem) => React.ReactNode
|
|
111
|
+
onScoreFeedback?: (type: "up" | "down", pills: string[], detail: string) => void
|
|
110
112
|
}
|
|
111
113
|
|
|
112
114
|
export function DetailView({
|
|
@@ -128,6 +130,7 @@ export function DetailView({
|
|
|
128
130
|
hideApproveButton,
|
|
129
131
|
signalBriefCopy,
|
|
130
132
|
renderDetailExtra,
|
|
133
|
+
onScoreFeedback,
|
|
131
134
|
}: DetailViewProps) {
|
|
132
135
|
const [evidenceExpanded, setEvidenceExpanded] = React.useState(false)
|
|
133
136
|
const [showTimeline, setShowTimeline] = React.useState(false)
|
|
@@ -180,9 +183,8 @@ export function DetailView({
|
|
|
180
183
|
signalData.onApproveFeedback?.(reasons, detail)
|
|
181
184
|
console.log("Approval feedback:", { taskId: item.id, company: item.company, reasons, detail })
|
|
182
185
|
}}
|
|
183
|
-
onDismiss={(reasons, detail) => {
|
|
184
|
-
signalData.onDismissFeedback?.(reasons, detail)
|
|
185
|
-
console.log("Dismissed signal:", { taskId: item.id, reasons, detail })
|
|
186
|
+
onDismiss={(reasons, detail, subReason) => {
|
|
187
|
+
signalData.onDismissFeedback?.(reasons, detail, subReason)
|
|
186
188
|
}}
|
|
187
189
|
>
|
|
188
190
|
<div className="mx-auto w-full max-w-3xl p-6 pb-12 md:p-8">
|
|
@@ -277,14 +279,17 @@ export function DetailView({
|
|
|
277
279
|
{signalData.whyNow}
|
|
278
280
|
</p>
|
|
279
281
|
|
|
282
|
+
<ScoreFeedback.Root onSubmitFeedback={(type, pills, detail) => onScoreFeedback?.(type, pills, detail)}>
|
|
280
283
|
<div className="mb-5 rounded-md border border-border bg-muted/20 p-3">
|
|
281
284
|
<div className="flex items-center justify-between mb-1.5">
|
|
282
285
|
<span className="text-[10px] font-bold text-muted-foreground uppercase tracking-wider">Signal Score</span>
|
|
283
286
|
<div className="flex items-center gap-2">
|
|
284
287
|
<span className="text-sm font-bold text-foreground">{signalData.score}/100</span>
|
|
285
288
|
<span className={`text-[10px] font-bold uppercase ${scoreColor}`}>{scoreLabel}</span>
|
|
289
|
+
<ScoreFeedback.Trigger />
|
|
286
290
|
</div>
|
|
287
291
|
</div>
|
|
292
|
+
<ScoreFeedback.Panel />
|
|
288
293
|
<div className="h-1.5 bg-muted rounded-full overflow-hidden mb-2">
|
|
289
294
|
<div
|
|
290
295
|
className={`h-full rounded-full transition-all duration-500 ${barColor}`}
|
|
@@ -320,6 +325,7 @@ export function DetailView({
|
|
|
320
325
|
</div>
|
|
321
326
|
)}
|
|
322
327
|
</div>
|
|
328
|
+
</ScoreFeedback.Root>
|
|
323
329
|
|
|
324
330
|
{!evidenceExpanded && <SignalApproval.Actions />}
|
|
325
331
|
</div>
|
|
@@ -398,6 +404,7 @@ export function PrototypeInboxView({
|
|
|
398
404
|
hideToolbarActions,
|
|
399
405
|
hideHoverActions,
|
|
400
406
|
onSuggestedActionFeedback,
|
|
407
|
+
onScoreFeedback,
|
|
401
408
|
headerActions,
|
|
402
409
|
onOpenEntityPanel,
|
|
403
410
|
onOpenRecentActivity,
|
|
@@ -640,6 +647,7 @@ export function PrototypeInboxView({
|
|
|
640
647
|
hideApproveButton,
|
|
641
648
|
signalBriefCopy,
|
|
642
649
|
renderDetailExtra,
|
|
650
|
+
onScoreFeedback,
|
|
643
651
|
}
|
|
644
652
|
|
|
645
653
|
return (
|