@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,258 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import {
|
|
5
|
+
CheckSquare,
|
|
6
|
+
Clock,
|
|
7
|
+
DollarSign,
|
|
8
|
+
HelpCircle,
|
|
9
|
+
MessageSquare,
|
|
10
|
+
SkipForward,
|
|
11
|
+
ThumbsDown,
|
|
12
|
+
ThumbsUp,
|
|
13
|
+
Users,
|
|
14
|
+
X,
|
|
15
|
+
} from "lucide-react"
|
|
16
|
+
import { Badge } from "./badge"
|
|
17
|
+
import { Button } from "./button"
|
|
18
|
+
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./tooltip"
|
|
19
|
+
|
|
20
|
+
export interface RecommendedAction {
|
|
21
|
+
id: string
|
|
22
|
+
title: string
|
|
23
|
+
reason: string
|
|
24
|
+
category?: "Churn" | "Expand" | "Nurture" | string
|
|
25
|
+
priority?: "High" | "Medium" | "Low" | string
|
|
26
|
+
dueDate?: string
|
|
27
|
+
confidence?: number
|
|
28
|
+
signals?: string[]
|
|
29
|
+
revenueImpact?: number
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface RecommendedActionsSectionProps {
|
|
33
|
+
actions: RecommendedAction[]
|
|
34
|
+
title?: string
|
|
35
|
+
onQueueAction?: (action: RecommendedAction) => void
|
|
36
|
+
onDismissAction?: (action: RecommendedAction) => void
|
|
37
|
+
onFeedback?: (actionId: string, feedback: "useful" | "not_useful", comment: string) => void
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function getCategoryBadgeClass(category?: string) {
|
|
41
|
+
if (category === "Churn") return "bg-white text-black border-red-300"
|
|
42
|
+
if (category === "Expand") return "bg-green-100 text-green-800 border-green-200"
|
|
43
|
+
if (category === "Nurture") return "bg-blue-100 text-blue-800 border-blue-200"
|
|
44
|
+
return "bg-muted text-muted-foreground border-border"
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function getPriorityBadgeClass(priority?: string) {
|
|
48
|
+
if (priority === "High") return "border-foreground text-foreground"
|
|
49
|
+
if (priority === "Medium") return "border-muted-foreground text-muted-foreground"
|
|
50
|
+
return "border-border text-muted-foreground"
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function RecommendedActionsSection({
|
|
54
|
+
actions,
|
|
55
|
+
title = "Recommended Actions",
|
|
56
|
+
onQueueAction,
|
|
57
|
+
onDismissAction,
|
|
58
|
+
onFeedback,
|
|
59
|
+
}: RecommendedActionsSectionProps) {
|
|
60
|
+
const [dismissedActions, setDismissedActions] = React.useState<Set<string>>(new Set())
|
|
61
|
+
const [actionFeedback, setActionFeedback] = React.useState<Record<string, "useful" | "not_useful" | null>>({})
|
|
62
|
+
const [feedbackComment, setFeedbackComment] = React.useState<Record<string, string>>({})
|
|
63
|
+
const [commentOpen, setCommentOpen] = React.useState<Record<string, boolean>>({})
|
|
64
|
+
|
|
65
|
+
const visibleActions = actions.filter((action) => !dismissedActions.has(action.id))
|
|
66
|
+
|
|
67
|
+
const handleDismissAction = (action: RecommendedAction) => {
|
|
68
|
+
setDismissedActions((previous) => new Set([...previous, action.id]))
|
|
69
|
+
onDismissAction?.(action)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const handleFeedback = (actionId: string, feedback: "useful" | "not_useful") => {
|
|
73
|
+
setActionFeedback((previous) => ({ ...previous, [actionId]: feedback }))
|
|
74
|
+
setCommentOpen((previous) => ({ ...previous, [actionId]: true }))
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const submitFeedback = (actionId: string) => {
|
|
78
|
+
const feedback = actionFeedback[actionId]
|
|
79
|
+
if (!feedback) return
|
|
80
|
+
|
|
81
|
+
onFeedback?.(actionId, feedback, feedbackComment[actionId]?.trim() ?? "")
|
|
82
|
+
setCommentOpen((previous) => ({ ...previous, [actionId]: false }))
|
|
83
|
+
setFeedbackComment((previous) => ({ ...previous, [actionId]: "" }))
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<section className="border-t border-border py-6">
|
|
88
|
+
<div className="mb-4 flex items-center justify-between">
|
|
89
|
+
<h3 className="text-[11px] font-bold uppercase tracking-wider text-muted-foreground/70">{title}</h3>
|
|
90
|
+
<span className="text-xs text-muted-foreground">{visibleActions.length} actions</span>
|
|
91
|
+
</div>
|
|
92
|
+
|
|
93
|
+
{visibleActions.length === 0 ? (
|
|
94
|
+
<div className="rounded-md border border-dashed border-border p-4 text-sm text-muted-foreground">
|
|
95
|
+
All recommended actions are dismissed.
|
|
96
|
+
</div>
|
|
97
|
+
) : (
|
|
98
|
+
<div className="space-y-3">
|
|
99
|
+
{visibleActions.map((action) => (
|
|
100
|
+
<div
|
|
101
|
+
key={action.id}
|
|
102
|
+
className="group rounded-md border border-border bg-background p-4 transition-colors hover:bg-muted/20"
|
|
103
|
+
>
|
|
104
|
+
<div className="mb-3 flex items-start justify-between gap-3">
|
|
105
|
+
<div className="min-w-0 flex-1 space-y-2">
|
|
106
|
+
<h4 className="text-sm font-medium leading-snug text-foreground">{action.title}</h4>
|
|
107
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
108
|
+
<Badge variant="outline" className={`px-2.5 py-1 text-xs font-medium ${getCategoryBadgeClass(action.category)}`}>
|
|
109
|
+
{action.category ?? "General"}
|
|
110
|
+
</Badge>
|
|
111
|
+
<TooltipProvider delayDuration={200}>
|
|
112
|
+
<Tooltip>
|
|
113
|
+
<TooltipTrigger asChild>
|
|
114
|
+
<Badge variant="outline" className={`cursor-help px-2.5 py-1 text-xs ${getPriorityBadgeClass(action.priority)}`}>
|
|
115
|
+
<DollarSign className="mr-1 h-3 w-3" />
|
|
116
|
+
{action.priority ?? "Medium"}
|
|
117
|
+
<HelpCircle className="ml-1 h-3 w-3" />
|
|
118
|
+
</Badge>
|
|
119
|
+
</TooltipTrigger>
|
|
120
|
+
<TooltipContent className="max-w-xs">
|
|
121
|
+
<p className="text-xs leading-relaxed">
|
|
122
|
+
{action.confidence
|
|
123
|
+
? `${Math.round(action.confidence * 100)}% confidence based on current account signals.`
|
|
124
|
+
: "Priority reflects expected impact and urgency."}
|
|
125
|
+
</p>
|
|
126
|
+
</TooltipContent>
|
|
127
|
+
</Tooltip>
|
|
128
|
+
</TooltipProvider>
|
|
129
|
+
{typeof action.revenueImpact === "number" && (
|
|
130
|
+
<span className="text-xs font-medium text-muted-foreground">
|
|
131
|
+
+${action.revenueImpact.toLocaleString()}/mo impact
|
|
132
|
+
</span>
|
|
133
|
+
)}
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
|
|
137
|
+
<div className="flex items-center gap-2">
|
|
138
|
+
<Button
|
|
139
|
+
variant="ghost"
|
|
140
|
+
size="icon"
|
|
141
|
+
className="h-8 w-8 bg-foreground text-background hover:bg-foreground/90"
|
|
142
|
+
onClick={() => onQueueAction?.(action)}
|
|
143
|
+
>
|
|
144
|
+
<CheckSquare className="h-4 w-4" />
|
|
145
|
+
</Button>
|
|
146
|
+
<Button
|
|
147
|
+
variant="ghost"
|
|
148
|
+
size="icon"
|
|
149
|
+
className="h-8 w-8 text-muted-foreground hover:bg-muted hover:text-foreground"
|
|
150
|
+
onClick={() => handleDismissAction(action)}
|
|
151
|
+
>
|
|
152
|
+
<X className="h-4 w-4" />
|
|
153
|
+
</Button>
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
|
|
157
|
+
<p className="mb-3 text-sm leading-relaxed text-muted-foreground">{action.reason}</p>
|
|
158
|
+
|
|
159
|
+
{action.signals && action.signals.length > 0 && (
|
|
160
|
+
<div className="mb-3 flex flex-wrap gap-1.5">
|
|
161
|
+
{action.signals.map((signal) => (
|
|
162
|
+
<Badge key={`${action.id}-${signal}`} variant="secondary" className="text-[10px]">
|
|
163
|
+
{signal}
|
|
164
|
+
</Badge>
|
|
165
|
+
))}
|
|
166
|
+
</div>
|
|
167
|
+
)}
|
|
168
|
+
|
|
169
|
+
<div className="flex items-center justify-between">
|
|
170
|
+
<span className="text-xs text-muted-foreground">{action.dueDate ?? "This week"}</span>
|
|
171
|
+
|
|
172
|
+
<div className="flex items-center gap-1.5">
|
|
173
|
+
<TooltipProvider delayDuration={200}>
|
|
174
|
+
<Tooltip>
|
|
175
|
+
<TooltipTrigger asChild>
|
|
176
|
+
<Button variant="ghost" size="icon" className="h-6 w-6">
|
|
177
|
+
<SkipForward className="h-3.5 w-3.5" />
|
|
178
|
+
</Button>
|
|
179
|
+
</TooltipTrigger>
|
|
180
|
+
<TooltipContent>Skip</TooltipContent>
|
|
181
|
+
</Tooltip>
|
|
182
|
+
<Tooltip>
|
|
183
|
+
<TooltipTrigger asChild>
|
|
184
|
+
<Button variant="ghost" size="icon" className="h-6 w-6">
|
|
185
|
+
<Clock className="h-3.5 w-3.5" />
|
|
186
|
+
</Button>
|
|
187
|
+
</TooltipTrigger>
|
|
188
|
+
<TooltipContent>Snooze</TooltipContent>
|
|
189
|
+
</Tooltip>
|
|
190
|
+
<Tooltip>
|
|
191
|
+
<TooltipTrigger asChild>
|
|
192
|
+
<Button variant="ghost" size="icon" className="h-6 w-6">
|
|
193
|
+
<Users className="h-3.5 w-3.5" />
|
|
194
|
+
</Button>
|
|
195
|
+
</TooltipTrigger>
|
|
196
|
+
<TooltipContent>Delegate</TooltipContent>
|
|
197
|
+
</Tooltip>
|
|
198
|
+
<Tooltip>
|
|
199
|
+
<TooltipTrigger asChild>
|
|
200
|
+
<Button variant="ghost" size="icon" className="h-6 w-6">
|
|
201
|
+
<MessageSquare className="h-3.5 w-3.5" />
|
|
202
|
+
</Button>
|
|
203
|
+
</TooltipTrigger>
|
|
204
|
+
<TooltipContent>Add comment</TooltipContent>
|
|
205
|
+
</Tooltip>
|
|
206
|
+
</TooltipProvider>
|
|
207
|
+
</div>
|
|
208
|
+
</div>
|
|
209
|
+
|
|
210
|
+
<div className="mt-3 border-t border-border/50 pt-3">
|
|
211
|
+
<div className="flex items-center gap-2">
|
|
212
|
+
<Button
|
|
213
|
+
variant="outline"
|
|
214
|
+
size="sm"
|
|
215
|
+
className={`h-7 text-xs ${actionFeedback[action.id] === "useful" ? "border-emerald-200 bg-emerald-50 text-emerald-700" : ""}`}
|
|
216
|
+
onClick={() => handleFeedback(action.id, "useful")}
|
|
217
|
+
>
|
|
218
|
+
<ThumbsUp className="mr-1.5 h-3.5 w-3.5" />
|
|
219
|
+
Helpful
|
|
220
|
+
</Button>
|
|
221
|
+
<Button
|
|
222
|
+
variant="outline"
|
|
223
|
+
size="sm"
|
|
224
|
+
className={`h-7 text-xs ${actionFeedback[action.id] === "not_useful" ? "border-red-200 bg-red-50 text-red-700" : ""}`}
|
|
225
|
+
onClick={() => handleFeedback(action.id, "not_useful")}
|
|
226
|
+
>
|
|
227
|
+
<ThumbsDown className="mr-1.5 h-3.5 w-3.5" />
|
|
228
|
+
Not helpful
|
|
229
|
+
</Button>
|
|
230
|
+
</div>
|
|
231
|
+
|
|
232
|
+
{commentOpen[action.id] && (
|
|
233
|
+
<div className="mt-2 flex gap-2">
|
|
234
|
+
<input
|
|
235
|
+
type="text"
|
|
236
|
+
value={feedbackComment[action.id] ?? ""}
|
|
237
|
+
onChange={(event) =>
|
|
238
|
+
setFeedbackComment((previous) => ({
|
|
239
|
+
...previous,
|
|
240
|
+
[action.id]: event.target.value,
|
|
241
|
+
}))
|
|
242
|
+
}
|
|
243
|
+
placeholder="Optional context for this feedback"
|
|
244
|
+
className="h-8 flex-1 rounded-md border border-border bg-background px-2.5 text-xs text-foreground placeholder:text-muted-foreground/60 focus:outline-none focus:ring-1 focus:ring-ring"
|
|
245
|
+
/>
|
|
246
|
+
<Button size="sm" className="h-8 text-xs" onClick={() => submitFeedback(action.id)}>
|
|
247
|
+
Submit
|
|
248
|
+
</Button>
|
|
249
|
+
</div>
|
|
250
|
+
)}
|
|
251
|
+
</div>
|
|
252
|
+
</div>
|
|
253
|
+
))}
|
|
254
|
+
</div>
|
|
255
|
+
)}
|
|
256
|
+
</section>
|
|
257
|
+
)
|
|
258
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
|
|
5
|
+
import { cn } from "../lib/utils"
|
|
6
|
+
|
|
7
|
+
interface ReportCardFilterOption {
|
|
8
|
+
label: string
|
|
9
|
+
value: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface ReportCardProps {
|
|
13
|
+
title: string
|
|
14
|
+
subtitle?: string
|
|
15
|
+
/** Filter pill group rendered top-right */
|
|
16
|
+
filterOptions?: ReportCardFilterOption[]
|
|
17
|
+
selectedFilter?: string
|
|
18
|
+
onFilterChange?: (value: string) => void
|
|
19
|
+
/** Secondary toggle (e.g. metric selector) */
|
|
20
|
+
toggleOptions?: string[]
|
|
21
|
+
selectedToggle?: string
|
|
22
|
+
onToggleChange?: (value: string) => void
|
|
23
|
+
/** Render slot between header and content */
|
|
24
|
+
headerExtra?: React.ReactNode
|
|
25
|
+
children: React.ReactNode
|
|
26
|
+
className?: string
|
|
27
|
+
contentClassName?: string
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function ReportCard({
|
|
31
|
+
title,
|
|
32
|
+
subtitle,
|
|
33
|
+
filterOptions,
|
|
34
|
+
selectedFilter,
|
|
35
|
+
onFilterChange,
|
|
36
|
+
toggleOptions,
|
|
37
|
+
selectedToggle,
|
|
38
|
+
onToggleChange,
|
|
39
|
+
headerExtra,
|
|
40
|
+
children,
|
|
41
|
+
className,
|
|
42
|
+
contentClassName,
|
|
43
|
+
}: ReportCardProps) {
|
|
44
|
+
return (
|
|
45
|
+
<div
|
|
46
|
+
className={cn(
|
|
47
|
+
"rounded-xl border border-border bg-card shadow-sm",
|
|
48
|
+
className,
|
|
49
|
+
)}
|
|
50
|
+
>
|
|
51
|
+
<div className="flex flex-col gap-3 border-b border-border/60 px-6 py-5 sm:flex-row sm:items-center sm:justify-between">
|
|
52
|
+
<div className="min-w-0">
|
|
53
|
+
<h3 className="text-sm font-bold text-foreground">{title}</h3>
|
|
54
|
+
{subtitle && (
|
|
55
|
+
<p className="mt-0.5 text-xs text-muted-foreground">{subtitle}</p>
|
|
56
|
+
)}
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
<div className="flex items-center gap-3">
|
|
60
|
+
{toggleOptions && toggleOptions.length > 1 && (
|
|
61
|
+
<div className="flex items-center gap-1 rounded-lg bg-muted p-1">
|
|
62
|
+
{toggleOptions.map((opt) => (
|
|
63
|
+
<button
|
|
64
|
+
key={opt}
|
|
65
|
+
onClick={() => onToggleChange?.(opt)}
|
|
66
|
+
className={cn(
|
|
67
|
+
"rounded-md px-3 py-1 text-xs font-medium transition-all",
|
|
68
|
+
selectedToggle === opt
|
|
69
|
+
? "bg-background text-foreground shadow-sm"
|
|
70
|
+
: "text-muted-foreground hover:text-foreground",
|
|
71
|
+
)}
|
|
72
|
+
>
|
|
73
|
+
{opt}
|
|
74
|
+
</button>
|
|
75
|
+
))}
|
|
76
|
+
</div>
|
|
77
|
+
)}
|
|
78
|
+
|
|
79
|
+
{filterOptions && filterOptions.length > 1 && (
|
|
80
|
+
<div className="flex items-center gap-1 rounded-lg bg-muted p-1">
|
|
81
|
+
{filterOptions.map((opt) => (
|
|
82
|
+
<button
|
|
83
|
+
key={opt.value}
|
|
84
|
+
onClick={() => onFilterChange?.(opt.value)}
|
|
85
|
+
className={cn(
|
|
86
|
+
"h-7 rounded-md border-none bg-transparent px-3 text-xs font-medium shadow-none transition-all hover:bg-white",
|
|
87
|
+
selectedFilter === opt.value &&
|
|
88
|
+
"bg-background text-foreground shadow-sm",
|
|
89
|
+
)}
|
|
90
|
+
>
|
|
91
|
+
{opt.label}
|
|
92
|
+
</button>
|
|
93
|
+
))}
|
|
94
|
+
</div>
|
|
95
|
+
)}
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
{headerExtra && (
|
|
100
|
+
<div className="border-b border-border/40 px-6 py-3">{headerExtra}</div>
|
|
101
|
+
)}
|
|
102
|
+
|
|
103
|
+
<div className={cn("p-6", contentClassName)}>{children}</div>
|
|
104
|
+
</div>
|
|
105
|
+
)
|
|
106
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { Sheet, SheetContent, SheetHeader, SheetTitle } from "./sheet"
|
|
5
|
+
import { Badge } from "./badge"
|
|
6
|
+
import { ScoreRing } from "./score-ring"
|
|
7
|
+
import { ScoreBreakdown, type ScoreFactor } from "./score-breakdown"
|
|
8
|
+
import { SignalApproval } from "./signal-feedback-inline"
|
|
9
|
+
import { X } from "lucide-react"
|
|
10
|
+
|
|
11
|
+
interface ScoreAnalysisModalProps {
|
|
12
|
+
open: boolean
|
|
13
|
+
onOpenChange: (open: boolean) => void
|
|
14
|
+
title: string
|
|
15
|
+
description: string
|
|
16
|
+
score: number
|
|
17
|
+
denominator?: number
|
|
18
|
+
whyNow: string
|
|
19
|
+
evidence: React.ReactNode[]
|
|
20
|
+
factors?: ScoreFactor[]
|
|
21
|
+
onFactorFeedback?: (factorKey: string, type: "up" | "down" | null, detail?: string) => void
|
|
22
|
+
companyName?: string
|
|
23
|
+
opportunityUrl?: string
|
|
24
|
+
onApprove?: () => void
|
|
25
|
+
onApproveFeedback?: (reasons: string[], detail: string) => void
|
|
26
|
+
onDismiss?: (reasons: string[], detail: string) => void
|
|
27
|
+
/** When true, renders as an absolute-positioned inline panel instead of a Radix Sheet portal. Useful when the component is inside a container that should not be escaped. */
|
|
28
|
+
useInlinePanel?: boolean
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function getScoreLabel(score: number, denominator: number) {
|
|
32
|
+
const pct = (score / denominator) * 100
|
|
33
|
+
if (pct >= 70) return { text: "HIGH", className: "text-emerald-600" }
|
|
34
|
+
if (pct >= 40) return { text: "MEDIUM", className: "text-amber-600" }
|
|
35
|
+
return { text: "LOW", className: "text-red-600" }
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function ScoreAnalysisModal({
|
|
39
|
+
open,
|
|
40
|
+
onOpenChange,
|
|
41
|
+
title,
|
|
42
|
+
description,
|
|
43
|
+
score,
|
|
44
|
+
denominator = 100,
|
|
45
|
+
whyNow,
|
|
46
|
+
evidence,
|
|
47
|
+
factors,
|
|
48
|
+
onFactorFeedback,
|
|
49
|
+
companyName = "Account",
|
|
50
|
+
opportunityUrl,
|
|
51
|
+
onApprove,
|
|
52
|
+
onApproveFeedback,
|
|
53
|
+
onDismiss,
|
|
54
|
+
useInlinePanel = false,
|
|
55
|
+
}: ScoreAnalysisModalProps) {
|
|
56
|
+
const label = getScoreLabel(score, denominator)
|
|
57
|
+
|
|
58
|
+
const panelContent = (
|
|
59
|
+
<SignalApproval.Root
|
|
60
|
+
companyName={companyName}
|
|
61
|
+
opportunityUrl={opportunityUrl}
|
|
62
|
+
onApprove={onApprove}
|
|
63
|
+
onApproveFeedback={onApproveFeedback}
|
|
64
|
+
onDismiss={onDismiss}
|
|
65
|
+
>
|
|
66
|
+
<div className="flex items-center justify-between px-6 py-4 border-b border-border shrink-0">
|
|
67
|
+
<h2 className="text-lg font-semibold text-foreground">{title}</h2>
|
|
68
|
+
<button
|
|
69
|
+
type="button"
|
|
70
|
+
onClick={() => onOpenChange(false)}
|
|
71
|
+
className="p-1.5 rounded-md text-muted-foreground hover:bg-secondary transition-colors"
|
|
72
|
+
>
|
|
73
|
+
<X className="w-4 h-4" />
|
|
74
|
+
</button>
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
<div className="flex-1 overflow-y-auto px-6 py-6">
|
|
78
|
+
<p className="text-sm text-muted-foreground mb-6">{description}</p>
|
|
79
|
+
|
|
80
|
+
<div className="mb-8">
|
|
81
|
+
<SignalApproval.Actions />
|
|
82
|
+
</div>
|
|
83
|
+
|
|
84
|
+
<div className="space-y-6">
|
|
85
|
+
<div className="flex flex-col items-center gap-3">
|
|
86
|
+
<ScoreRing score={score} denominator={denominator} size={120} strokeWidth={10} />
|
|
87
|
+
<Badge variant="outline">
|
|
88
|
+
{Math.round((score / denominator) * 100)}% Score
|
|
89
|
+
{" \u2014 "}
|
|
90
|
+
<span className={label.className}>{label.text}</span>
|
|
91
|
+
</Badge>
|
|
92
|
+
</div>
|
|
93
|
+
|
|
94
|
+
<div>
|
|
95
|
+
<h3 className="font-semibold mb-2 text-sm">Why Now</h3>
|
|
96
|
+
<p className="text-sm text-muted-foreground leading-relaxed">{whyNow}</p>
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
<div>
|
|
100
|
+
<h3 className="font-semibold mb-2 text-sm">Supporting Evidence</h3>
|
|
101
|
+
<ul className="space-y-2">
|
|
102
|
+
{evidence.map((item, index) => (
|
|
103
|
+
<li key={index} className="flex items-start gap-2 text-sm">
|
|
104
|
+
<div className="w-1.5 h-1.5 bg-primary rounded-full mt-2 flex-shrink-0" />
|
|
105
|
+
<span className="text-muted-foreground leading-relaxed">{item}</span>
|
|
106
|
+
</li>
|
|
107
|
+
))}
|
|
108
|
+
</ul>
|
|
109
|
+
</div>
|
|
110
|
+
|
|
111
|
+
{factors && factors.length > 0 && (
|
|
112
|
+
<div>
|
|
113
|
+
<h3 className="font-semibold mb-2 text-sm">Score Breakdown</h3>
|
|
114
|
+
<ScoreBreakdown factors={factors} onFactorFeedback={onFactorFeedback} />
|
|
115
|
+
</div>
|
|
116
|
+
)}
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
</SignalApproval.Root>
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
if (useInlinePanel) {
|
|
123
|
+
if (!open) return null
|
|
124
|
+
return (
|
|
125
|
+
<div style={{ position: "absolute", inset: 0, zIndex: 50, pointerEvents: "auto", overflow: "hidden" }}>
|
|
126
|
+
<div
|
|
127
|
+
onClick={() => onOpenChange(false)}
|
|
128
|
+
style={{ position: "absolute", inset: 0, backgroundColor: "rgba(0,0,0,0.5)", transition: "opacity 200ms" }}
|
|
129
|
+
/>
|
|
130
|
+
<div
|
|
131
|
+
style={{
|
|
132
|
+
position: "absolute",
|
|
133
|
+
top: 0,
|
|
134
|
+
right: 0,
|
|
135
|
+
bottom: 0,
|
|
136
|
+
width: 500,
|
|
137
|
+
maxWidth: "100%",
|
|
138
|
+
zIndex: 50,
|
|
139
|
+
display: "flex",
|
|
140
|
+
flexDirection: "column",
|
|
141
|
+
overflow: "hidden",
|
|
142
|
+
borderLeft: "1px solid var(--border)",
|
|
143
|
+
backgroundColor: "var(--background, #fff)",
|
|
144
|
+
boxShadow: "-4px 0 24px rgba(0,0,0,0.12)",
|
|
145
|
+
}}
|
|
146
|
+
>
|
|
147
|
+
{panelContent}
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return (
|
|
154
|
+
<Sheet open={open} onOpenChange={onOpenChange}>
|
|
155
|
+
<SheetContent
|
|
156
|
+
side="right"
|
|
157
|
+
className="w-full sm:w-[500px] sm:max-w-[600px] overflow-hidden p-0 bg-background border-l border-border flex flex-col"
|
|
158
|
+
showCloseButton={false}
|
|
159
|
+
>
|
|
160
|
+
<SheetHeader className="sr-only p-0">
|
|
161
|
+
<SheetTitle>{title}</SheetTitle>
|
|
162
|
+
</SheetHeader>
|
|
163
|
+
{panelContent}
|
|
164
|
+
</SheetContent>
|
|
165
|
+
</Sheet>
|
|
166
|
+
)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const ScoreAnalysisPanel = ScoreAnalysisModal
|
|
170
|
+
|
|
171
|
+
export { ScoreAnalysisModal, ScoreAnalysisPanel }
|
|
172
|
+
export type { ScoreAnalysisModalProps }
|