@handled-ai/design-system 0.16.2 → 0.17.1
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/contextual-quick-action-launcher.d.ts +32 -0
- package/dist/components/contextual-quick-action-launcher.js +202 -0
- package/dist/components/contextual-quick-action-launcher.js.map +1 -0
- package/dist/components/score-why-chips.d.ts +46 -0
- package/dist/components/score-why-chips.js +281 -0
- package/dist/components/score-why-chips.js.map +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/prototype/index.d.ts +1 -1
- package/dist/prototype/prototype-config.d.ts +41 -1
- package/dist/prototype/prototype-inbox-view.d.ts +13 -3
- package/dist/prototype/prototype-inbox-view.js +41 -97
- package/dist/prototype/prototype-inbox-view.js.map +1 -1
- package/package.json +1 -1
- package/src/components/__tests__/contextual-quick-action-launcher.test.tsx +193 -0
- package/src/components/contextual-quick-action-launcher.tsx +231 -0
- package/src/components/score-why-chips.tsx +358 -0
- package/src/index.ts +2 -0
- package/src/prototype/__tests__/detail-view-score-why.test.tsx +326 -0
- package/src/prototype/__tests__/detail-view-title-subtext.test.tsx +72 -0
- package/src/prototype/prototype-config.ts +39 -0
- package/src/prototype/prototype-inbox-view.tsx +48 -105
|
@@ -38,9 +38,8 @@ import {
|
|
|
38
38
|
} from "../components/inbox-toolbar"
|
|
39
39
|
import { GroupedListView, type GroupedListGroup } from "../components/item-list"
|
|
40
40
|
import { SignalApproval, type ApprovalState, type OpportunityPreview } from "../components/signal-feedback-inline"
|
|
41
|
-
import {
|
|
42
|
-
import {
|
|
43
|
-
import { Citation, type SourceDef } from "../components/detail-view"
|
|
41
|
+
import { ScoreWhyChips, SignalPriorityChip, SignalPriorityPanel } from "../components/score-why-chips"
|
|
42
|
+
import { type SourceDef } from "../components/detail-view"
|
|
44
43
|
import {
|
|
45
44
|
SuggestedActions,
|
|
46
45
|
type SuggestedAction,
|
|
@@ -124,6 +123,8 @@ export interface DetailViewProps {
|
|
|
124
123
|
onOpenEntityPanel?: () => void
|
|
125
124
|
onOpenRecentActivity?: () => void
|
|
126
125
|
onSuggestedActionFeedback?: (actionId: number | string, feedback: string, actionTitle?: string) => void
|
|
126
|
+
/** @deprecated The compact score UX no longer renders score-level thumbs by default. */
|
|
127
|
+
onScoreFeedback?: (type: "up" | "down", pills: string[], detail: string) => void
|
|
127
128
|
onSignalApprove?: (item: QueueItem) => void | Promise<boolean>
|
|
128
129
|
getSignalApprovalState?: (item: QueueItem) => ApprovalState | undefined
|
|
129
130
|
signalLabels?: InboxViewConfig["signalLabels"]
|
|
@@ -136,9 +137,13 @@ export interface DetailViewProps {
|
|
|
136
137
|
/** Render content between the signal score section and the activity timeline. */
|
|
137
138
|
renderAfterScore?: (item: QueueItem) => React.ReactNode
|
|
138
139
|
lastActivityTime?: string
|
|
140
|
+
/** Render extra content inline with the detail title. */
|
|
141
|
+
renderTitleExtra?: (item: QueueItem) => React.ReactNode
|
|
142
|
+
/** Render supporting content below the detail title. */
|
|
143
|
+
renderTitleSubtext?: (item: QueueItem) => React.ReactNode
|
|
139
144
|
/** Render extra metadata chips (e.g. assignee) inside the chips row below the title. */
|
|
140
145
|
renderMetadataExtra?: (item: QueueItem) => React.ReactNode
|
|
141
|
-
|
|
146
|
+
onOpenSignalBucket?: (args: { item: QueueItem; bucketKey: string; signalId: string }) => void
|
|
142
147
|
approveButtonIconUrl?: string
|
|
143
148
|
opportunityPreview?: OpportunityPreview
|
|
144
149
|
onRequestApproval?: () => Promise<void>
|
|
@@ -151,7 +156,7 @@ export function DetailView({
|
|
|
151
156
|
sections,
|
|
152
157
|
getSignalScore,
|
|
153
158
|
buildSuggestedActions,
|
|
154
|
-
buildSourceItems,
|
|
159
|
+
buildSourceItems: _buildSourceItems,
|
|
155
160
|
getTimelineEvents,
|
|
156
161
|
accountContacts,
|
|
157
162
|
emailSignature,
|
|
@@ -159,6 +164,7 @@ export function DetailView({
|
|
|
159
164
|
onOpenEntityPanel,
|
|
160
165
|
onOpenRecentActivity,
|
|
161
166
|
onSuggestedActionFeedback: _onSuggestedActionFeedback,
|
|
167
|
+
onScoreFeedback: _onScoreFeedback,
|
|
162
168
|
onSignalApprove,
|
|
163
169
|
getSignalApprovalState,
|
|
164
170
|
signalLabels,
|
|
@@ -169,33 +175,35 @@ export function DetailView({
|
|
|
169
175
|
renderBeforeScore,
|
|
170
176
|
renderAfterScore,
|
|
171
177
|
lastActivityTime,
|
|
178
|
+
renderTitleExtra,
|
|
179
|
+
renderTitleSubtext,
|
|
172
180
|
renderMetadataExtra,
|
|
173
|
-
|
|
181
|
+
onOpenSignalBucket,
|
|
174
182
|
approveButtonIconUrl,
|
|
175
183
|
opportunityPreview,
|
|
176
184
|
onRequestApproval,
|
|
177
185
|
attentionCount,
|
|
178
186
|
}: DetailViewProps) {
|
|
179
|
-
const [evidenceExpanded, setEvidenceExpanded] = React.useState(false)
|
|
180
187
|
const [showTimeline, setShowTimeline] = React.useState(false)
|
|
181
188
|
const [extraActions, setExtraActions] = React.useState<SuggestedAction[]>([])
|
|
189
|
+
const [priorityOpen, setPriorityOpen] = React.useState(false)
|
|
190
|
+
const priorityPanelId = React.useId()
|
|
182
191
|
|
|
183
192
|
React.useEffect(() => {
|
|
184
193
|
setShowTimeline(false)
|
|
185
|
-
setEvidenceExpanded(false)
|
|
186
194
|
setExtraActions([])
|
|
195
|
+
setPriorityOpen(false)
|
|
187
196
|
}, [item.id])
|
|
188
197
|
|
|
189
198
|
const signalData = React.useMemo(
|
|
190
199
|
() => getSignalScore(item.company, item),
|
|
191
|
-
[getSignalScore, item
|
|
200
|
+
[getSignalScore, item],
|
|
192
201
|
)
|
|
193
202
|
|
|
194
203
|
const suggestedActions = React.useMemo(
|
|
195
204
|
() => [...buildSuggestedActions(item), ...extraActions],
|
|
196
205
|
[buildSuggestedActions, item, extraActions],
|
|
197
206
|
)
|
|
198
|
-
const sourceItems = React.useMemo(() => buildSourceItems(item), [buildSourceItems, item])
|
|
199
207
|
const timelineEvents = React.useMemo(
|
|
200
208
|
() => getTimelineEvents?.(item) ?? [],
|
|
201
209
|
[getTimelineEvents, item],
|
|
@@ -249,21 +257,22 @@ export function DetailView({
|
|
|
249
257
|
<span className="text-xs text-muted-foreground">{item.company}</span>
|
|
250
258
|
</div>
|
|
251
259
|
|
|
252
|
-
<
|
|
260
|
+
<div className="mb-3 flex flex-wrap items-start gap-x-3 gap-y-2">
|
|
261
|
+
<div className="min-w-0 flex-1">
|
|
262
|
+
<h1 className="text-2xl font-bold tracking-tight text-foreground">{item.title}</h1>
|
|
263
|
+
{renderTitleSubtext?.(item)}
|
|
264
|
+
</div>
|
|
265
|
+
{renderTitleExtra?.(item)}
|
|
266
|
+
</div>
|
|
253
267
|
|
|
254
268
|
<div className="mb-6 flex flex-wrap items-center gap-2">
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
</div>
|
|
263
|
-
)}
|
|
264
|
-
<div className="inline-flex items-center gap-1 rounded-md bg-muted px-2.5 py-1 text-xs font-medium text-muted-foreground">
|
|
265
|
-
{item.tag1}
|
|
266
|
-
</div>
|
|
269
|
+
<SignalPriorityChip
|
|
270
|
+
score={signalData.score}
|
|
271
|
+
urgencyLabel={signalData.urgencyLabel}
|
|
272
|
+
isOpen={priorityOpen}
|
|
273
|
+
controlsId={priorityPanelId}
|
|
274
|
+
onClick={() => setPriorityOpen((prev) => !prev)}
|
|
275
|
+
/>
|
|
267
276
|
{signalData.timeChipLabel && (
|
|
268
277
|
<Badge variant="outline" title={signalData.timeChipDetail ?? undefined}>
|
|
269
278
|
{signalData.timeChipLabel}
|
|
@@ -283,37 +292,10 @@ export function DetailView({
|
|
|
283
292
|
{renderMetadataExtra?.(item)}
|
|
284
293
|
</div>
|
|
285
294
|
|
|
295
|
+
{priorityOpen && <SignalPriorityPanel id={priorityPanelId} signalData={signalData} className="mb-6" />}
|
|
296
|
+
|
|
286
297
|
{/* Signal Brief */}
|
|
287
298
|
{sections.signalBrief && (() => {
|
|
288
|
-
const pct = signalData.score
|
|
289
|
-
const scoreColor = pct >= 70 ? "text-emerald-600" : pct >= 40 ? "text-amber-600" : "text-red-600"
|
|
290
|
-
const barColor = pct >= 70 ? "bg-emerald-500" : pct >= 40 ? "bg-amber-500" : "bg-red-500"
|
|
291
|
-
const scoreLabel = pct >= 70 ? "HIGH" : pct >= 40 ? "MEDIUM" : "LOW"
|
|
292
|
-
|
|
293
|
-
const evidenceWithCitations: React.ReactNode[] =
|
|
294
|
-
sourceItems.length >= 4
|
|
295
|
-
? [
|
|
296
|
-
<>
|
|
297
|
-
There are <span className="font-medium text-foreground">3 unusual signals</span> including a large balance
|
|
298
|
-
outflow and reduced login frequency
|
|
299
|
-
<Citation number={1} source={sourceItems[0]} />
|
|
300
|
-
<Citation number={2} source={sourceItems[1]} />
|
|
301
|
-
<Citation number={3} source={sourceItems[2]} />
|
|
302
|
-
</>,
|
|
303
|
-
<>
|
|
304
|
-
Scott mentioned in <span className="font-medium text-foreground">#treasury-questions</span> that they are actively
|
|
305
|
-
looking for treasury management options
|
|
306
|
-
<Citation number={4} source={sourceItems[2]} />
|
|
307
|
-
</>,
|
|
308
|
-
<>
|
|
309
|
-
You have a recent email thread regarding optimization options that hasn't been replied to
|
|
310
|
-
<Citation number={5} source={sourceItems[3]} />
|
|
311
|
-
</>,
|
|
312
|
-
]
|
|
313
|
-
: signalData.evidence.map((ev, i) => (
|
|
314
|
-
<span key={i}>{ev}</span>
|
|
315
|
-
))
|
|
316
|
-
|
|
317
299
|
const briefHeading = signalBriefCopy?.heading ?? "Signal brief"
|
|
318
300
|
const introOpt = signalBriefCopy?.intro
|
|
319
301
|
const briefIntro =
|
|
@@ -355,59 +337,14 @@ export function DetailView({
|
|
|
355
337
|
{/* Before-score content slot (e.g. "Signals on Case" chips) */}
|
|
356
338
|
{renderBeforeScore?.(item)}
|
|
357
339
|
|
|
358
|
-
<
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
<div className="flex items-center gap-2">
|
|
366
|
-
<span className="text-sm font-bold text-foreground">{signalData.score}/100</span>
|
|
367
|
-
<span className={`text-[10px] font-bold uppercase ${scoreColor}`}>{scoreLabel}</span>
|
|
368
|
-
<ScoreFeedback.Trigger />
|
|
369
|
-
</div>
|
|
370
|
-
</div>
|
|
371
|
-
<ScoreFeedback.Panel />
|
|
372
|
-
<div className="h-1.5 bg-muted rounded-full overflow-hidden mb-2">
|
|
373
|
-
<div
|
|
374
|
-
className={`h-full rounded-full transition-all duration-500 ${barColor}`}
|
|
375
|
-
style={{ width: `${signalData.score}%` }}
|
|
376
|
-
/>
|
|
377
|
-
</div>
|
|
378
|
-
<button
|
|
379
|
-
type="button"
|
|
380
|
-
onClick={() => setEvidenceExpanded((prev) => !prev)}
|
|
381
|
-
className="flex items-center gap-1 text-[11px] font-medium text-muted-foreground hover:text-foreground transition-colors"
|
|
382
|
-
>
|
|
383
|
-
<ChevronDown className={`h-3 w-3 transition-transform duration-200 ${evidenceExpanded ? "rotate-180" : ""}`} />
|
|
384
|
-
View more
|
|
385
|
-
</button>
|
|
386
|
-
|
|
387
|
-
{evidenceExpanded && (
|
|
388
|
-
<div className="mt-3 space-y-3">
|
|
389
|
-
<ul className="space-y-2">
|
|
390
|
-
{evidenceWithCitations.map((ev, index) => (
|
|
391
|
-
<li key={index} className="flex items-start gap-2 text-sm">
|
|
392
|
-
<div className="w-1.5 h-1.5 bg-primary rounded-full mt-2 flex-shrink-0" />
|
|
393
|
-
<span className="text-muted-foreground leading-relaxed">{ev}</span>
|
|
394
|
-
</li>
|
|
395
|
-
))}
|
|
396
|
-
</ul>
|
|
397
|
-
<ScoreBreakdown
|
|
398
|
-
factors={signalData.factors}
|
|
399
|
-
onFactorFeedback={signalData.onFactorFeedback ?? ((key, type, detail) =>
|
|
400
|
-
console.log("Signal factor feedback:", { company: item.company, factor: key, type, detail })
|
|
401
|
-
)}
|
|
402
|
-
initialFeedback={signalData.initialFactorFeedback}
|
|
403
|
-
/>
|
|
404
|
-
<SignalApproval.Actions />
|
|
405
|
-
</div>
|
|
406
|
-
)}
|
|
340
|
+
<ScoreWhyChips
|
|
341
|
+
item={item}
|
|
342
|
+
signalData={signalData}
|
|
343
|
+
onOpenSignalBucket={onOpenSignalBucket}
|
|
344
|
+
/>
|
|
345
|
+
<div className="mt-4">
|
|
346
|
+
<SignalApproval.Actions />
|
|
407
347
|
</div>
|
|
408
|
-
</ScoreFeedback.Root>
|
|
409
|
-
|
|
410
|
-
{!evidenceExpanded && <SignalApproval.Actions />}
|
|
411
348
|
</div>
|
|
412
349
|
)
|
|
413
350
|
})()}
|
|
@@ -495,6 +432,7 @@ export function PrototypeInboxView({
|
|
|
495
432
|
hideHoverActions,
|
|
496
433
|
onSuggestedActionFeedback,
|
|
497
434
|
onScoreFeedback,
|
|
435
|
+
onOpenSignalBucket,
|
|
498
436
|
headerActions,
|
|
499
437
|
onOpenEntityPanel,
|
|
500
438
|
onOpenRecentActivity,
|
|
@@ -514,6 +452,8 @@ export function PrototypeInboxView({
|
|
|
514
452
|
renderBeforeScore,
|
|
515
453
|
renderAfterScore,
|
|
516
454
|
lastActivityTime,
|
|
455
|
+
renderTitleExtra,
|
|
456
|
+
renderTitleSubtext,
|
|
517
457
|
sortOptions,
|
|
518
458
|
activeSortId,
|
|
519
459
|
onSortChange,
|
|
@@ -741,6 +681,7 @@ export function PrototypeInboxView({
|
|
|
741
681
|
onOpenEntityPanel,
|
|
742
682
|
onOpenRecentActivity,
|
|
743
683
|
onSuggestedActionFeedback,
|
|
684
|
+
onScoreFeedback,
|
|
744
685
|
onSignalApprove,
|
|
745
686
|
getSignalApprovalState,
|
|
746
687
|
signalLabels,
|
|
@@ -751,7 +692,9 @@ export function PrototypeInboxView({
|
|
|
751
692
|
renderBeforeScore,
|
|
752
693
|
renderAfterScore,
|
|
753
694
|
lastActivityTime,
|
|
754
|
-
|
|
695
|
+
renderTitleExtra,
|
|
696
|
+
renderTitleSubtext,
|
|
697
|
+
onOpenSignalBucket,
|
|
755
698
|
}
|
|
756
699
|
|
|
757
700
|
return (
|