@handled-ai/design-system 0.8.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (340) 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/components/virtualized-data-table.d.ts +29 -0
  210. package/dist/components/virtualized-data-table.js +231 -0
  211. package/dist/components/virtualized-data-table.js.map +1 -0
  212. package/dist/hooks/use-mobile.d.ts +3 -0
  213. package/dist/hooks/use-mobile.js +21 -0
  214. package/dist/hooks/use-mobile.js.map +1 -0
  215. package/dist/index.d.ts +69 -1878
  216. package/dist/index.js +70 -10918
  217. package/dist/index.js.map +1 -1
  218. package/dist/lib/icons.d.ts +18 -0
  219. package/dist/lib/icons.js +21 -0
  220. package/dist/lib/icons.js.map +1 -0
  221. package/dist/lib/utils.d.ts +5 -0
  222. package/dist/lib/utils.js +9 -0
  223. package/dist/lib/utils.js.map +1 -0
  224. package/dist/prototype/index.d.ts +20 -0
  225. package/dist/prototype/index.js +8 -0
  226. package/dist/prototype/index.js.map +1 -0
  227. package/dist/prototype/prototype-accounts-view.d.ts +22 -0
  228. package/dist/prototype/prototype-accounts-view.js +70 -0
  229. package/dist/prototype/prototype-accounts-view.js.map +1 -0
  230. package/dist/prototype/prototype-admin-view.d.ts +21 -0
  231. package/dist/prototype/prototype-admin-view.js +53 -0
  232. package/dist/prototype/prototype-admin-view.js.map +1 -0
  233. package/dist/prototype/prototype-config.d.ts +226 -0
  234. package/dist/prototype/prototype-config.js +1 -0
  235. package/dist/prototype/prototype-config.js.map +1 -0
  236. package/dist/prototype/prototype-inbox-view.d.ts +48 -0
  237. package/dist/prototype/prototype-inbox-view.js +701 -0
  238. package/dist/prototype/prototype-inbox-view.js.map +1 -0
  239. package/dist/prototype/prototype-insights-view.d.ts +23 -0
  240. package/dist/prototype/prototype-insights-view.js +335 -0
  241. package/dist/prototype/prototype-insights-view.js.map +1 -0
  242. package/dist/prototype/prototype-shell.d.ts +40 -0
  243. package/dist/prototype/prototype-shell.js +190 -0
  244. package/dist/prototype/prototype-shell.js.map +1 -0
  245. package/dist/prototype/prototype-work-queue-view.d.ts +8 -0
  246. package/dist/prototype/prototype-work-queue-view.js +17 -0
  247. package/dist/prototype/prototype-work-queue-view.js.map +1 -0
  248. package/dist/three/agent-orb.d.ts +39 -0
  249. package/dist/three/agent-orb.js +500 -0
  250. package/dist/three/agent-orb.js.map +1 -0
  251. package/dist/three/index.d.ts +2 -0
  252. package/dist/three/index.js +2 -0
  253. package/dist/three/index.js.map +1 -0
  254. package/package.json +104 -18
  255. package/src/charts/bar-chart-component.tsx +150 -0
  256. package/src/charts/chart-tooltip.tsx +86 -0
  257. package/src/charts/chart.tsx +371 -0
  258. package/src/charts/donut-chart.tsx +112 -0
  259. package/src/charts/index.ts +13 -0
  260. package/src/charts/pipeline-overview.tsx +476 -0
  261. package/src/charts/sankey-chart.tsx +290 -0
  262. package/src/charts/top-line-metrics.tsx +261 -0
  263. package/src/charts/trend-area-chart.tsx +150 -0
  264. package/src/charts/volume-analysis-chart.tsx +124 -0
  265. package/src/components/activity-detail.tsx +233 -0
  266. package/src/components/activity-log.tsx +89 -0
  267. package/src/components/agent-popover.tsx +373 -0
  268. package/src/components/agent-widget.tsx +163 -0
  269. package/src/components/avatar.tsx +109 -0
  270. package/src/components/badge.tsx +48 -0
  271. package/src/components/button.tsx +59 -0
  272. package/src/components/card.tsx +92 -0
  273. package/src/components/contact-list.tsx +121 -0
  274. package/src/components/dashboard-cards.tsx +170 -0
  275. package/src/components/data-table-display.tsx +139 -0
  276. package/src/components/data-table-filter.tsx +138 -0
  277. package/src/components/data-table-quick-views.tsx +103 -0
  278. package/src/components/data-table-toolbar.tsx +56 -0
  279. package/src/components/data-table.tsx +915 -0
  280. package/src/components/detail-view.tsx +237 -0
  281. package/src/components/dialog.tsx +158 -0
  282. package/src/components/dropdown-menu.tsx +257 -0
  283. package/src/components/entity-panel.tsx +767 -0
  284. package/src/components/inbox-row.tsx +132 -0
  285. package/src/components/inbox-toolbar.tsx +213 -0
  286. package/src/components/input.tsx +21 -0
  287. package/src/components/insights-filter-bar.tsx +132 -0
  288. package/src/components/item-list-display.tsx +278 -0
  289. package/src/components/item-list-filter.tsx +118 -0
  290. package/src/components/item-list-toolbar.tsx +97 -0
  291. package/src/components/item-list.tsx +843 -0
  292. package/src/components/label.tsx +24 -0
  293. package/src/components/message.tsx +83 -0
  294. package/src/components/metric-card.tsx +178 -0
  295. package/src/components/performance-metrics-table.tsx +442 -0
  296. package/src/components/preview-list.tsx +62 -0
  297. package/src/components/progress.tsx +31 -0
  298. package/src/components/quick-action-chat-area.tsx +156 -0
  299. package/src/components/quick-action-modal.tsx +331 -0
  300. package/src/components/quick-action-sidebar-nav.tsx +592 -0
  301. package/src/components/recommended-actions-section.tsx +258 -0
  302. package/src/components/report-card.tsx +106 -0
  303. package/src/components/score-analysis-modal.tsx +172 -0
  304. package/src/components/score-breakdown.tsx +179 -0
  305. package/src/components/score-feedback.tsx +288 -0
  306. package/src/components/score-ring.tsx +87 -0
  307. package/src/components/scroll-area.tsx +58 -0
  308. package/src/components/select.tsx +190 -0
  309. package/src/components/separator.tsx +28 -0
  310. package/src/components/sheet.tsx +143 -0
  311. package/src/components/sidebar.tsx +726 -0
  312. package/src/components/signal-feedback-inline.tsx +591 -0
  313. package/src/components/simple-data-table.tsx +124 -0
  314. package/src/components/skeleton.tsx +15 -0
  315. package/src/components/status-badge.tsx +63 -0
  316. package/src/components/styled-bar-list.tsx +70 -0
  317. package/src/components/suggested-actions.tsx +1985 -0
  318. package/src/components/table.tsx +116 -0
  319. package/src/components/tabs.tsx +91 -0
  320. package/src/components/textarea.tsx +18 -0
  321. package/src/components/timeline-activity.tsx +234 -0
  322. package/src/components/tooltip.tsx +57 -0
  323. package/src/components/view-mode-toggle.tsx +39 -0
  324. package/src/components/virtualized-data-table.tsx +309 -0
  325. package/src/hooks/use-mobile.ts +21 -0
  326. package/src/index.ts +78 -0
  327. package/src/lib/icons.ts +18 -0
  328. package/src/lib/utils.ts +6 -0
  329. package/src/prototype/index.ts +11 -0
  330. package/src/prototype/prototype-accounts-view.tsx +112 -0
  331. package/src/prototype/prototype-admin-view.tsx +67 -0
  332. package/src/prototype/prototype-config.ts +243 -0
  333. package/src/prototype/prototype-inbox-view.tsx +810 -0
  334. package/src/prototype/prototype-insights-view.tsx +379 -0
  335. package/src/prototype/prototype-shell.tsx +219 -0
  336. package/src/prototype/prototype-work-queue-view.tsx +30 -0
  337. package/src/styles/globals.css +299 -0
  338. package/src/three/agent-orb.tsx +557 -0
  339. package/src/three/index.ts +5 -0
  340. package/src/types/r3f.d.ts +8 -0
@@ -0,0 +1,309 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { useVirtualizer } from "@tanstack/react-virtual"
5
+ import {
6
+ useReactTable,
7
+ getCoreRowModel,
8
+ flexRender,
9
+ type ColumnDef,
10
+ type SortingState,
11
+ type ColumnFiltersState,
12
+ type VisibilityState,
13
+ type OnChangeFn,
14
+ } from "@tanstack/react-table"
15
+ import { ArrowDown, ArrowUp, ArrowUpDown, SearchX, Loader2 } from "lucide-react"
16
+
17
+ import { cn } from "../lib/utils"
18
+
19
+ export interface VirtualizedDataTableProps<TData> {
20
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
21
+ columns: ColumnDef<TData, any>[]
22
+ data: TData[]
23
+
24
+ // Virtualization
25
+ height?: number | string
26
+ estimateRowHeight?: number
27
+ overscan?: number
28
+
29
+ // Row interaction
30
+ onRowClick?: (row: TData) => void
31
+ getRowId?: (original: TData, index: number) => string
32
+
33
+ // Infinite scroll
34
+ onReachBottom?: () => void
35
+ reachBottomThreshold?: number
36
+ hasMore?: boolean
37
+ isFetchingMore?: boolean
38
+
39
+ // Server-driven state (controlled) — omit for internal state
40
+ sorting?: SortingState
41
+ onSortingChange?: OnChangeFn<SortingState>
42
+ columnFilters?: ColumnFiltersState
43
+ onColumnFiltersChange?: OnChangeFn<ColumnFiltersState>
44
+ columnVisibility?: VisibilityState
45
+ onColumnVisibilityChange?: OnChangeFn<VisibilityState>
46
+
47
+ // Empty state
48
+ emptyIcon?: React.ReactNode
49
+ emptyMessage?: string
50
+ emptyDescription?: string
51
+
52
+ // Styling
53
+ className?: string
54
+ }
55
+
56
+ export function VirtualizedDataTable<TData>({
57
+ columns,
58
+ data,
59
+ height = 600,
60
+ estimateRowHeight = 48,
61
+ overscan = 8,
62
+ onRowClick,
63
+ getRowId,
64
+ onReachBottom,
65
+ reachBottomThreshold = 5,
66
+ hasMore = true,
67
+ isFetchingMore,
68
+ sorting,
69
+ onSortingChange,
70
+ columnFilters,
71
+ onColumnFiltersChange,
72
+ columnVisibility,
73
+ onColumnVisibilityChange,
74
+ emptyIcon,
75
+ emptyMessage = "No rows found",
76
+ emptyDescription = "Try adjusting your filters",
77
+ className,
78
+ }: VirtualizedDataTableProps<TData>) {
79
+ // Controlled/uncontrolled state for sorting
80
+ const [internalSorting, setInternalSorting] = React.useState<SortingState>([])
81
+ const resolvedSorting = sorting ?? internalSorting
82
+ const resolvedOnSortingChange = onSortingChange ?? setInternalSorting
83
+
84
+ // Controlled/uncontrolled state for column filters
85
+ const [internalColumnFilters, setInternalColumnFilters] =
86
+ React.useState<ColumnFiltersState>([])
87
+ const resolvedColumnFilters = columnFilters ?? internalColumnFilters
88
+ const resolvedOnColumnFiltersChange =
89
+ onColumnFiltersChange ?? setInternalColumnFilters
90
+
91
+ // Controlled/uncontrolled state for column visibility
92
+ const [internalColumnVisibility, setInternalColumnVisibility] =
93
+ React.useState<VisibilityState>({})
94
+ const resolvedColumnVisibility = columnVisibility ?? internalColumnVisibility
95
+ const resolvedOnColumnVisibilityChange =
96
+ onColumnVisibilityChange ?? setInternalColumnVisibility
97
+
98
+ // TanStack Table setup
99
+ const table = useReactTable({
100
+ data,
101
+ columns,
102
+ ...(getRowId ? { getRowId } : {}),
103
+ state: {
104
+ sorting: resolvedSorting,
105
+ columnFilters: resolvedColumnFilters,
106
+ columnVisibility: resolvedColumnVisibility,
107
+ },
108
+ onSortingChange: resolvedOnSortingChange,
109
+ onColumnFiltersChange: resolvedOnColumnFiltersChange,
110
+ onColumnVisibilityChange: resolvedOnColumnVisibilityChange,
111
+ manualSorting: true,
112
+ manualFiltering: true,
113
+ manualPagination: true,
114
+ getCoreRowModel: getCoreRowModel(),
115
+ })
116
+
117
+ // Virtualizer setup
118
+ const scrollContainerRef = React.useRef<HTMLDivElement>(null)
119
+ const rows = table.getRowModel().rows
120
+
121
+ const virtualizer = useVirtualizer({
122
+ count: rows.length,
123
+ getScrollElement: () => scrollContainerRef.current,
124
+ estimateSize: () => estimateRowHeight,
125
+ overscan,
126
+ measureElement: (element) => element.getBoundingClientRect().height,
127
+ })
128
+
129
+ // Infinite scroll detection
130
+ const lastTriggeredDataLengthRef = React.useRef<number>(0)
131
+
132
+ // Derive a stable primitive for the last visible virtual-item index so the
133
+ // effect below doesn't re-run on every render (getVirtualItems() returns a
134
+ // new array reference each call).
135
+ const virtualItems = virtualizer.getVirtualItems()
136
+ const lastVirtualItemIndex =
137
+ virtualItems.length > 0
138
+ ? virtualItems[virtualItems.length - 1].index
139
+ : -1
140
+
141
+ React.useEffect(() => {
142
+ if (!onReachBottom || isFetchingMore || hasMore === false) return
143
+ if (lastVirtualItemIndex < 0) return
144
+ if (lastVirtualItemIndex < rows.length - reachBottomThreshold) return
145
+
146
+ // Prevent re-firing until data.length changes (i.e. new page loaded).
147
+ if (lastTriggeredDataLengthRef.current === data.length) return
148
+ lastTriggeredDataLengthRef.current = data.length
149
+
150
+ onReachBottom()
151
+ }, [
152
+ lastVirtualItemIndex,
153
+ rows.length,
154
+ data.length,
155
+ onReachBottom,
156
+ isFetchingMore,
157
+ hasMore,
158
+ reachBottomThreshold,
159
+ ])
160
+
161
+ return (
162
+ <div className={cn("w-full", className)}>
163
+ <div
164
+ ref={scrollContainerRef}
165
+ className="relative overflow-auto"
166
+ style={{
167
+ height: typeof height === "number" ? `${height}px` : height,
168
+ contain: "strict",
169
+ }}
170
+ role="table"
171
+ aria-rowcount={data.length}
172
+ aria-colcount={table.getVisibleLeafColumns().length}
173
+ >
174
+ {/* Sticky header */}
175
+ <div className="sticky top-0 z-10 bg-background" role="rowgroup">
176
+ {table.getHeaderGroups().map((headerGroup) => (
177
+ <div
178
+ key={headerGroup.id}
179
+ className="flex w-max min-w-full border-b border-border/50"
180
+ role="row"
181
+ >
182
+ {headerGroup.headers.map((header, colIdx) => (
183
+ <div
184
+ key={header.id}
185
+ className="h-9 px-3 flex items-center text-xs font-medium text-muted-foreground whitespace-nowrap"
186
+ style={{
187
+ width: header.getSize(),
188
+ minWidth: header.getSize(),
189
+ }}
190
+ role="columnheader"
191
+ aria-colindex={colIdx + 1}
192
+ aria-sort={
193
+ header.column.getIsSorted() === "asc"
194
+ ? "ascending"
195
+ : header.column.getIsSorted() === "desc"
196
+ ? "descending"
197
+ : header.column.getCanSort()
198
+ ? "none"
199
+ : undefined
200
+ }
201
+ >
202
+ {header.isPlaceholder ? null : header.column.getCanSort() ? (
203
+ <button
204
+ type="button"
205
+ className="inline-flex items-center gap-1 hover:text-foreground transition-colors"
206
+ onClick={header.column.getToggleSortingHandler()}
207
+ >
208
+ {flexRender(
209
+ header.column.columnDef.header,
210
+ header.getContext(),
211
+ )}
212
+ {header.column.getIsSorted() === "asc" ? (
213
+ <ArrowUp className="w-3 h-3" />
214
+ ) : header.column.getIsSorted() === "desc" ? (
215
+ <ArrowDown className="w-3 h-3" />
216
+ ) : (
217
+ <ArrowUpDown className="w-3 h-3 opacity-40" />
218
+ )}
219
+ </button>
220
+ ) : (
221
+ flexRender(
222
+ header.column.columnDef.header,
223
+ header.getContext(),
224
+ )
225
+ )}
226
+ </div>
227
+ ))}
228
+ </div>
229
+ ))}
230
+ </div>
231
+
232
+ {/* Virtualized body or empty state */}
233
+ {rows.length > 0 ? (
234
+ <div
235
+ role="rowgroup"
236
+ style={{
237
+ height: virtualizer.getTotalSize(),
238
+ width: "100%",
239
+ position: "relative",
240
+ }}
241
+ >
242
+ {virtualizer.getVirtualItems().map((virtualRow) => {
243
+ const row = rows[virtualRow.index]
244
+ return (
245
+ <div
246
+ key={row.id}
247
+ data-index={virtualRow.index}
248
+ ref={virtualizer.measureElement}
249
+ className={cn(
250
+ "absolute left-0 w-max min-w-full flex group transition-colors",
251
+ onRowClick && "cursor-pointer",
252
+ )}
253
+ style={{
254
+ transform: `translateY(${virtualRow.start}px)`,
255
+ }}
256
+ role="row"
257
+ aria-rowindex={virtualRow.index + 2}
258
+ onClick={() => onRowClick?.(row.original)}
259
+ tabIndex={onRowClick ? 0 : undefined}
260
+ onKeyDown={
261
+ onRowClick
262
+ ? (e: React.KeyboardEvent) => {
263
+ if (e.key === "Enter" || e.key === " ") {
264
+ e.preventDefault()
265
+ onRowClick(row.original)
266
+ }
267
+ }
268
+ : undefined
269
+ }
270
+ >
271
+ {row.getVisibleCells().map((cell, colIdx) => (
272
+ <div
273
+ key={cell.id}
274
+ className="px-3 py-3 flex items-center whitespace-nowrap group-hover:bg-muted/50"
275
+ style={{
276
+ width: cell.column.getSize(),
277
+ minWidth: cell.column.getSize(),
278
+ }}
279
+ role="cell"
280
+ aria-colindex={colIdx + 1}
281
+ >
282
+ {flexRender(
283
+ cell.column.columnDef.cell,
284
+ cell.getContext(),
285
+ )}
286
+ </div>
287
+ ))}
288
+ </div>
289
+ )
290
+ })}
291
+ </div>
292
+ ) : (
293
+ <div className="flex flex-col items-center justify-center gap-1 text-muted-foreground py-20">
294
+ {emptyIcon ?? <SearchX className="h-7 w-7 opacity-40" />}
295
+ <p className="text-sm font-medium">{emptyMessage}</p>
296
+ <p className="text-xs">{emptyDescription}</p>
297
+ </div>
298
+ )}
299
+
300
+ {/* Loading indicator */}
301
+ {isFetchingMore && (
302
+ <div className="flex items-center justify-center py-4">
303
+ <Loader2 className="h-5 w-5 animate-spin text-muted-foreground" />
304
+ </div>
305
+ )}
306
+ </div>
307
+ </div>
308
+ )
309
+ }
@@ -0,0 +1,21 @@
1
+ import * as React from "react";
2
+
3
+ const MOBILE_BREAKPOINT = 768;
4
+
5
+ export function useIsMobile() {
6
+ const [isMobile, setIsMobile] = React.useState<boolean | undefined>(
7
+ undefined,
8
+ );
9
+
10
+ React.useEffect(() => {
11
+ const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
12
+ const onChange = () => {
13
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
14
+ };
15
+ mql.addEventListener("change", onChange);
16
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
17
+ return () => mql.removeEventListener("change", onChange);
18
+ }, []);
19
+
20
+ return !!isMobile;
21
+ }
package/src/index.ts ADDED
@@ -0,0 +1,78 @@
1
+ /**
2
+ * @handled-ai/design-system
3
+ * UI components and utilities (shadcn-style, New York)
4
+ */
5
+
6
+ // Utilities
7
+ export { cn } from "./lib/utils"
8
+ export { BRAND_ICONS } from "./lib/icons"
9
+
10
+ // Hooks
11
+ export { useIsMobile } from "./hooks/use-mobile"
12
+
13
+ // Components (light — no recharts/nivo/three transitive deps)
14
+ export * from "./components/activity-detail"
15
+ export * from "./components/activity-log"
16
+ export * from "./components/agent-popover"
17
+ export * from "./components/agent-widget"
18
+ export * from "./components/avatar"
19
+ export * from "./components/badge"
20
+ export * from "./components/button"
21
+ export * from "./components/card"
22
+ export * from "./components/contact-list"
23
+ export * from "./components/dashboard-cards"
24
+ export * from "./components/data-table"
25
+ export * from "./components/data-table-display"
26
+ export * from "./components/data-table-filter"
27
+ export * from "./components/data-table-quick-views"
28
+ export * from "./components/data-table-toolbar"
29
+ export * from "./components/detail-view"
30
+ export * from "./components/dialog"
31
+ export * from "./components/dropdown-menu"
32
+ export * from "./components/entity-panel"
33
+ export * from "./components/inbox-row"
34
+ export * from "./components/inbox-toolbar"
35
+ export * from "./components/input"
36
+ export * from "./components/insights-filter-bar"
37
+ export * from "./components/item-list"
38
+ export * from "./components/item-list-display"
39
+ export * from "./components/item-list-filter"
40
+ export * from "./components/item-list-toolbar"
41
+ export * from "./components/label"
42
+ export * from "./components/message"
43
+ export * from "./components/metric-card"
44
+ export * from "./components/performance-metrics-table"
45
+ export * from "./components/preview-list"
46
+ export * from "./components/progress"
47
+ export * from "./components/quick-action-chat-area"
48
+ export {
49
+ QuickActionModal,
50
+ type QuickActionPriority,
51
+ type QuickActionTaskDraft,
52
+ type QuickActionTemplate,
53
+ } from "./components/quick-action-modal"
54
+ export * from "./components/quick-action-sidebar-nav"
55
+ export * from "./components/recommended-actions-section"
56
+ export * from "./components/report-card"
57
+ export * from "./components/score-analysis-modal"
58
+ export * from "./components/score-breakdown"
59
+ export * from "./components/score-feedback"
60
+ export * from "./components/score-ring"
61
+ export * from "./components/scroll-area"
62
+ export * from "./components/select"
63
+ export * from "./components/separator"
64
+ export * from "./components/sheet"
65
+ export * from "./components/sidebar"
66
+ export * from "./components/signal-feedback-inline"
67
+ export * from "./components/simple-data-table"
68
+ export * from "./components/virtualized-data-table"
69
+ export * from "./components/skeleton"
70
+ export * from "./components/status-badge"
71
+ export * from "./components/styled-bar-list"
72
+ export * from "./components/suggested-actions"
73
+ export * from "./components/table"
74
+ export * from "./components/tabs"
75
+ export * from "./components/textarea"
76
+ export * from "./components/timeline-activity"
77
+ export * from "./components/tooltip"
78
+ export * from "./components/view-mode-toggle"
@@ -0,0 +1,18 @@
1
+ const SUPABASE_ICONS = "https://bqvtedneuaozpgcgcxow.supabase.co/storage/v1/object/public/media/Icons";
2
+
3
+ export const BRAND_ICONS = {
4
+ gdoc: `${SUPABASE_ICONS}/gdoc.a1.svg`,
5
+ gmail: {
6
+ icon: `${SUPABASE_ICONS}/gmail.icon.a1.svg`,
7
+ logo: `${SUPABASE_ICONS}/gmail.logo.a2.svg`,
8
+ },
9
+ gong: `${SUPABASE_ICONS}/gong.icon.a1.png`,
10
+ google: `${SUPABASE_ICONS}/google.icon.a1.svg`,
11
+ hubspot: `${SUPABASE_ICONS}/hubspot.icon.a1.svg`,
12
+ linkedin: `${SUPABASE_ICONS}/linkedin.icon.a1.svg`,
13
+ microsoft: `${SUPABASE_ICONS}/microsoft.icon.a1.svg`,
14
+ outlook: `${SUPABASE_ICONS}/outlook.icon.a2.svg`,
15
+ salesforce: `${SUPABASE_ICONS}/salesforce.icon.a1.svg`,
16
+ slack: `${SUPABASE_ICONS}/slack.icon.a1.svg`,
17
+ zendesk: `${SUPABASE_ICONS}/zendesk.icon.a1.svg`,
18
+ } as const;
@@ -0,0 +1,6 @@
1
+ import { clsx, type ClassValue } from "clsx"
2
+ import { twMerge } from "tailwind-merge"
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs))
6
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @handled-ai/design-system/prototype
3
+ * Prototype template system (transitively depends on charts)
4
+ */
5
+ export * from "./prototype-config"
6
+ export * from "./prototype-shell"
7
+ export * from "./prototype-inbox-view"
8
+ export * from "./prototype-insights-view"
9
+ export * from "./prototype-accounts-view"
10
+ export * from "./prototype-admin-view"
11
+ export * from "./prototype-work-queue-view"
@@ -0,0 +1,112 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { Button } from "../components/button"
5
+ import { Badge } from "../components/badge"
6
+ import { DataTable } from "../components/data-table"
7
+ import type { AccountsViewConfig, AccountFilterTab } from "./prototype-config"
8
+ import type { DataRow } from "../components/data-table"
9
+
10
+ // ---------------------------------------------------------------------------
11
+ // Props
12
+ // ---------------------------------------------------------------------------
13
+
14
+ export interface PrototypeAccountsViewProps extends AccountsViewConfig {
15
+ headerActions?: React.ReactNode
16
+ onRowClick?: (row: DataRow) => void
17
+ }
18
+
19
+ // ---------------------------------------------------------------------------
20
+ // Defaults
21
+ // ---------------------------------------------------------------------------
22
+
23
+ const DEFAULT_FILTER_TABS: AccountFilterTab[] = [
24
+ { label: "All Accounts", count: 6, variant: "default" },
25
+ { label: "Needs Attention", count: 11, variant: "attention" },
26
+ { label: "Recent", count: 7, variant: "ghost" },
27
+ ]
28
+
29
+ // ---------------------------------------------------------------------------
30
+ // Component
31
+ // ---------------------------------------------------------------------------
32
+
33
+ export function PrototypeAccountsView({
34
+ filterTabs,
35
+ headerActions,
36
+ onRowClick,
37
+ rows,
38
+ filterCategories,
39
+ quickViews,
40
+ moreQuickViews,
41
+ quickViewFilters,
42
+ iconMap,
43
+ entityUrlBuilder,
44
+ onScoreFactorFeedback,
45
+ onScoreApproveFeedback,
46
+ onScoreDismissFeedback,
47
+ }: PrototypeAccountsViewProps) {
48
+ const tabs = filterTabs ?? DEFAULT_FILTER_TABS
49
+
50
+ return (
51
+ <div className="flex flex-col h-full w-full bg-background relative">
52
+ {headerActions && (
53
+ <div className="absolute top-4 right-4 z-10">{headerActions}</div>
54
+ )}
55
+
56
+ <div className="px-4 py-3 border-b border-border flex items-center gap-2 overflow-x-auto shrink-0 mt-2">
57
+ {tabs.map((tab, i) => {
58
+ if (tab.variant === "attention") {
59
+ return (
60
+ <Button key={i} size="sm" className="h-7 text-xs rounded-md bg-emerald-600 hover:bg-emerald-700 text-white font-medium">
61
+ {tab.label}
62
+ {tab.count != null && (
63
+ <Badge variant="secondary" className="ml-2 h-4 px-1.5 text-[10px] bg-white/20 text-white border-transparent">
64
+ {tab.count}
65
+ </Badge>
66
+ )}
67
+ </Button>
68
+ )
69
+ }
70
+ if (tab.variant === "ghost") {
71
+ return (
72
+ <Button key={i} variant="ghost" size="sm" className="h-7 text-xs rounded-md border border-border bg-transparent font-medium">
73
+ {tab.label}
74
+ {tab.count != null && (
75
+ <Badge variant="secondary" className="ml-2 h-4 px-1.5 text-[10px]">
76
+ {tab.count}
77
+ </Badge>
78
+ )}
79
+ </Button>
80
+ )
81
+ }
82
+ return (
83
+ <Button key={i} variant="secondary" size="sm" className="h-7 text-xs rounded-md bg-muted font-medium">
84
+ {tab.label}
85
+ {tab.count != null && (
86
+ <Badge variant="outline" className="ml-2 h-4 px-1.5 text-[10px]">
87
+ {tab.count}
88
+ </Badge>
89
+ )}
90
+ </Button>
91
+ )
92
+ })}
93
+ </div>
94
+
95
+ <div className="flex-1 overflow-auto">
96
+ <DataTable
97
+ onRowClick={onRowClick}
98
+ rows={rows}
99
+ filterCategories={filterCategories}
100
+ quickViews={quickViews}
101
+ moreQuickViews={moreQuickViews}
102
+ quickViewFilters={quickViewFilters}
103
+ iconMap={iconMap}
104
+ entityUrlBuilder={entityUrlBuilder}
105
+ onScoreFactorFeedback={onScoreFactorFeedback}
106
+ onScoreApproveFeedback={onScoreApproveFeedback}
107
+ onScoreDismissFeedback={onScoreDismissFeedback}
108
+ />
109
+ </div>
110
+ </div>
111
+ )
112
+ }
@@ -0,0 +1,67 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { Settings } from "lucide-react"
5
+ import { Tabs, TabsList, TabsTrigger, TabsContent } from "../components/tabs"
6
+ import type { AdminViewConfig } from "./prototype-config"
7
+
8
+ export interface PrototypeAdminViewProps extends AdminViewConfig {
9
+ headerActions?: React.ReactNode
10
+ }
11
+
12
+ export function PrototypeAdminView({
13
+ title = "Admin",
14
+ icon: Icon = Settings,
15
+ tabs,
16
+ defaultTab,
17
+ headerActions,
18
+ }: PrototypeAdminViewProps) {
19
+ const resolvedDefault = defaultTab ?? tabs[0]?.id
20
+
21
+ if (!tabs.length) return null
22
+
23
+ return (
24
+ <div className="flex h-full w-full flex-col overflow-hidden bg-background">
25
+ <div className="shrink-0 border-b border-border bg-background">
26
+ <div className="flex items-center justify-between px-6 py-4">
27
+ <div className="flex items-center gap-2 text-lg font-bold text-foreground">
28
+ <Icon className="h-5 w-5" />
29
+ <span>{title}</span>
30
+ </div>
31
+ {headerActions}
32
+ </div>
33
+ </div>
34
+
35
+ <div className="flex-1 overflow-auto p-8">
36
+ <div className="mx-auto max-w-6xl">
37
+ <Tabs defaultValue={resolvedDefault} className="w-full space-y-8">
38
+ <div className="flex justify-start">
39
+ <TabsList className="h-9 rounded-lg border border-border/50 bg-muted/50 p-1">
40
+ {tabs.map((tab) => (
41
+ <TabsTrigger
42
+ key={tab.id}
43
+ value={tab.id}
44
+ className="gap-2 rounded-md px-3 py-1.5 text-xs font-medium transition-all data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm"
45
+ >
46
+ {tab.icon && <tab.icon className="h-3.5 w-3.5" />}
47
+ {tab.label}
48
+ </TabsTrigger>
49
+ ))}
50
+ </TabsList>
51
+ </div>
52
+
53
+ {tabs.map((tab) => (
54
+ <TabsContent
55
+ key={tab.id}
56
+ value={tab.id}
57
+ className="space-y-6 focus-visible:outline-none"
58
+ >
59
+ {tab.content}
60
+ </TabsContent>
61
+ ))}
62
+ </Tabs>
63
+ </div>
64
+ </div>
65
+ </div>
66
+ )
67
+ }