@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.
Files changed (336) hide show
  1. package/README.md +14 -4
  2. package/dist/charts/bar-chart-component.d.ts +24 -0
  3. package/dist/charts/bar-chart-component.js +123 -0
  4. package/dist/charts/bar-chart-component.js.map +1 -0
  5. package/dist/charts/chart-tooltip.d.ts +26 -0
  6. package/dist/charts/chart-tooltip.js +69 -0
  7. package/dist/charts/chart-tooltip.js.map +1 -0
  8. package/dist/charts/chart.d.ts +64 -0
  9. package/dist/charts/chart.js +285 -0
  10. package/dist/charts/chart.js.map +1 -0
  11. package/dist/charts/donut-chart.d.ts +21 -0
  12. package/dist/charts/donut-chart.js +96 -0
  13. package/dist/charts/donut-chart.js.map +1 -0
  14. package/dist/charts/index.d.ts +11 -0
  15. package/dist/charts/index.js +10 -0
  16. package/dist/charts/index.js.map +1 -0
  17. package/dist/charts/pipeline-overview.d.ts +76 -0
  18. package/dist/charts/pipeline-overview.js +372 -0
  19. package/dist/charts/pipeline-overview.js.map +1 -0
  20. package/dist/charts/sankey-chart.d.ts +52 -0
  21. package/dist/charts/sankey-chart.js +219 -0
  22. package/dist/charts/sankey-chart.js.map +1 -0
  23. package/dist/charts/top-line-metrics.d.ts +26 -0
  24. package/dist/charts/top-line-metrics.js +224 -0
  25. package/dist/charts/top-line-metrics.js.map +1 -0
  26. package/dist/charts/trend-area-chart.d.ts +21 -0
  27. package/dist/charts/trend-area-chart.js +150 -0
  28. package/dist/charts/trend-area-chart.js.map +1 -0
  29. package/dist/charts/volume-analysis-chart.d.ts +19 -0
  30. package/dist/charts/volume-analysis-chart.js +121 -0
  31. package/dist/charts/volume-analysis-chart.js.map +1 -0
  32. package/dist/components/activity-detail.d.ts +38 -0
  33. package/dist/components/activity-detail.js +163 -0
  34. package/dist/components/activity-detail.js.map +1 -0
  35. package/dist/components/activity-log.d.ts +21 -0
  36. package/dist/components/activity-log.js +61 -0
  37. package/dist/components/activity-log.js.map +1 -0
  38. package/dist/components/agent-popover.d.ts +71 -0
  39. package/dist/components/agent-popover.js +282 -0
  40. package/dist/components/agent-popover.js.map +1 -0
  41. package/dist/components/agent-widget.d.ts +24 -0
  42. package/dist/components/agent-widget.js +117 -0
  43. package/dist/components/agent-widget.js.map +1 -0
  44. package/dist/components/avatar.d.ts +13 -0
  45. package/dist/components/avatar.js +140 -0
  46. package/dist/components/avatar.js.map +1 -0
  47. package/dist/components/badge.d.ts +12 -0
  48. package/dist/components/badge.js +75 -0
  49. package/dist/components/badge.js.map +1 -0
  50. package/dist/components/button.d.ts +13 -0
  51. package/dist/components/button.js +83 -0
  52. package/dist/components/button.js.map +1 -0
  53. package/dist/components/card.d.ts +11 -0
  54. package/dist/components/card.js +119 -0
  55. package/dist/components/card.js.map +1 -0
  56. package/dist/components/contact-list.d.ts +34 -0
  57. package/dist/components/contact-list.js +84 -0
  58. package/dist/components/contact-list.js.map +1 -0
  59. package/dist/components/dashboard-cards.d.ts +10 -0
  60. package/dist/components/dashboard-cards.js +164 -0
  61. package/dist/components/dashboard-cards.js.map +1 -0
  62. package/dist/components/data-table-display.d.ts +19 -0
  63. package/dist/components/data-table-display.js +109 -0
  64. package/dist/components/data-table-display.js.map +1 -0
  65. package/dist/components/data-table-filter.d.ts +18 -0
  66. package/dist/components/data-table-filter.js +107 -0
  67. package/dist/components/data-table-filter.js.map +1 -0
  68. package/dist/components/data-table-quick-views.d.ts +13 -0
  69. package/dist/components/data-table-quick-views.js +90 -0
  70. package/dist/components/data-table-quick-views.js.map +1 -0
  71. package/dist/components/data-table-toolbar.d.ts +18 -0
  72. package/dist/components/data-table-toolbar.js +45 -0
  73. package/dist/components/data-table-toolbar.js.map +1 -0
  74. package/dist/components/data-table.d.ts +39 -0
  75. package/dist/components/data-table.js +821 -0
  76. package/dist/components/data-table.js.map +1 -0
  77. package/dist/components/detail-view.d.ts +44 -0
  78. package/dist/components/detail-view.js +165 -0
  79. package/dist/components/detail-view.js.map +1 -0
  80. package/dist/components/dialog.d.ts +19 -0
  81. package/dist/components/dialog.js +188 -0
  82. package/dist/components/dialog.js.map +1 -0
  83. package/dist/components/dropdown-menu.d.ts +27 -0
  84. package/dist/components/dropdown-menu.js +279 -0
  85. package/dist/components/dropdown-menu.js.map +1 -0
  86. package/dist/components/entity-panel.d.ts +69 -0
  87. package/dist/components/entity-panel.js +584 -0
  88. package/dist/components/entity-panel.js.map +1 -0
  89. package/dist/components/inbox-row.d.ts +27 -0
  90. package/dist/components/inbox-row.js +139 -0
  91. package/dist/components/inbox-row.js.map +1 -0
  92. package/dist/components/inbox-toolbar.d.ts +21 -0
  93. package/dist/components/inbox-toolbar.js +203 -0
  94. package/dist/components/inbox-toolbar.js.map +1 -0
  95. package/dist/components/input.d.ts +5 -0
  96. package/dist/components/input.js +50 -0
  97. package/dist/components/input.js.map +1 -0
  98. package/dist/components/insights-filter-bar.d.ts +21 -0
  99. package/dist/components/insights-filter-bar.js +99 -0
  100. package/dist/components/insights-filter-bar.js.map +1 -0
  101. package/dist/components/item-list-display.d.ts +22 -0
  102. package/dist/components/item-list-display.js +240 -0
  103. package/dist/components/item-list-display.js.map +1 -0
  104. package/dist/components/item-list-filter.d.ts +16 -0
  105. package/dist/components/item-list-filter.js +87 -0
  106. package/dist/components/item-list-filter.js.map +1 -0
  107. package/dist/components/item-list-toolbar.d.ts +25 -0
  108. package/dist/components/item-list-toolbar.js +79 -0
  109. package/dist/components/item-list-toolbar.js.map +1 -0
  110. package/dist/components/item-list.d.ts +20 -0
  111. package/dist/components/item-list.js +702 -0
  112. package/dist/components/item-list.js.map +1 -0
  113. package/dist/components/label.d.ts +6 -0
  114. package/dist/components/label.js +55 -0
  115. package/dist/components/label.js.map +1 -0
  116. package/dist/components/message.d.ts +23 -0
  117. package/dist/components/message.js +117 -0
  118. package/dist/components/message.js.map +1 -0
  119. package/dist/components/metric-card.d.ts +25 -0
  120. package/dist/components/metric-card.js +107 -0
  121. package/dist/components/metric-card.js.map +1 -0
  122. package/dist/components/performance-metrics-table.d.ts +38 -0
  123. package/dist/components/performance-metrics-table.js +342 -0
  124. package/dist/components/performance-metrics-table.js.map +1 -0
  125. package/dist/components/preview-list.d.ts +14 -0
  126. package/dist/components/preview-list.js +83 -0
  127. package/dist/components/preview-list.js.map +1 -0
  128. package/dist/components/progress.d.ts +6 -0
  129. package/dist/components/progress.js +69 -0
  130. package/dist/components/progress.js.map +1 -0
  131. package/dist/components/quick-action-chat-area.d.ts +24 -0
  132. package/dist/components/quick-action-chat-area.js +178 -0
  133. package/dist/components/quick-action-chat-area.js.map +1 -0
  134. package/dist/components/quick-action-modal.d.ts +30 -0
  135. package/dist/components/quick-action-modal.js +288 -0
  136. package/dist/components/quick-action-modal.js.map +1 -0
  137. package/dist/components/quick-action-sidebar-nav.d.ts +51 -0
  138. package/dist/components/quick-action-sidebar-nav.js +528 -0
  139. package/dist/components/quick-action-sidebar-nav.js.map +1 -0
  140. package/dist/components/recommended-actions-section.d.ts +23 -0
  141. package/dist/components/recommended-actions-section.js +215 -0
  142. package/dist/components/recommended-actions-section.js.map +1 -0
  143. package/dist/components/report-card.d.ts +26 -0
  144. package/dist/components/report-card.js +69 -0
  145. package/dist/components/report-card.js.map +1 -0
  146. package/dist/components/score-analysis-modal.d.ts +26 -0
  147. package/dist/components/score-analysis-modal.js +141 -0
  148. package/dist/components/score-analysis-modal.js.map +1 -0
  149. package/dist/components/score-breakdown.d.ts +17 -0
  150. package/dist/components/score-breakdown.js +162 -0
  151. package/dist/components/score-breakdown.js.map +1 -0
  152. package/dist/components/score-feedback.d.ts +40 -0
  153. package/dist/components/score-feedback.js +209 -0
  154. package/dist/components/score-feedback.js.map +1 -0
  155. package/dist/components/score-ring.d.ts +14 -0
  156. package/dist/components/score-ring.js +79 -0
  157. package/dist/components/score-ring.js.map +1 -0
  158. package/dist/components/scroll-area.d.ts +7 -0
  159. package/dist/components/scroll-area.js +101 -0
  160. package/dist/components/scroll-area.js.map +1 -0
  161. package/dist/components/select.d.ts +17 -0
  162. package/dist/components/select.js +228 -0
  163. package/dist/components/select.js.map +1 -0
  164. package/dist/components/separator.d.ts +6 -0
  165. package/dist/components/separator.js +61 -0
  166. package/dist/components/separator.js.map +1 -0
  167. package/dist/components/sheet.d.ts +16 -0
  168. package/dist/components/sheet.js +168 -0
  169. package/dist/components/sheet.js.map +1 -0
  170. package/dist/components/sidebar.d.ts +73 -0
  171. package/dist/components/sidebar.js +723 -0
  172. package/dist/components/sidebar.js.map +1 -0
  173. package/dist/components/signal-feedback-inline.d.ts +51 -0
  174. package/dist/components/signal-feedback-inline.js +548 -0
  175. package/dist/components/signal-feedback-inline.js.map +1 -0
  176. package/dist/components/simple-data-table.d.ts +15 -0
  177. package/dist/components/simple-data-table.js +91 -0
  178. package/dist/components/simple-data-table.js.map +1 -0
  179. package/dist/components/skeleton.d.ts +5 -0
  180. package/dist/components/skeleton.js +44 -0
  181. package/dist/components/skeleton.js.map +1 -0
  182. package/dist/components/status-badge.d.ts +10 -0
  183. package/dist/components/status-badge.js +82 -0
  184. package/dist/components/status-badge.js.map +1 -0
  185. package/dist/components/styled-bar-list.d.ts +20 -0
  186. package/dist/components/styled-bar-list.js +59 -0
  187. package/dist/components/styled-bar-list.js.map +1 -0
  188. package/dist/components/suggested-actions.d.ts +110 -0
  189. package/dist/components/suggested-actions.js +1538 -0
  190. package/dist/components/suggested-actions.js.map +1 -0
  191. package/dist/components/table.d.ts +12 -0
  192. package/dist/components/table.js +147 -0
  193. package/dist/components/table.js.map +1 -0
  194. package/dist/components/tabs.d.ts +14 -0
  195. package/dist/components/tabs.js +129 -0
  196. package/dist/components/tabs.js.map +1 -0
  197. package/dist/components/textarea.d.ts +5 -0
  198. package/dist/components/textarea.js +47 -0
  199. package/dist/components/textarea.js.map +1 -0
  200. package/dist/components/timeline-activity.d.ts +34 -0
  201. package/dist/components/timeline-activity.js +181 -0
  202. package/dist/components/timeline-activity.js.map +1 -0
  203. package/dist/components/tooltip.d.ts +9 -0
  204. package/dist/components/tooltip.js +93 -0
  205. package/dist/components/tooltip.js.map +1 -0
  206. package/dist/components/view-mode-toggle.d.ts +16 -0
  207. package/dist/components/view-mode-toggle.js +24 -0
  208. package/dist/components/view-mode-toggle.js.map +1 -0
  209. package/dist/hooks/use-mobile.d.ts +3 -0
  210. package/dist/hooks/use-mobile.js +21 -0
  211. package/dist/hooks/use-mobile.js.map +1 -0
  212. package/dist/index.d.ts +68 -1878
  213. package/dist/index.js +69 -10918
  214. package/dist/index.js.map +1 -1
  215. package/dist/lib/icons.d.ts +18 -0
  216. package/dist/lib/icons.js +21 -0
  217. package/dist/lib/icons.js.map +1 -0
  218. package/dist/lib/utils.d.ts +5 -0
  219. package/dist/lib/utils.js +9 -0
  220. package/dist/lib/utils.js.map +1 -0
  221. package/dist/prototype/index.d.ts +20 -0
  222. package/dist/prototype/index.js +8 -0
  223. package/dist/prototype/index.js.map +1 -0
  224. package/dist/prototype/prototype-accounts-view.d.ts +22 -0
  225. package/dist/prototype/prototype-accounts-view.js +70 -0
  226. package/dist/prototype/prototype-accounts-view.js.map +1 -0
  227. package/dist/prototype/prototype-admin-view.d.ts +21 -0
  228. package/dist/prototype/prototype-admin-view.js +53 -0
  229. package/dist/prototype/prototype-admin-view.js.map +1 -0
  230. package/dist/prototype/prototype-config.d.ts +226 -0
  231. package/dist/prototype/prototype-config.js +1 -0
  232. package/dist/prototype/prototype-config.js.map +1 -0
  233. package/dist/prototype/prototype-inbox-view.d.ts +48 -0
  234. package/dist/prototype/prototype-inbox-view.js +701 -0
  235. package/dist/prototype/prototype-inbox-view.js.map +1 -0
  236. package/dist/prototype/prototype-insights-view.d.ts +23 -0
  237. package/dist/prototype/prototype-insights-view.js +335 -0
  238. package/dist/prototype/prototype-insights-view.js.map +1 -0
  239. package/dist/prototype/prototype-shell.d.ts +40 -0
  240. package/dist/prototype/prototype-shell.js +190 -0
  241. package/dist/prototype/prototype-shell.js.map +1 -0
  242. package/dist/prototype/prototype-work-queue-view.d.ts +8 -0
  243. package/dist/prototype/prototype-work-queue-view.js +17 -0
  244. package/dist/prototype/prototype-work-queue-view.js.map +1 -0
  245. package/dist/three/agent-orb.d.ts +39 -0
  246. package/dist/three/agent-orb.js +500 -0
  247. package/dist/three/agent-orb.js.map +1 -0
  248. package/dist/three/index.d.ts +2 -0
  249. package/dist/three/index.js +2 -0
  250. package/dist/three/index.js.map +1 -0
  251. package/package.json +98 -17
  252. package/src/charts/bar-chart-component.tsx +150 -0
  253. package/src/charts/chart-tooltip.tsx +86 -0
  254. package/src/charts/chart.tsx +371 -0
  255. package/src/charts/donut-chart.tsx +112 -0
  256. package/src/charts/index.ts +13 -0
  257. package/src/charts/pipeline-overview.tsx +476 -0
  258. package/src/charts/sankey-chart.tsx +290 -0
  259. package/src/charts/top-line-metrics.tsx +261 -0
  260. package/src/charts/trend-area-chart.tsx +150 -0
  261. package/src/charts/volume-analysis-chart.tsx +124 -0
  262. package/src/components/activity-detail.tsx +233 -0
  263. package/src/components/activity-log.tsx +89 -0
  264. package/src/components/agent-popover.tsx +373 -0
  265. package/src/components/agent-widget.tsx +163 -0
  266. package/src/components/avatar.tsx +109 -0
  267. package/src/components/badge.tsx +48 -0
  268. package/src/components/button.tsx +59 -0
  269. package/src/components/card.tsx +92 -0
  270. package/src/components/contact-list.tsx +121 -0
  271. package/src/components/dashboard-cards.tsx +170 -0
  272. package/src/components/data-table-display.tsx +139 -0
  273. package/src/components/data-table-filter.tsx +138 -0
  274. package/src/components/data-table-quick-views.tsx +103 -0
  275. package/src/components/data-table-toolbar.tsx +56 -0
  276. package/src/components/data-table.tsx +915 -0
  277. package/src/components/detail-view.tsx +237 -0
  278. package/src/components/dialog.tsx +158 -0
  279. package/src/components/dropdown-menu.tsx +257 -0
  280. package/src/components/entity-panel.tsx +767 -0
  281. package/src/components/inbox-row.tsx +132 -0
  282. package/src/components/inbox-toolbar.tsx +213 -0
  283. package/src/components/input.tsx +21 -0
  284. package/src/components/insights-filter-bar.tsx +132 -0
  285. package/src/components/item-list-display.tsx +278 -0
  286. package/src/components/item-list-filter.tsx +118 -0
  287. package/src/components/item-list-toolbar.tsx +97 -0
  288. package/src/components/item-list.tsx +843 -0
  289. package/src/components/label.tsx +24 -0
  290. package/src/components/message.tsx +83 -0
  291. package/src/components/metric-card.tsx +178 -0
  292. package/src/components/performance-metrics-table.tsx +442 -0
  293. package/src/components/preview-list.tsx +62 -0
  294. package/src/components/progress.tsx +31 -0
  295. package/src/components/quick-action-chat-area.tsx +156 -0
  296. package/src/components/quick-action-modal.tsx +331 -0
  297. package/src/components/quick-action-sidebar-nav.tsx +592 -0
  298. package/src/components/recommended-actions-section.tsx +258 -0
  299. package/src/components/report-card.tsx +106 -0
  300. package/src/components/score-analysis-modal.tsx +172 -0
  301. package/src/components/score-breakdown.tsx +179 -0
  302. package/src/components/score-feedback.tsx +288 -0
  303. package/src/components/score-ring.tsx +87 -0
  304. package/src/components/scroll-area.tsx +58 -0
  305. package/src/components/select.tsx +190 -0
  306. package/src/components/separator.tsx +28 -0
  307. package/src/components/sheet.tsx +143 -0
  308. package/src/components/sidebar.tsx +726 -0
  309. package/src/components/signal-feedback-inline.tsx +591 -0
  310. package/src/components/simple-data-table.tsx +124 -0
  311. package/src/components/skeleton.tsx +15 -0
  312. package/src/components/status-badge.tsx +63 -0
  313. package/src/components/styled-bar-list.tsx +70 -0
  314. package/src/components/suggested-actions.tsx +1985 -0
  315. package/src/components/table.tsx +116 -0
  316. package/src/components/tabs.tsx +91 -0
  317. package/src/components/textarea.tsx +18 -0
  318. package/src/components/timeline-activity.tsx +234 -0
  319. package/src/components/tooltip.tsx +57 -0
  320. package/src/components/view-mode-toggle.tsx +39 -0
  321. package/src/hooks/use-mobile.ts +21 -0
  322. package/src/index.ts +77 -0
  323. package/src/lib/icons.ts +18 -0
  324. package/src/lib/utils.ts +6 -0
  325. package/src/prototype/index.ts +11 -0
  326. package/src/prototype/prototype-accounts-view.tsx +112 -0
  327. package/src/prototype/prototype-admin-view.tsx +67 -0
  328. package/src/prototype/prototype-config.ts +243 -0
  329. package/src/prototype/prototype-inbox-view.tsx +810 -0
  330. package/src/prototype/prototype-insights-view.tsx +379 -0
  331. package/src/prototype/prototype-shell.tsx +219 -0
  332. package/src/prototype/prototype-work-queue-view.tsx +30 -0
  333. package/src/styles/globals.css +299 -0
  334. package/src/three/agent-orb.tsx +557 -0
  335. package/src/three/index.ts +5 -0
  336. 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 }