@handled-ai/design-system 0.8.0 → 0.9.0
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/README.md +14 -4
- package/dist/charts/bar-chart-component.d.ts +24 -0
- package/dist/charts/bar-chart-component.js +123 -0
- package/dist/charts/bar-chart-component.js.map +1 -0
- package/dist/charts/chart-tooltip.d.ts +26 -0
- package/dist/charts/chart-tooltip.js +69 -0
- package/dist/charts/chart-tooltip.js.map +1 -0
- package/dist/charts/chart.d.ts +64 -0
- package/dist/charts/chart.js +285 -0
- package/dist/charts/chart.js.map +1 -0
- package/dist/charts/donut-chart.d.ts +21 -0
- package/dist/charts/donut-chart.js +96 -0
- package/dist/charts/donut-chart.js.map +1 -0
- package/dist/charts/index.d.ts +11 -0
- package/dist/charts/index.js +10 -0
- package/dist/charts/index.js.map +1 -0
- package/dist/charts/pipeline-overview.d.ts +76 -0
- package/dist/charts/pipeline-overview.js +372 -0
- package/dist/charts/pipeline-overview.js.map +1 -0
- package/dist/charts/sankey-chart.d.ts +52 -0
- package/dist/charts/sankey-chart.js +219 -0
- package/dist/charts/sankey-chart.js.map +1 -0
- package/dist/charts/top-line-metrics.d.ts +26 -0
- package/dist/charts/top-line-metrics.js +224 -0
- package/dist/charts/top-line-metrics.js.map +1 -0
- package/dist/charts/trend-area-chart.d.ts +21 -0
- package/dist/charts/trend-area-chart.js +150 -0
- package/dist/charts/trend-area-chart.js.map +1 -0
- package/dist/charts/volume-analysis-chart.d.ts +19 -0
- package/dist/charts/volume-analysis-chart.js +121 -0
- package/dist/charts/volume-analysis-chart.js.map +1 -0
- package/dist/components/activity-detail.d.ts +38 -0
- package/dist/components/activity-detail.js +163 -0
- package/dist/components/activity-detail.js.map +1 -0
- package/dist/components/activity-log.d.ts +21 -0
- package/dist/components/activity-log.js +61 -0
- package/dist/components/activity-log.js.map +1 -0
- package/dist/components/agent-popover.d.ts +71 -0
- package/dist/components/agent-popover.js +282 -0
- package/dist/components/agent-popover.js.map +1 -0
- package/dist/components/agent-widget.d.ts +24 -0
- package/dist/components/agent-widget.js +117 -0
- package/dist/components/agent-widget.js.map +1 -0
- package/dist/components/avatar.d.ts +13 -0
- package/dist/components/avatar.js +140 -0
- package/dist/components/avatar.js.map +1 -0
- package/dist/components/badge.d.ts +12 -0
- package/dist/components/badge.js +75 -0
- package/dist/components/badge.js.map +1 -0
- package/dist/components/button.d.ts +13 -0
- package/dist/components/button.js +83 -0
- package/dist/components/button.js.map +1 -0
- package/dist/components/card.d.ts +11 -0
- package/dist/components/card.js +119 -0
- package/dist/components/card.js.map +1 -0
- package/dist/components/contact-list.d.ts +34 -0
- package/dist/components/contact-list.js +84 -0
- package/dist/components/contact-list.js.map +1 -0
- package/dist/components/dashboard-cards.d.ts +10 -0
- package/dist/components/dashboard-cards.js +164 -0
- package/dist/components/dashboard-cards.js.map +1 -0
- package/dist/components/data-table-display.d.ts +19 -0
- package/dist/components/data-table-display.js +109 -0
- package/dist/components/data-table-display.js.map +1 -0
- package/dist/components/data-table-filter.d.ts +18 -0
- package/dist/components/data-table-filter.js +107 -0
- package/dist/components/data-table-filter.js.map +1 -0
- package/dist/components/data-table-quick-views.d.ts +13 -0
- package/dist/components/data-table-quick-views.js +90 -0
- package/dist/components/data-table-quick-views.js.map +1 -0
- package/dist/components/data-table-toolbar.d.ts +18 -0
- package/dist/components/data-table-toolbar.js +45 -0
- package/dist/components/data-table-toolbar.js.map +1 -0
- package/dist/components/data-table.d.ts +39 -0
- package/dist/components/data-table.js +821 -0
- package/dist/components/data-table.js.map +1 -0
- package/dist/components/detail-view.d.ts +44 -0
- package/dist/components/detail-view.js +165 -0
- package/dist/components/detail-view.js.map +1 -0
- package/dist/components/dialog.d.ts +19 -0
- package/dist/components/dialog.js +188 -0
- package/dist/components/dialog.js.map +1 -0
- package/dist/components/dropdown-menu.d.ts +27 -0
- package/dist/components/dropdown-menu.js +279 -0
- package/dist/components/dropdown-menu.js.map +1 -0
- package/dist/components/entity-panel.d.ts +69 -0
- package/dist/components/entity-panel.js +584 -0
- package/dist/components/entity-panel.js.map +1 -0
- package/dist/components/inbox-row.d.ts +27 -0
- package/dist/components/inbox-row.js +139 -0
- package/dist/components/inbox-row.js.map +1 -0
- package/dist/components/inbox-toolbar.d.ts +21 -0
- package/dist/components/inbox-toolbar.js +203 -0
- package/dist/components/inbox-toolbar.js.map +1 -0
- package/dist/components/input.d.ts +5 -0
- package/dist/components/input.js +50 -0
- package/dist/components/input.js.map +1 -0
- package/dist/components/insights-filter-bar.d.ts +21 -0
- package/dist/components/insights-filter-bar.js +99 -0
- package/dist/components/insights-filter-bar.js.map +1 -0
- package/dist/components/item-list-display.d.ts +22 -0
- package/dist/components/item-list-display.js +240 -0
- package/dist/components/item-list-display.js.map +1 -0
- package/dist/components/item-list-filter.d.ts +16 -0
- package/dist/components/item-list-filter.js +87 -0
- package/dist/components/item-list-filter.js.map +1 -0
- package/dist/components/item-list-toolbar.d.ts +25 -0
- package/dist/components/item-list-toolbar.js +79 -0
- package/dist/components/item-list-toolbar.js.map +1 -0
- package/dist/components/item-list.d.ts +20 -0
- package/dist/components/item-list.js +702 -0
- package/dist/components/item-list.js.map +1 -0
- package/dist/components/label.d.ts +6 -0
- package/dist/components/label.js +55 -0
- package/dist/components/label.js.map +1 -0
- package/dist/components/message.d.ts +23 -0
- package/dist/components/message.js +117 -0
- package/dist/components/message.js.map +1 -0
- package/dist/components/metric-card.d.ts +25 -0
- package/dist/components/metric-card.js +107 -0
- package/dist/components/metric-card.js.map +1 -0
- package/dist/components/performance-metrics-table.d.ts +38 -0
- package/dist/components/performance-metrics-table.js +342 -0
- package/dist/components/performance-metrics-table.js.map +1 -0
- package/dist/components/preview-list.d.ts +14 -0
- package/dist/components/preview-list.js +83 -0
- package/dist/components/preview-list.js.map +1 -0
- package/dist/components/progress.d.ts +6 -0
- package/dist/components/progress.js +69 -0
- package/dist/components/progress.js.map +1 -0
- package/dist/components/quick-action-chat-area.d.ts +24 -0
- package/dist/components/quick-action-chat-area.js +178 -0
- package/dist/components/quick-action-chat-area.js.map +1 -0
- package/dist/components/quick-action-modal.d.ts +30 -0
- package/dist/components/quick-action-modal.js +288 -0
- package/dist/components/quick-action-modal.js.map +1 -0
- package/dist/components/quick-action-sidebar-nav.d.ts +51 -0
- package/dist/components/quick-action-sidebar-nav.js +528 -0
- package/dist/components/quick-action-sidebar-nav.js.map +1 -0
- package/dist/components/recommended-actions-section.d.ts +23 -0
- package/dist/components/recommended-actions-section.js +215 -0
- package/dist/components/recommended-actions-section.js.map +1 -0
- package/dist/components/report-card.d.ts +26 -0
- package/dist/components/report-card.js +69 -0
- package/dist/components/report-card.js.map +1 -0
- package/dist/components/score-analysis-modal.d.ts +26 -0
- package/dist/components/score-analysis-modal.js +141 -0
- package/dist/components/score-analysis-modal.js.map +1 -0
- package/dist/components/score-breakdown.d.ts +17 -0
- package/dist/components/score-breakdown.js +162 -0
- package/dist/components/score-breakdown.js.map +1 -0
- package/dist/components/score-feedback.d.ts +40 -0
- package/dist/components/score-feedback.js +209 -0
- package/dist/components/score-feedback.js.map +1 -0
- package/dist/components/score-ring.d.ts +14 -0
- package/dist/components/score-ring.js +79 -0
- package/dist/components/score-ring.js.map +1 -0
- package/dist/components/scroll-area.d.ts +7 -0
- package/dist/components/scroll-area.js +101 -0
- package/dist/components/scroll-area.js.map +1 -0
- package/dist/components/select.d.ts +17 -0
- package/dist/components/select.js +228 -0
- package/dist/components/select.js.map +1 -0
- package/dist/components/separator.d.ts +6 -0
- package/dist/components/separator.js +61 -0
- package/dist/components/separator.js.map +1 -0
- package/dist/components/sheet.d.ts +16 -0
- package/dist/components/sheet.js +168 -0
- package/dist/components/sheet.js.map +1 -0
- package/dist/components/sidebar.d.ts +73 -0
- package/dist/components/sidebar.js +723 -0
- package/dist/components/sidebar.js.map +1 -0
- package/dist/components/signal-feedback-inline.d.ts +51 -0
- package/dist/components/signal-feedback-inline.js +548 -0
- package/dist/components/signal-feedback-inline.js.map +1 -0
- package/dist/components/simple-data-table.d.ts +15 -0
- package/dist/components/simple-data-table.js +91 -0
- package/dist/components/simple-data-table.js.map +1 -0
- package/dist/components/skeleton.d.ts +5 -0
- package/dist/components/skeleton.js +44 -0
- package/dist/components/skeleton.js.map +1 -0
- package/dist/components/status-badge.d.ts +10 -0
- package/dist/components/status-badge.js +82 -0
- package/dist/components/status-badge.js.map +1 -0
- package/dist/components/styled-bar-list.d.ts +20 -0
- package/dist/components/styled-bar-list.js +59 -0
- package/dist/components/styled-bar-list.js.map +1 -0
- package/dist/components/suggested-actions.d.ts +110 -0
- package/dist/components/suggested-actions.js +1538 -0
- package/dist/components/suggested-actions.js.map +1 -0
- package/dist/components/table.d.ts +12 -0
- package/dist/components/table.js +147 -0
- package/dist/components/table.js.map +1 -0
- package/dist/components/tabs.d.ts +14 -0
- package/dist/components/tabs.js +129 -0
- package/dist/components/tabs.js.map +1 -0
- package/dist/components/textarea.d.ts +5 -0
- package/dist/components/textarea.js +47 -0
- package/dist/components/textarea.js.map +1 -0
- package/dist/components/timeline-activity.d.ts +34 -0
- package/dist/components/timeline-activity.js +181 -0
- package/dist/components/timeline-activity.js.map +1 -0
- package/dist/components/tooltip.d.ts +9 -0
- package/dist/components/tooltip.js +93 -0
- package/dist/components/tooltip.js.map +1 -0
- package/dist/components/view-mode-toggle.d.ts +16 -0
- package/dist/components/view-mode-toggle.js +24 -0
- package/dist/components/view-mode-toggle.js.map +1 -0
- package/dist/hooks/use-mobile.d.ts +3 -0
- package/dist/hooks/use-mobile.js +21 -0
- package/dist/hooks/use-mobile.js.map +1 -0
- package/dist/index.d.ts +68 -1878
- package/dist/index.js +69 -10918
- package/dist/index.js.map +1 -1
- package/dist/lib/icons.d.ts +18 -0
- package/dist/lib/icons.js +21 -0
- package/dist/lib/icons.js.map +1 -0
- package/dist/lib/utils.d.ts +5 -0
- package/dist/lib/utils.js +9 -0
- package/dist/lib/utils.js.map +1 -0
- package/dist/prototype/index.d.ts +20 -0
- package/dist/prototype/index.js +8 -0
- package/dist/prototype/index.js.map +1 -0
- package/dist/prototype/prototype-accounts-view.d.ts +22 -0
- package/dist/prototype/prototype-accounts-view.js +70 -0
- package/dist/prototype/prototype-accounts-view.js.map +1 -0
- package/dist/prototype/prototype-admin-view.d.ts +21 -0
- package/dist/prototype/prototype-admin-view.js +53 -0
- package/dist/prototype/prototype-admin-view.js.map +1 -0
- package/dist/prototype/prototype-config.d.ts +226 -0
- package/dist/prototype/prototype-config.js +1 -0
- package/dist/prototype/prototype-config.js.map +1 -0
- package/dist/prototype/prototype-inbox-view.d.ts +48 -0
- package/dist/prototype/prototype-inbox-view.js +701 -0
- package/dist/prototype/prototype-inbox-view.js.map +1 -0
- package/dist/prototype/prototype-insights-view.d.ts +23 -0
- package/dist/prototype/prototype-insights-view.js +335 -0
- package/dist/prototype/prototype-insights-view.js.map +1 -0
- package/dist/prototype/prototype-shell.d.ts +40 -0
- package/dist/prototype/prototype-shell.js +190 -0
- package/dist/prototype/prototype-shell.js.map +1 -0
- package/dist/prototype/prototype-work-queue-view.d.ts +8 -0
- package/dist/prototype/prototype-work-queue-view.js +17 -0
- package/dist/prototype/prototype-work-queue-view.js.map +1 -0
- package/dist/three/agent-orb.d.ts +39 -0
- package/dist/three/agent-orb.js +500 -0
- package/dist/three/agent-orb.js.map +1 -0
- package/dist/three/index.d.ts +2 -0
- package/dist/three/index.js +2 -0
- package/dist/three/index.js.map +1 -0
- package/package.json +98 -17
- package/src/charts/bar-chart-component.tsx +150 -0
- package/src/charts/chart-tooltip.tsx +86 -0
- package/src/charts/chart.tsx +371 -0
- package/src/charts/donut-chart.tsx +112 -0
- package/src/charts/index.ts +13 -0
- package/src/charts/pipeline-overview.tsx +476 -0
- package/src/charts/sankey-chart.tsx +290 -0
- package/src/charts/top-line-metrics.tsx +261 -0
- package/src/charts/trend-area-chart.tsx +150 -0
- package/src/charts/volume-analysis-chart.tsx +124 -0
- package/src/components/activity-detail.tsx +233 -0
- package/src/components/activity-log.tsx +89 -0
- package/src/components/agent-popover.tsx +373 -0
- package/src/components/agent-widget.tsx +163 -0
- package/src/components/avatar.tsx +109 -0
- package/src/components/badge.tsx +48 -0
- package/src/components/button.tsx +59 -0
- package/src/components/card.tsx +92 -0
- package/src/components/contact-list.tsx +121 -0
- package/src/components/dashboard-cards.tsx +170 -0
- package/src/components/data-table-display.tsx +139 -0
- package/src/components/data-table-filter.tsx +138 -0
- package/src/components/data-table-quick-views.tsx +103 -0
- package/src/components/data-table-toolbar.tsx +56 -0
- package/src/components/data-table.tsx +915 -0
- package/src/components/detail-view.tsx +237 -0
- package/src/components/dialog.tsx +158 -0
- package/src/components/dropdown-menu.tsx +257 -0
- package/src/components/entity-panel.tsx +767 -0
- package/src/components/inbox-row.tsx +132 -0
- package/src/components/inbox-toolbar.tsx +213 -0
- package/src/components/input.tsx +21 -0
- package/src/components/insights-filter-bar.tsx +132 -0
- package/src/components/item-list-display.tsx +278 -0
- package/src/components/item-list-filter.tsx +118 -0
- package/src/components/item-list-toolbar.tsx +97 -0
- package/src/components/item-list.tsx +843 -0
- package/src/components/label.tsx +24 -0
- package/src/components/message.tsx +83 -0
- package/src/components/metric-card.tsx +178 -0
- package/src/components/performance-metrics-table.tsx +442 -0
- package/src/components/preview-list.tsx +62 -0
- package/src/components/progress.tsx +31 -0
- package/src/components/quick-action-chat-area.tsx +156 -0
- package/src/components/quick-action-modal.tsx +331 -0
- package/src/components/quick-action-sidebar-nav.tsx +592 -0
- package/src/components/recommended-actions-section.tsx +258 -0
- package/src/components/report-card.tsx +106 -0
- package/src/components/score-analysis-modal.tsx +172 -0
- package/src/components/score-breakdown.tsx +179 -0
- package/src/components/score-feedback.tsx +288 -0
- package/src/components/score-ring.tsx +87 -0
- package/src/components/scroll-area.tsx +58 -0
- package/src/components/select.tsx +190 -0
- package/src/components/separator.tsx +28 -0
- package/src/components/sheet.tsx +143 -0
- package/src/components/sidebar.tsx +726 -0
- package/src/components/signal-feedback-inline.tsx +591 -0
- package/src/components/simple-data-table.tsx +124 -0
- package/src/components/skeleton.tsx +15 -0
- package/src/components/status-badge.tsx +63 -0
- package/src/components/styled-bar-list.tsx +70 -0
- package/src/components/suggested-actions.tsx +1985 -0
- package/src/components/table.tsx +116 -0
- package/src/components/tabs.tsx +91 -0
- package/src/components/textarea.tsx +18 -0
- package/src/components/timeline-activity.tsx +234 -0
- package/src/components/tooltip.tsx +57 -0
- package/src/components/view-mode-toggle.tsx +39 -0
- package/src/hooks/use-mobile.ts +21 -0
- package/src/index.ts +77 -0
- package/src/lib/icons.ts +18 -0
- package/src/lib/utils.ts +6 -0
- package/src/prototype/index.ts +11 -0
- package/src/prototype/prototype-accounts-view.tsx +112 -0
- package/src/prototype/prototype-admin-view.tsx +67 -0
- package/src/prototype/prototype-config.ts +243 -0
- package/src/prototype/prototype-inbox-view.tsx +810 -0
- package/src/prototype/prototype-insights-view.tsx +379 -0
- package/src/prototype/prototype-shell.tsx +219 -0
- package/src/prototype/prototype-work-queue-view.tsx +30 -0
- package/src/styles/globals.css +299 -0
- package/src/three/agent-orb.tsx +557 -0
- package/src/three/index.ts +5 -0
- package/src/types/r3f.d.ts +8 -0
|
@@ -0,0 +1,591 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { Check, CirclePlus, ExternalLink, Lock, ThumbsDown } from "lucide-react"
|
|
5
|
+
|
|
6
|
+
const dismissReasons = [
|
|
7
|
+
"Bad timing",
|
|
8
|
+
"Inaccurate data",
|
|
9
|
+
"Wrong account",
|
|
10
|
+
"Already handled",
|
|
11
|
+
"Not actionable",
|
|
12
|
+
"Other",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
const approveReasons = [
|
|
16
|
+
"Right timing",
|
|
17
|
+
"Accurate data",
|
|
18
|
+
"Good prospect fit",
|
|
19
|
+
"Actionable",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
type ApprovalState = "pending" | "confirming" | "approving-feedback" | "dismissing" | "approved" | "dismissed" | "auto-approved"
|
|
23
|
+
|
|
24
|
+
interface SignalApprovalLabels {
|
|
25
|
+
approveButton?: string
|
|
26
|
+
dismissButton?: string
|
|
27
|
+
approvedStatus?: string
|
|
28
|
+
dismissedStatus?: string
|
|
29
|
+
confirmPrompt?: string
|
|
30
|
+
dismissPrompt?: string
|
|
31
|
+
feedbackPrompt?: string
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const DEFAULT_LABELS: Required<SignalApprovalLabels> = {
|
|
35
|
+
approveButton: "Approve action",
|
|
36
|
+
dismissButton: "Not Helpful",
|
|
37
|
+
approvedStatus: "Action Approved",
|
|
38
|
+
dismissedStatus: "Action Dismissed",
|
|
39
|
+
confirmPrompt: "This will approve this action for",
|
|
40
|
+
dismissPrompt: "What\u2019s the issue with this action?",
|
|
41
|
+
feedbackPrompt: "Quick feedback \u2014 what made this action useful?",
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface SignalApprovalContextValue {
|
|
45
|
+
approvalState: ApprovalState
|
|
46
|
+
companyName: string
|
|
47
|
+
opportunityUrl?: string
|
|
48
|
+
scheduledTime?: string
|
|
49
|
+
labels: Required<SignalApprovalLabels>
|
|
50
|
+
approve: () => void
|
|
51
|
+
submitApproveFeedback: (reasons: string[], detail: string) => void
|
|
52
|
+
skipApproveFeedback: () => void
|
|
53
|
+
dismiss: (reasons: string[], detail: string) => void
|
|
54
|
+
requestApproval: () => void
|
|
55
|
+
requestDismiss: () => void
|
|
56
|
+
cancel: () => void
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const SignalApprovalCtx = React.createContext<SignalApprovalContextValue | null>(null)
|
|
60
|
+
|
|
61
|
+
export function useSignalApproval() {
|
|
62
|
+
const ctx = React.useContext(SignalApprovalCtx)
|
|
63
|
+
if (!ctx) throw new Error("SignalApproval components must be used within SignalApproval.Root")
|
|
64
|
+
return ctx
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
interface RootProps {
|
|
68
|
+
children: React.ReactNode
|
|
69
|
+
companyName: string
|
|
70
|
+
opportunityUrl?: string
|
|
71
|
+
scheduledTime?: string
|
|
72
|
+
initialApprovalState?: ApprovalState
|
|
73
|
+
labels?: SignalApprovalLabels
|
|
74
|
+
onApprove?: () => void
|
|
75
|
+
onApproveFeedback?: (reasons: string[], detail: string) => void
|
|
76
|
+
onDismiss?: (reasons: string[], detail: string) => void
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function Root({ children, companyName, opportunityUrl, scheduledTime, initialApprovalState, labels: labelOverrides, onApprove, onApproveFeedback, onDismiss }: RootProps) {
|
|
80
|
+
const labels = React.useMemo(() => ({ ...DEFAULT_LABELS, ...labelOverrides }), [labelOverrides])
|
|
81
|
+
const [approvalState, setApprovalState] = React.useState<ApprovalState>(initialApprovalState ?? "pending")
|
|
82
|
+
|
|
83
|
+
const requestApproval = React.useCallback(() => {
|
|
84
|
+
setApprovalState("confirming")
|
|
85
|
+
}, [])
|
|
86
|
+
|
|
87
|
+
const requestDismiss = React.useCallback(() => {
|
|
88
|
+
setApprovalState("dismissing")
|
|
89
|
+
}, [])
|
|
90
|
+
|
|
91
|
+
const cancel = React.useCallback(() => {
|
|
92
|
+
setApprovalState("pending")
|
|
93
|
+
}, [])
|
|
94
|
+
|
|
95
|
+
const approve = React.useCallback(() => {
|
|
96
|
+
setApprovalState("approving-feedback")
|
|
97
|
+
onApprove?.()
|
|
98
|
+
}, [onApprove])
|
|
99
|
+
|
|
100
|
+
const submitApproveFeedback = React.useCallback(
|
|
101
|
+
(reasons: string[], detail: string) => {
|
|
102
|
+
setApprovalState("approved")
|
|
103
|
+
onApproveFeedback?.(reasons, detail)
|
|
104
|
+
},
|
|
105
|
+
[onApproveFeedback]
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
const skipApproveFeedback = React.useCallback(() => {
|
|
109
|
+
setApprovalState("approved")
|
|
110
|
+
}, [])
|
|
111
|
+
|
|
112
|
+
const dismiss = React.useCallback(
|
|
113
|
+
(reasons: string[], detail: string) => {
|
|
114
|
+
setApprovalState("dismissed")
|
|
115
|
+
onDismiss?.(reasons, detail)
|
|
116
|
+
},
|
|
117
|
+
[onDismiss]
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
<SignalApprovalCtx.Provider
|
|
122
|
+
value={{ approvalState, companyName, opportunityUrl, scheduledTime, labels, approve, submitApproveFeedback, skipApproveFeedback, dismiss, requestApproval, requestDismiss, cancel }}
|
|
123
|
+
>
|
|
124
|
+
{children}
|
|
125
|
+
</SignalApprovalCtx.Provider>
|
|
126
|
+
)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function SubmittedFeedback({
|
|
130
|
+
reasons,
|
|
131
|
+
detail,
|
|
132
|
+
variant,
|
|
133
|
+
onEdit,
|
|
134
|
+
}: {
|
|
135
|
+
reasons: string[]
|
|
136
|
+
detail: string
|
|
137
|
+
variant: "approve" | "dismiss"
|
|
138
|
+
onEdit: () => void
|
|
139
|
+
}) {
|
|
140
|
+
if (reasons.length === 0 && !detail) return null
|
|
141
|
+
const pillClass =
|
|
142
|
+
variant === "approve"
|
|
143
|
+
? "border-emerald-200/60 bg-emerald-50/50 text-emerald-700/70"
|
|
144
|
+
: "border-red-200/60 bg-red-50/50 text-red-700/70"
|
|
145
|
+
|
|
146
|
+
return (
|
|
147
|
+
<button
|
|
148
|
+
type="button"
|
|
149
|
+
onClick={onEdit}
|
|
150
|
+
className="w-full text-left space-y-1.5 group cursor-pointer"
|
|
151
|
+
>
|
|
152
|
+
{reasons.length > 0 && (
|
|
153
|
+
<div className="flex flex-wrap gap-1">
|
|
154
|
+
{reasons.map((r) => (
|
|
155
|
+
<span
|
|
156
|
+
key={r}
|
|
157
|
+
className={`rounded-full border px-2 py-0.5 text-[10px] font-medium transition-colors group-hover:opacity-80 ${pillClass}`}
|
|
158
|
+
>
|
|
159
|
+
{r}
|
|
160
|
+
</span>
|
|
161
|
+
))}
|
|
162
|
+
</div>
|
|
163
|
+
)}
|
|
164
|
+
{detail && (
|
|
165
|
+
<p className="text-[11px] text-muted-foreground/70 leading-snug group-hover:text-muted-foreground transition-colors">{detail}</p>
|
|
166
|
+
)}
|
|
167
|
+
</button>
|
|
168
|
+
)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function Actions() {
|
|
172
|
+
const { approvalState, companyName, opportunityUrl, scheduledTime, labels, approve, submitApproveFeedback, skipApproveFeedback, dismiss, requestApproval, requestDismiss, cancel } =
|
|
173
|
+
useSignalApproval()
|
|
174
|
+
const [selectedReasons, setSelectedReasons] = React.useState<string[]>([])
|
|
175
|
+
const [detailText, setDetailText] = React.useState("")
|
|
176
|
+
const [submittedFeedback, setSubmittedFeedback] = React.useState<{ reasons: string[]; detail: string } | null>(null)
|
|
177
|
+
const [isEditing, setIsEditing] = React.useState(false)
|
|
178
|
+
|
|
179
|
+
const otherSelected = selectedReasons.includes("Other")
|
|
180
|
+
const canSubmitDismiss = selectedReasons.length > 0 && (!otherSelected || detailText.trim().length > 0)
|
|
181
|
+
const canSubmitApprove = selectedReasons.length > 0 || detailText.trim().length > 0
|
|
182
|
+
|
|
183
|
+
const toggleReason = (reason: string) => {
|
|
184
|
+
setSelectedReasons((prev) =>
|
|
185
|
+
prev.includes(reason) ? prev.filter((r) => r !== reason) : [...prev, reason]
|
|
186
|
+
)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const startEditing = () => {
|
|
190
|
+
if (submittedFeedback) {
|
|
191
|
+
setSelectedReasons([...submittedFeedback.reasons])
|
|
192
|
+
setDetailText(submittedFeedback.detail)
|
|
193
|
+
}
|
|
194
|
+
setIsEditing(true)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const handleDismissSubmit = () => {
|
|
198
|
+
if (!canSubmitDismiss) return
|
|
199
|
+
const fb = { reasons: [...selectedReasons], detail: detailText.trim() }
|
|
200
|
+
setSubmittedFeedback(fb)
|
|
201
|
+
dismiss(selectedReasons, detailText.trim())
|
|
202
|
+
setSelectedReasons([])
|
|
203
|
+
setDetailText("")
|
|
204
|
+
setIsEditing(false)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const handleApproveSubmit = () => {
|
|
208
|
+
const fb = { reasons: [...selectedReasons], detail: detailText.trim() }
|
|
209
|
+
setSubmittedFeedback(fb)
|
|
210
|
+
submitApproveFeedback(selectedReasons, detailText.trim())
|
|
211
|
+
setSelectedReasons([])
|
|
212
|
+
setDetailText("")
|
|
213
|
+
setIsEditing(false)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const handleEditCancel = () => {
|
|
217
|
+
setSelectedReasons([])
|
|
218
|
+
setDetailText("")
|
|
219
|
+
setIsEditing(false)
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const handleCancel = () => {
|
|
223
|
+
cancel()
|
|
224
|
+
setSelectedReasons([])
|
|
225
|
+
setDetailText("")
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (approvalState === "approved") {
|
|
229
|
+
if (isEditing) {
|
|
230
|
+
return (
|
|
231
|
+
<div className="space-y-3">
|
|
232
|
+
<div className="flex items-center gap-1.5 text-xs font-medium text-emerald-600 mb-2">
|
|
233
|
+
<Check className="h-3.5 w-3.5" />
|
|
234
|
+
{opportunityUrl ? (
|
|
235
|
+
<a href={opportunityUrl} target="_blank" rel="noopener noreferrer" className="inline-flex items-center gap-1 hover:underline underline-offset-2">
|
|
236
|
+
{labels.approvedStatus} <ExternalLink className="h-3 w-3" />
|
|
237
|
+
</a>
|
|
238
|
+
) : (
|
|
239
|
+
<span>{labels.approvedStatus}</span>
|
|
240
|
+
)}
|
|
241
|
+
</div>
|
|
242
|
+
<p className="text-xs font-medium text-muted-foreground">Edit your feedback</p>
|
|
243
|
+
<div className="flex flex-wrap gap-1.5">
|
|
244
|
+
{approveReasons.map((reason) => {
|
|
245
|
+
const selected = selectedReasons.includes(reason)
|
|
246
|
+
return (
|
|
247
|
+
<button key={reason} type="button" onClick={() => toggleReason(reason)}
|
|
248
|
+
className={`rounded-full border px-2.5 py-1 text-[11px] font-medium transition-colors ${
|
|
249
|
+
selected ? "border-emerald-200 bg-emerald-100 text-emerald-700" : "border-border bg-background text-muted-foreground hover:bg-muted/50 hover:text-foreground"
|
|
250
|
+
}`}>{reason}</button>
|
|
251
|
+
)
|
|
252
|
+
})}
|
|
253
|
+
</div>
|
|
254
|
+
<input type="text" value={detailText} onChange={(e) => setDetailText(e.target.value)}
|
|
255
|
+
onKeyDown={(e) => { if (e.key === "Enter" && canSubmitApprove) handleApproveSubmit() }}
|
|
256
|
+
placeholder="Tell us more (optional)"
|
|
257
|
+
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" />
|
|
258
|
+
<div className="flex items-center gap-2">
|
|
259
|
+
<button type="button" onClick={handleApproveSubmit} disabled={!canSubmitApprove}
|
|
260
|
+
className={`inline-flex h-7 items-center gap-1.5 rounded-md px-3 text-xs font-semibold transition-colors ${canSubmitApprove ? "bg-foreground text-background hover:bg-foreground/90" : "cursor-not-allowed bg-muted text-muted-foreground"}`}>
|
|
261
|
+
Save
|
|
262
|
+
</button>
|
|
263
|
+
<button type="button" onClick={handleEditCancel}
|
|
264
|
+
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">
|
|
265
|
+
Cancel
|
|
266
|
+
</button>
|
|
267
|
+
</div>
|
|
268
|
+
</div>
|
|
269
|
+
)
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return (
|
|
273
|
+
<div className="space-y-2">
|
|
274
|
+
<div className="flex items-center gap-1.5 text-xs font-medium text-emerald-600">
|
|
275
|
+
<Check className="h-3.5 w-3.5" />
|
|
276
|
+
{opportunityUrl ? (
|
|
277
|
+
<a
|
|
278
|
+
href={opportunityUrl}
|
|
279
|
+
target="_blank"
|
|
280
|
+
rel="noopener noreferrer"
|
|
281
|
+
className="inline-flex items-center gap-1 hover:underline underline-offset-2"
|
|
282
|
+
>
|
|
283
|
+
Opportunity Created
|
|
284
|
+
<ExternalLink className="h-3 w-3" />
|
|
285
|
+
</a>
|
|
286
|
+
) : (
|
|
287
|
+
<span>Opportunity Created</span>
|
|
288
|
+
)}
|
|
289
|
+
</div>
|
|
290
|
+
{submittedFeedback && (
|
|
291
|
+
<SubmittedFeedback
|
|
292
|
+
reasons={submittedFeedback.reasons}
|
|
293
|
+
detail={submittedFeedback.detail}
|
|
294
|
+
variant="approve"
|
|
295
|
+
onEdit={startEditing}
|
|
296
|
+
/>
|
|
297
|
+
)}
|
|
298
|
+
</div>
|
|
299
|
+
)
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (approvalState === "auto-approved") {
|
|
303
|
+
return (
|
|
304
|
+
<div className="space-y-2">
|
|
305
|
+
<div className="flex items-center gap-1.5 text-xs font-medium text-emerald-600">
|
|
306
|
+
<Check className="h-3.5 w-3.5" />
|
|
307
|
+
<span>{labels.approvedStatus}</span>
|
|
308
|
+
</div>
|
|
309
|
+
{scheduledTime && (
|
|
310
|
+
<p className="text-[11px] text-muted-foreground">Scheduled: {scheduledTime}</p>
|
|
311
|
+
)}
|
|
312
|
+
</div>
|
|
313
|
+
)
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
if (approvalState === "approving-feedback") {
|
|
317
|
+
return (
|
|
318
|
+
<div className="space-y-3">
|
|
319
|
+
<div className="flex items-center gap-1.5 text-xs font-medium text-emerald-600 mb-2">
|
|
320
|
+
<Check className="h-3.5 w-3.5" />
|
|
321
|
+
{opportunityUrl ? (
|
|
322
|
+
<a
|
|
323
|
+
href={opportunityUrl}
|
|
324
|
+
target="_blank"
|
|
325
|
+
rel="noopener noreferrer"
|
|
326
|
+
className="inline-flex items-center gap-1 hover:underline underline-offset-2"
|
|
327
|
+
>
|
|
328
|
+
Opportunity Created
|
|
329
|
+
<ExternalLink className="h-3 w-3" />
|
|
330
|
+
</a>
|
|
331
|
+
) : (
|
|
332
|
+
<span>Opportunity Created</span>
|
|
333
|
+
)}
|
|
334
|
+
</div>
|
|
335
|
+
<p className="text-xs font-medium text-muted-foreground">{labels.feedbackPrompt}</p>
|
|
336
|
+
<div className="flex flex-wrap gap-1.5">
|
|
337
|
+
{approveReasons.map((reason) => {
|
|
338
|
+
const selected = selectedReasons.includes(reason)
|
|
339
|
+
return (
|
|
340
|
+
<button
|
|
341
|
+
key={reason}
|
|
342
|
+
type="button"
|
|
343
|
+
onClick={() => toggleReason(reason)}
|
|
344
|
+
className={`rounded-full border px-2.5 py-1 text-[11px] font-medium transition-colors ${
|
|
345
|
+
selected
|
|
346
|
+
? "border-emerald-200 bg-emerald-100 text-emerald-700"
|
|
347
|
+
: "border-border bg-background text-muted-foreground hover:bg-muted/50 hover:text-foreground"
|
|
348
|
+
}`}
|
|
349
|
+
>
|
|
350
|
+
{reason}
|
|
351
|
+
</button>
|
|
352
|
+
)
|
|
353
|
+
})}
|
|
354
|
+
</div>
|
|
355
|
+
|
|
356
|
+
<input
|
|
357
|
+
type="text"
|
|
358
|
+
value={detailText}
|
|
359
|
+
onChange={(e) => setDetailText(e.target.value)}
|
|
360
|
+
onKeyDown={(e) => {
|
|
361
|
+
if (e.key === "Enter" && canSubmitApprove) handleApproveSubmit()
|
|
362
|
+
}}
|
|
363
|
+
placeholder="Tell us more (optional)"
|
|
364
|
+
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"
|
|
365
|
+
/>
|
|
366
|
+
|
|
367
|
+
<div className="flex items-center gap-2">
|
|
368
|
+
<button
|
|
369
|
+
type="button"
|
|
370
|
+
onClick={handleApproveSubmit}
|
|
371
|
+
disabled={!canSubmitApprove}
|
|
372
|
+
className={`inline-flex h-7 items-center gap-1.5 rounded-md px-3 text-xs font-semibold transition-colors ${
|
|
373
|
+
canSubmitApprove
|
|
374
|
+
? "bg-foreground text-background hover:bg-foreground/90"
|
|
375
|
+
: "cursor-not-allowed bg-muted text-muted-foreground"
|
|
376
|
+
}`}
|
|
377
|
+
>
|
|
378
|
+
Submit
|
|
379
|
+
</button>
|
|
380
|
+
<button
|
|
381
|
+
type="button"
|
|
382
|
+
onClick={skipApproveFeedback}
|
|
383
|
+
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"
|
|
384
|
+
>
|
|
385
|
+
Skip
|
|
386
|
+
</button>
|
|
387
|
+
</div>
|
|
388
|
+
</div>
|
|
389
|
+
)
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
if (approvalState === "dismissed") {
|
|
393
|
+
if (isEditing) {
|
|
394
|
+
return (
|
|
395
|
+
<div className="space-y-3">
|
|
396
|
+
<div className="flex items-center gap-1.5 text-xs text-muted-foreground mb-2">
|
|
397
|
+
<ThumbsDown className="h-3.5 w-3.5" />
|
|
398
|
+
<span>{labels.dismissedStatus}</span>
|
|
399
|
+
</div>
|
|
400
|
+
<p className="text-xs font-medium text-muted-foreground">Edit your feedback</p>
|
|
401
|
+
<div className="flex flex-wrap gap-1.5">
|
|
402
|
+
{dismissReasons.map((reason) => {
|
|
403
|
+
const selected = selectedReasons.includes(reason)
|
|
404
|
+
return (
|
|
405
|
+
<button key={reason} type="button" onClick={() => toggleReason(reason)}
|
|
406
|
+
className={`rounded-full border px-2.5 py-1 text-[11px] font-medium transition-colors ${
|
|
407
|
+
selected ? "border-red-200 bg-red-100 text-red-700" : "border-border bg-background text-muted-foreground hover:bg-muted/50 hover:text-foreground"
|
|
408
|
+
}`}>{reason}</button>
|
|
409
|
+
)
|
|
410
|
+
})}
|
|
411
|
+
</div>
|
|
412
|
+
<input type="text" value={detailText} onChange={(e) => setDetailText(e.target.value)}
|
|
413
|
+
onKeyDown={(e) => { if (e.key === "Enter" && canSubmitDismiss) handleDismissSubmit() }}
|
|
414
|
+
placeholder={otherSelected ? "Please describe (required)" : "Provide additional feedback..."}
|
|
415
|
+
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" />
|
|
416
|
+
<div className="flex items-center gap-2">
|
|
417
|
+
<button type="button" onClick={handleDismissSubmit} disabled={!canSubmitDismiss}
|
|
418
|
+
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"}`}>
|
|
419
|
+
Save
|
|
420
|
+
</button>
|
|
421
|
+
<button type="button" onClick={handleEditCancel}
|
|
422
|
+
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">
|
|
423
|
+
Cancel
|
|
424
|
+
</button>
|
|
425
|
+
</div>
|
|
426
|
+
</div>
|
|
427
|
+
)
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
return (
|
|
431
|
+
<div className="space-y-2">
|
|
432
|
+
<div className="flex items-center gap-1.5 text-xs text-muted-foreground">
|
|
433
|
+
<ThumbsDown className="h-3.5 w-3.5" />
|
|
434
|
+
<span>Signal Dismissed</span>
|
|
435
|
+
</div>
|
|
436
|
+
{submittedFeedback && (
|
|
437
|
+
<SubmittedFeedback
|
|
438
|
+
reasons={submittedFeedback.reasons}
|
|
439
|
+
detail={submittedFeedback.detail}
|
|
440
|
+
variant="dismiss"
|
|
441
|
+
onEdit={startEditing}
|
|
442
|
+
/>
|
|
443
|
+
)}
|
|
444
|
+
</div>
|
|
445
|
+
)
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
if (approvalState === "confirming") {
|
|
449
|
+
return (
|
|
450
|
+
<div className="space-y-3">
|
|
451
|
+
<div className="rounded-md border border-border bg-muted/30 p-3">
|
|
452
|
+
<p className="text-sm text-foreground">
|
|
453
|
+
{labels.confirmPrompt} <strong>{companyName}</strong>. Confirm?
|
|
454
|
+
</p>
|
|
455
|
+
</div>
|
|
456
|
+
<div className="flex items-center gap-2">
|
|
457
|
+
<button
|
|
458
|
+
type="button"
|
|
459
|
+
onClick={approve}
|
|
460
|
+
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"
|
|
461
|
+
>
|
|
462
|
+
<Check className="h-3 w-3" />
|
|
463
|
+
Confirm
|
|
464
|
+
</button>
|
|
465
|
+
<button
|
|
466
|
+
type="button"
|
|
467
|
+
onClick={handleCancel}
|
|
468
|
+
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"
|
|
469
|
+
>
|
|
470
|
+
Cancel
|
|
471
|
+
</button>
|
|
472
|
+
</div>
|
|
473
|
+
</div>
|
|
474
|
+
)
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
if (approvalState === "dismissing") {
|
|
478
|
+
return (
|
|
479
|
+
<div className="space-y-3">
|
|
480
|
+
<p className="text-xs font-medium text-muted-foreground">{labels.dismissPrompt}</p>
|
|
481
|
+
<div className="flex flex-wrap gap-1.5">
|
|
482
|
+
{dismissReasons.map((reason) => {
|
|
483
|
+
const selected = selectedReasons.includes(reason)
|
|
484
|
+
return (
|
|
485
|
+
<button
|
|
486
|
+
key={reason}
|
|
487
|
+
type="button"
|
|
488
|
+
onClick={() => toggleReason(reason)}
|
|
489
|
+
className={`rounded-full border px-2.5 py-1 text-[11px] font-medium transition-colors ${
|
|
490
|
+
selected
|
|
491
|
+
? "border-red-200 bg-red-100 text-red-700"
|
|
492
|
+
: "border-border bg-background text-muted-foreground hover:bg-muted/50 hover:text-foreground"
|
|
493
|
+
}`}
|
|
494
|
+
>
|
|
495
|
+
{reason}
|
|
496
|
+
</button>
|
|
497
|
+
)
|
|
498
|
+
})}
|
|
499
|
+
</div>
|
|
500
|
+
|
|
501
|
+
{(selectedReasons.length > 0 || otherSelected) && (
|
|
502
|
+
<input
|
|
503
|
+
type="text"
|
|
504
|
+
value={detailText}
|
|
505
|
+
onChange={(e) => setDetailText(e.target.value)}
|
|
506
|
+
onKeyDown={(e) => {
|
|
507
|
+
if (e.key === "Enter" && canSubmitDismiss) handleDismissSubmit()
|
|
508
|
+
}}
|
|
509
|
+
placeholder={otherSelected ? "Please describe (required)" : "Provide additional feedback..."}
|
|
510
|
+
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"
|
|
511
|
+
/>
|
|
512
|
+
)}
|
|
513
|
+
|
|
514
|
+
<div className="flex items-center gap-2">
|
|
515
|
+
<button
|
|
516
|
+
type="button"
|
|
517
|
+
onClick={handleDismissSubmit}
|
|
518
|
+
disabled={!canSubmitDismiss}
|
|
519
|
+
className={`inline-flex h-7 items-center gap-1.5 rounded-md px-3 text-xs font-semibold transition-colors ${
|
|
520
|
+
canSubmitDismiss
|
|
521
|
+
? "bg-foreground text-background hover:bg-foreground/90"
|
|
522
|
+
: "cursor-not-allowed bg-muted text-muted-foreground"
|
|
523
|
+
}`}
|
|
524
|
+
>
|
|
525
|
+
Submit
|
|
526
|
+
</button>
|
|
527
|
+
<button
|
|
528
|
+
type="button"
|
|
529
|
+
onClick={handleCancel}
|
|
530
|
+
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"
|
|
531
|
+
>
|
|
532
|
+
Cancel
|
|
533
|
+
</button>
|
|
534
|
+
</div>
|
|
535
|
+
</div>
|
|
536
|
+
)
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
return (
|
|
540
|
+
<div className="flex items-center gap-2">
|
|
541
|
+
<button
|
|
542
|
+
type="button"
|
|
543
|
+
onClick={requestApproval}
|
|
544
|
+
className="inline-flex h-7 items-center gap-1.5 rounded-md border border-border bg-foreground px-3 text-xs font-semibold text-background shadow-none transition-colors hover:bg-foreground/90"
|
|
545
|
+
>
|
|
546
|
+
<CirclePlus className="h-3.5 w-3.5" />
|
|
547
|
+
{labels.approveButton}
|
|
548
|
+
</button>
|
|
549
|
+
<button
|
|
550
|
+
type="button"
|
|
551
|
+
onClick={requestDismiss}
|
|
552
|
+
className="inline-flex h-7 items-center gap-1.5 rounded-md border border-border px-3 text-xs font-medium text-muted-foreground shadow-none transition-colors hover:bg-muted hover:text-foreground"
|
|
553
|
+
>
|
|
554
|
+
<ThumbsDown className="h-3.5 w-3.5" />
|
|
555
|
+
{labels.dismissButton}
|
|
556
|
+
</button>
|
|
557
|
+
</div>
|
|
558
|
+
)
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
function Gate({ children }: { children: React.ReactNode }) {
|
|
562
|
+
const { approvalState } = useSignalApproval()
|
|
563
|
+
const isLocked =
|
|
564
|
+
approvalState === "pending" || approvalState === "confirming" || approvalState === "dismissing"
|
|
565
|
+
|
|
566
|
+
return (
|
|
567
|
+
<div className="relative">
|
|
568
|
+
{isLocked && (
|
|
569
|
+
<div className="pointer-events-none absolute inset-x-0 top-4 z-10 flex justify-center">
|
|
570
|
+
<div className="flex items-center gap-1.5 rounded-full border border-border bg-background px-3 py-1.5 text-xs text-muted-foreground shadow-sm">
|
|
571
|
+
<Lock className="h-3 w-3" />
|
|
572
|
+
Approve or dismiss the signal above to unlock
|
|
573
|
+
</div>
|
|
574
|
+
</div>
|
|
575
|
+
)}
|
|
576
|
+
<div
|
|
577
|
+
className={`transition-opacity duration-300 ${isLocked ? "pointer-events-none select-none opacity-40" : "opacity-100"}`}
|
|
578
|
+
>
|
|
579
|
+
{children}
|
|
580
|
+
</div>
|
|
581
|
+
</div>
|
|
582
|
+
)
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
export {
|
|
586
|
+
Root as SignalApprovalRoot,
|
|
587
|
+
Actions as SignalApprovalActions,
|
|
588
|
+
Gate as SignalApprovalGate,
|
|
589
|
+
}
|
|
590
|
+
export const SignalApproval = { Root, Actions, Gate }
|
|
591
|
+
export type { ApprovalState, SignalApprovalLabels, SignalApprovalContextValue, RootProps as SignalApprovalRootProps }
|