@handled-ai/design-system 0.9.24 → 0.9.25
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/charts/chart.d.ts +1 -1
- package/dist/components/score-breakdown.js +32 -5
- package/dist/components/score-breakdown.js.map +1 -1
- package/dist/components/score-feedback.js +5 -1
- package/dist/components/score-feedback.js.map +1 -1
- package/dist/components/signal-feedback-inline.d.ts +13 -2
- package/dist/components/signal-feedback-inline.js +33 -4
- package/dist/components/signal-feedback-inline.js.map +1 -1
- package/dist/prototype/prototype-config.d.ts +2 -1
- package/dist/prototype/prototype-inbox-view.d.ts +1 -1
- package/dist/prototype/prototype-inbox-view.js.map +1 -1
- package/package.json +23 -6
- package/src/__test-helpers__/fixtures.ts +152 -0
- package/src/components/__tests__/score-breakdown-initial.test.tsx +310 -0
- package/src/components/__tests__/score-feedback-initial.test.tsx +253 -0
- package/src/components/score-breakdown.tsx +41 -13
- package/src/components/score-feedback.tsx +8 -1
- package/src/components/signal-feedback-inline.tsx +51 -5
- package/src/prototype/prototype-config.ts +2 -1
- package/src/prototype/prototype-inbox-view.tsx +1 -1
|
@@ -71,7 +71,7 @@ const approveReasons = [
|
|
|
71
71
|
"Actionable",
|
|
72
72
|
]
|
|
73
73
|
|
|
74
|
-
type ApprovalState = "pending" | "confirming" | "approving-feedback" | "dismissing" | "approved" | "dismissed" | "auto-approved"
|
|
74
|
+
type ApprovalState = "pending" | "confirming" | "creating" | "approving-feedback" | "dismissing" | "approved" | "dismissed" | "auto-approved"
|
|
75
75
|
|
|
76
76
|
interface SignalApprovalLabels {
|
|
77
77
|
approveButton?: string
|
|
@@ -82,6 +82,8 @@ interface SignalApprovalLabels {
|
|
|
82
82
|
confirmPrompt?: string
|
|
83
83
|
dismissPrompt?: string
|
|
84
84
|
feedbackPrompt?: string
|
|
85
|
+
/** Label shown while the approve action is in progress (e.g. "Creating Opportunity..."). */
|
|
86
|
+
creatingStatus?: string
|
|
85
87
|
}
|
|
86
88
|
|
|
87
89
|
const DEFAULT_LABELS: Required<SignalApprovalLabels> = {
|
|
@@ -93,6 +95,7 @@ const DEFAULT_LABELS: Required<SignalApprovalLabels> = {
|
|
|
93
95
|
confirmPrompt: "This will approve this action for",
|
|
94
96
|
dismissPrompt: "What\u2019s the issue with this action?",
|
|
95
97
|
feedbackPrompt: "Quick feedback \u2014 what made this action useful?",
|
|
98
|
+
creatingStatus: "Creating\u2026",
|
|
96
99
|
}
|
|
97
100
|
|
|
98
101
|
interface SignalApprovalContextValue {
|
|
@@ -128,7 +131,16 @@ interface RootProps {
|
|
|
128
131
|
labels?: SignalApprovalLabels
|
|
129
132
|
/** When true, the approve/create-opportunity button is hidden but the dismiss button remains. */
|
|
130
133
|
hideApproveButton?: boolean
|
|
131
|
-
|
|
134
|
+
/**
|
|
135
|
+
* Called when the user confirms the approval action.
|
|
136
|
+
*
|
|
137
|
+
* - If the callback returns `void` (or `undefined`), the component transitions
|
|
138
|
+
* directly to the feedback step (backward-compatible behavior).
|
|
139
|
+
* - If the callback returns a `Promise<boolean>`, the component shows a
|
|
140
|
+
* "creating" loading state while the promise is pending. On `true` it
|
|
141
|
+
* transitions to the feedback step; on `false` it reverts to "pending".
|
|
142
|
+
*/
|
|
143
|
+
onApprove?: () => void | Promise<boolean>
|
|
132
144
|
onApproveFeedback?: (reasons: string[], detail: string) => void
|
|
133
145
|
onDismiss?: (reasons: string[], detail: string, subReason?: string) => void
|
|
134
146
|
}
|
|
@@ -137,6 +149,13 @@ function Root({ children, companyName, opportunityUrl, scheduledTime, initialApp
|
|
|
137
149
|
const labels = React.useMemo(() => ({ ...DEFAULT_LABELS, ...labelOverrides }), [labelOverrides])
|
|
138
150
|
const [approvalState, setApprovalState] = React.useState<ApprovalState>(initialApprovalState ?? "pending")
|
|
139
151
|
|
|
152
|
+
// Guard against state updates after unmount (e.g. user navigates away while
|
|
153
|
+
// an async onApprove promise is still in flight).
|
|
154
|
+
const mountedRef = React.useRef(true)
|
|
155
|
+
React.useEffect(() => {
|
|
156
|
+
return () => { mountedRef.current = false }
|
|
157
|
+
}, [])
|
|
158
|
+
|
|
140
159
|
const requestApproval = React.useCallback(() => {
|
|
141
160
|
setApprovalState("confirming")
|
|
142
161
|
}, [])
|
|
@@ -150,8 +169,23 @@ function Root({ children, companyName, opportunityUrl, scheduledTime, initialApp
|
|
|
150
169
|
}, [])
|
|
151
170
|
|
|
152
171
|
const approve = React.useCallback(() => {
|
|
153
|
-
|
|
154
|
-
|
|
172
|
+
const result = onApprove?.()
|
|
173
|
+
// If the callback returns a Promise, show a loading state and wait for it.
|
|
174
|
+
if (result && typeof (result as Promise<boolean>).then === "function") {
|
|
175
|
+
setApprovalState("creating")
|
|
176
|
+
;(result as Promise<boolean>).then((success) => {
|
|
177
|
+
if (mountedRef.current) {
|
|
178
|
+
setApprovalState(success ? "approving-feedback" : "pending")
|
|
179
|
+
}
|
|
180
|
+
}).catch(() => {
|
|
181
|
+
if (mountedRef.current) {
|
|
182
|
+
setApprovalState("pending")
|
|
183
|
+
}
|
|
184
|
+
})
|
|
185
|
+
} else {
|
|
186
|
+
// Synchronous / void — transition immediately (backward-compatible).
|
|
187
|
+
setApprovalState("approving-feedback")
|
|
188
|
+
}
|
|
155
189
|
}, [onApprove])
|
|
156
190
|
|
|
157
191
|
const submitApproveFeedback = React.useCallback(
|
|
@@ -439,6 +473,18 @@ function Actions() {
|
|
|
439
473
|
setDetailText("")
|
|
440
474
|
}
|
|
441
475
|
|
|
476
|
+
if (approvalState === "creating") {
|
|
477
|
+
return (
|
|
478
|
+
<div className="flex items-center gap-2 text-xs font-medium text-muted-foreground">
|
|
479
|
+
<svg className="h-3.5 w-3.5 animate-spin" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
|
480
|
+
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
|
|
481
|
+
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" />
|
|
482
|
+
</svg>
|
|
483
|
+
<span>{labels.creatingStatus}</span>
|
|
484
|
+
</div>
|
|
485
|
+
)
|
|
486
|
+
}
|
|
487
|
+
|
|
442
488
|
if (approvalState === "approved") {
|
|
443
489
|
if (isEditing) {
|
|
444
490
|
return (
|
|
@@ -728,7 +774,7 @@ function Gate({ children }: { children: React.ReactNode }) {
|
|
|
728
774
|
const { approvalState, hideApproveButton } = useSignalApproval()
|
|
729
775
|
// When the approve button is hidden, don't lock content behind approval
|
|
730
776
|
const isLocked = !hideApproveButton &&
|
|
731
|
-
(approvalState === "pending" || approvalState === "confirming" || approvalState === "dismissing")
|
|
777
|
+
(approvalState === "pending" || approvalState === "confirming" || approvalState === "creating" || approvalState === "dismissing")
|
|
732
778
|
|
|
733
779
|
return (
|
|
734
780
|
<div className="relative">
|
|
@@ -80,7 +80,7 @@ export interface InboxViewConfig {
|
|
|
80
80
|
quickFilterTabs?: Array<{ id: string; label: string; matchValue?: string; count?: number }>
|
|
81
81
|
hideAccountsButton?: boolean
|
|
82
82
|
accountDetailsLabel?: string
|
|
83
|
-
onSignalApprove?: (item: QueueItem) => void
|
|
83
|
+
onSignalApprove?: (item: QueueItem) => void | Promise<boolean>
|
|
84
84
|
getSignalApprovalState?: (item: QueueItem) => ApprovalState | undefined
|
|
85
85
|
signalLabels?: {
|
|
86
86
|
approveButton?: string
|
|
@@ -89,6 +89,7 @@ export interface InboxViewConfig {
|
|
|
89
89
|
dismissedStatus?: string
|
|
90
90
|
opportunityCreated?: string
|
|
91
91
|
confirmPrompt?: string
|
|
92
|
+
creatingStatus?: string
|
|
92
93
|
}
|
|
93
94
|
/** When true, the approve/create-opportunity button is hidden but the dismiss button remains. */
|
|
94
95
|
hideApproveButton?: boolean
|
|
@@ -102,7 +102,7 @@ export interface DetailViewProps {
|
|
|
102
102
|
onOpenEntityPanel?: () => void
|
|
103
103
|
onOpenRecentActivity?: () => void
|
|
104
104
|
onSuggestedActionFeedback?: (actionId: number | string, feedback: string, actionTitle?: string) => void
|
|
105
|
-
onSignalApprove?: (item: QueueItem) => void
|
|
105
|
+
onSignalApprove?: (item: QueueItem) => void | Promise<boolean>
|
|
106
106
|
getSignalApprovalState?: (item: QueueItem) => ApprovalState | undefined
|
|
107
107
|
signalLabels?: InboxViewConfig["signalLabels"]
|
|
108
108
|
hideApproveButton?: boolean
|