@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,278 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import {
5
+ ArrowDownAZ,
6
+ ArrowUpAZ,
7
+ LayoutTemplate,
8
+ List as ListIcon,
9
+ RotateCcw,
10
+ SlidersHorizontal,
11
+ } from "lucide-react"
12
+ import { Popover as PopoverPrimitive } from "radix-ui"
13
+
14
+ import { cn } from "../lib/utils"
15
+ import { Button } from "./button"
16
+ import {
17
+ Select,
18
+ SelectContent,
19
+ SelectItem,
20
+ SelectTrigger,
21
+ SelectValue,
22
+ } from "./select"
23
+ import { Separator } from "./separator"
24
+
25
+ export type ItemListGrouping = "stage" | "owner" | "risk"
26
+ export type ItemListViewMode = "list" | "board"
27
+
28
+ export interface ItemListDisplayState {
29
+ viewMode: ItemListViewMode
30
+ grouping: ItemListGrouping
31
+ subGrouping: "none" | ItemListGrouping
32
+ ordering: string
33
+ orderingDirection: "asc" | "desc"
34
+ showContactSignals: boolean
35
+ showOwner: boolean
36
+ showStatus: boolean
37
+ }
38
+
39
+ interface ItemListDisplayProps {
40
+ value: ItemListDisplayState
41
+ onChange: (next: ItemListDisplayState) => void
42
+ onReset: () => void
43
+ }
44
+
45
+ export function ItemListDisplay({
46
+ value,
47
+ onChange,
48
+ onReset,
49
+ }: ItemListDisplayProps) {
50
+ const toggleField = (
51
+ field: "showContactSignals" | "showOwner" | "showStatus",
52
+ ) => {
53
+ onChange({
54
+ ...value,
55
+ [field]: !value[field],
56
+ })
57
+ }
58
+
59
+ return (
60
+ <PopoverPrimitive.Root>
61
+ <PopoverPrimitive.Trigger asChild>
62
+ <Button
63
+ variant="outline"
64
+ size="sm"
65
+ className="h-8 gap-2 rounded-md border-border bg-background text-xs font-medium text-foreground shadow-none hover:bg-muted/50"
66
+ >
67
+ <SlidersHorizontal className="h-3.5 w-3.5" />
68
+ Display
69
+ </Button>
70
+ </PopoverPrimitive.Trigger>
71
+ <PopoverPrimitive.Portal>
72
+ <PopoverPrimitive.Content
73
+ align="start"
74
+ sideOffset={4}
75
+ className="z-50 w-[280px] rounded-lg border border-border bg-background p-4 shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95"
76
+ >
77
+ <div className="flex flex-col gap-4">
78
+ {/* View Toggle */}
79
+ <div className="flex rounded-md bg-muted/60 p-1">
80
+ <button
81
+ type="button"
82
+ onClick={() => onChange({ ...value, viewMode: "list" })}
83
+ className={cn(
84
+ "flex flex-1 flex-col items-center justify-center gap-1.5 rounded-sm px-3 py-2 text-xs font-medium transition-all",
85
+ value.viewMode === "list"
86
+ ? "bg-background text-foreground shadow-sm"
87
+ : "text-muted-foreground hover:text-foreground",
88
+ )}
89
+ >
90
+ <ListIcon className="h-4 w-4" />
91
+ List
92
+ </button>
93
+ <button
94
+ type="button"
95
+ onClick={() => onChange({ ...value, viewMode: "board" })}
96
+ className={cn(
97
+ "flex flex-1 flex-col items-center justify-center gap-1.5 rounded-sm px-3 py-2 text-xs font-medium transition-all",
98
+ value.viewMode === "board"
99
+ ? "bg-background text-foreground shadow-sm"
100
+ : "text-muted-foreground hover:text-foreground",
101
+ )}
102
+ >
103
+ <LayoutTemplate className="h-4 w-4" />
104
+ Board
105
+ </button>
106
+ </div>
107
+
108
+ <Separator />
109
+
110
+ {/* Grouping Controls */}
111
+ <div className="space-y-3">
112
+ <div className="grid grid-cols-[80px_1fr] items-center gap-2">
113
+ <span className="text-xs font-medium text-muted-foreground">
114
+ Grouping
115
+ </span>
116
+ <Select
117
+ value={value.grouping}
118
+ onValueChange={(g) =>
119
+ onChange({ ...value, grouping: g as ItemListGrouping })
120
+ }
121
+ >
122
+ <SelectTrigger className="h-8 text-xs">
123
+ <SelectValue />
124
+ </SelectTrigger>
125
+ <SelectContent>
126
+ <SelectItem value="stage" className="text-xs">
127
+ Stage
128
+ </SelectItem>
129
+ <SelectItem value="owner" className="text-xs">
130
+ Assignee
131
+ </SelectItem>
132
+ <SelectItem value="risk" className="text-xs">
133
+ Risk level
134
+ </SelectItem>
135
+ </SelectContent>
136
+ </Select>
137
+ </div>
138
+
139
+ <div className="grid grid-cols-[80px_1fr] items-center gap-2">
140
+ <span className="text-xs font-medium text-muted-foreground">
141
+ Sub-grouping
142
+ </span>
143
+ <Select
144
+ value={value.subGrouping}
145
+ onValueChange={(g) =>
146
+ onChange({
147
+ ...value,
148
+ subGrouping: g as "none" | ItemListGrouping,
149
+ })
150
+ }
151
+ >
152
+ <SelectTrigger className="h-8 text-xs">
153
+ <SelectValue />
154
+ </SelectTrigger>
155
+ <SelectContent>
156
+ <SelectItem value="none" className="text-xs">
157
+ No grouping
158
+ </SelectItem>
159
+ <SelectItem value="stage" className="text-xs">
160
+ Stage
161
+ </SelectItem>
162
+ <SelectItem value="owner" className="text-xs">
163
+ Assignee
164
+ </SelectItem>
165
+ <SelectItem value="risk" className="text-xs">
166
+ Risk level
167
+ </SelectItem>
168
+ </SelectContent>
169
+ </Select>
170
+ </div>
171
+
172
+ <div className="grid grid-cols-[80px_1fr_32px] items-center gap-2">
173
+ <span className="text-xs font-medium text-muted-foreground">
174
+ Ordering
175
+ </span>
176
+ <Select
177
+ value={value.ordering}
178
+ onValueChange={(o) => onChange({ ...value, ordering: o })}
179
+ >
180
+ <SelectTrigger className="h-8 text-xs">
181
+ <SelectValue />
182
+ </SelectTrigger>
183
+ <SelectContent>
184
+ <SelectItem value="priority" className="text-xs">
185
+ Priority
186
+ </SelectItem>
187
+ <SelectItem value="aging" className="text-xs">
188
+ Age
189
+ </SelectItem>
190
+ <SelectItem value="name" className="text-xs">
191
+ Name
192
+ </SelectItem>
193
+ </SelectContent>
194
+ </Select>
195
+ <Button
196
+ variant="outline"
197
+ size="icon"
198
+ className="h-8 w-8"
199
+ onClick={() =>
200
+ onChange({
201
+ ...value,
202
+ orderingDirection:
203
+ value.orderingDirection === "asc" ? "desc" : "asc",
204
+ })
205
+ }
206
+ >
207
+ {value.orderingDirection === "asc" ? (
208
+ <ArrowDownAZ className="h-3.5 w-3.5" />
209
+ ) : (
210
+ <ArrowUpAZ className="h-3.5 w-3.5" />
211
+ )}
212
+ </Button>
213
+ </div>
214
+ </div>
215
+
216
+ <Separator />
217
+
218
+ {/* Visible Row Details */}
219
+ <div className="space-y-2">
220
+ <span className="text-[11px] font-semibold uppercase tracking-wide text-muted-foreground">
221
+ Visible row details
222
+ </span>
223
+ <div className="flex flex-wrap gap-2">
224
+ <button
225
+ type="button"
226
+ onClick={() => toggleField("showContactSignals")}
227
+ className={cn(
228
+ "rounded-md border px-2.5 py-1 text-[11px] font-medium transition-colors",
229
+ value.showContactSignals
230
+ ? "border-emerald-300 bg-emerald-50 text-emerald-700"
231
+ : "border-border bg-background text-muted-foreground hover:bg-muted",
232
+ )}
233
+ >
234
+ Contact signals
235
+ </button>
236
+ <button
237
+ type="button"
238
+ onClick={() => toggleField("showOwner")}
239
+ className={cn(
240
+ "rounded-md border px-2.5 py-1 text-[11px] font-medium transition-colors",
241
+ value.showOwner
242
+ ? "border-emerald-300 bg-emerald-50 text-emerald-700"
243
+ : "border-border bg-background text-muted-foreground hover:bg-muted",
244
+ )}
245
+ >
246
+ Assignee
247
+ </button>
248
+ <button
249
+ type="button"
250
+ onClick={() => toggleField("showStatus")}
251
+ className={cn(
252
+ "rounded-md border px-2.5 py-1 text-[11px] font-medium transition-colors",
253
+ value.showStatus
254
+ ? "border-emerald-300 bg-emerald-50 text-emerald-700"
255
+ : "border-border bg-background text-muted-foreground hover:bg-muted",
256
+ )}
257
+ >
258
+ Stage badge
259
+ </button>
260
+ </div>
261
+ </div>
262
+
263
+ <Separator />
264
+
265
+ <button
266
+ type="button"
267
+ onClick={onReset}
268
+ className="inline-flex items-center gap-1 text-[11px] font-medium text-muted-foreground transition-colors hover:text-foreground"
269
+ >
270
+ <RotateCcw className="h-3 w-3" />
271
+ Reset display settings
272
+ </button>
273
+ </div>
274
+ </PopoverPrimitive.Content>
275
+ </PopoverPrimitive.Portal>
276
+ </PopoverPrimitive.Root>
277
+ )
278
+ }
@@ -0,0 +1,118 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { Filter, X } from "lucide-react"
5
+
6
+ import { cn } from "../lib/utils"
7
+ import { Button } from "./button"
8
+ import {
9
+ DropdownMenu,
10
+ DropdownMenuContent,
11
+ DropdownMenuLabel,
12
+ DropdownMenuSeparator,
13
+ DropdownMenuTrigger,
14
+ } from "./dropdown-menu"
15
+
16
+ export interface ItemListFilterCategory {
17
+ id: string
18
+ label: string
19
+ options: string[]
20
+ }
21
+
22
+ interface ItemListFilterProps {
23
+ categories: ItemListFilterCategory[]
24
+ selectedFilters: Record<string, string[]>
25
+ onToggleFilter: (categoryId: string, option: string) => void
26
+ onClearFilters: () => void
27
+ }
28
+
29
+ function getActiveFilterCount(selectedFilters: Record<string, string[]>) {
30
+ return Object.values(selectedFilters).reduce(
31
+ (count, values) => count + values.length,
32
+ 0
33
+ )
34
+ }
35
+
36
+ export function ItemListFilter({
37
+ categories,
38
+ selectedFilters,
39
+ onToggleFilter,
40
+ onClearFilters,
41
+ }: ItemListFilterProps) {
42
+ const activeFilterCount = getActiveFilterCount(selectedFilters)
43
+
44
+ return (
45
+ <DropdownMenu>
46
+ <DropdownMenuTrigger asChild>
47
+ <Button
48
+ variant="outline"
49
+ size="sm"
50
+ className="h-8 gap-2 rounded-md border-border bg-background text-xs font-medium text-foreground shadow-none hover:bg-muted/50"
51
+ >
52
+ <Filter className="h-3.5 w-3.5" />
53
+ Filter
54
+ {activeFilterCount > 0 ? (
55
+ <span className="rounded bg-muted px-1.5 py-0 text-[10px] font-semibold text-muted-foreground">
56
+ {activeFilterCount}
57
+ </span>
58
+ ) : null}
59
+ </Button>
60
+ </DropdownMenuTrigger>
61
+ <DropdownMenuContent align="end" className="w-[280px] p-3">
62
+ <DropdownMenuLabel className="px-0 text-xs font-semibold text-foreground">
63
+ Apply filters
64
+ </DropdownMenuLabel>
65
+ <DropdownMenuSeparator className="my-2" />
66
+
67
+ <div className="space-y-3">
68
+ {categories.map((category, index) => (
69
+ <div key={category.id} className="space-y-1.5">
70
+ <p className="text-[11px] font-semibold uppercase tracking-wide text-muted-foreground">
71
+ {category.label}
72
+ </p>
73
+ <div className="flex flex-wrap gap-1.5">
74
+ {category.options.map((option) => {
75
+ const selected =
76
+ selectedFilters[category.id]?.includes(option) ?? false
77
+
78
+ return (
79
+ <button
80
+ key={option}
81
+ type="button"
82
+ onClick={() => onToggleFilter(category.id, option)}
83
+ className={cn(
84
+ "rounded-md border px-2.5 py-1 text-[11px] font-medium transition-colors",
85
+ selected
86
+ ? "border-emerald-300 bg-emerald-50 text-emerald-700"
87
+ : "border-border bg-background text-muted-foreground hover:bg-muted"
88
+ )}
89
+ >
90
+ {option}
91
+ </button>
92
+ )
93
+ })}
94
+ </div>
95
+ {index < categories.length - 1 ? (
96
+ <DropdownMenuSeparator className="mt-2" />
97
+ ) : null}
98
+ </div>
99
+ ))}
100
+ </div>
101
+
102
+ {activeFilterCount > 0 ? (
103
+ <>
104
+ <DropdownMenuSeparator className="my-2" />
105
+ <button
106
+ type="button"
107
+ onClick={onClearFilters}
108
+ className="inline-flex items-center gap-1 text-[11px] font-medium text-muted-foreground transition-colors hover:text-foreground"
109
+ >
110
+ <X className="h-3 w-3" />
111
+ Clear all
112
+ </button>
113
+ </>
114
+ ) : null}
115
+ </DropdownMenuContent>
116
+ </DropdownMenu>
117
+ )
118
+ }
@@ -0,0 +1,97 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+
5
+ import { cn } from "../lib/utils"
6
+ import {
7
+ ItemListDisplay,
8
+ type ItemListDisplayState,
9
+ } from "./item-list-display"
10
+ import {
11
+ ItemListFilter,
12
+ type ItemListFilterCategory,
13
+ } from "./item-list-filter"
14
+
15
+ export interface ItemListQuickView {
16
+ id: string
17
+ label: string
18
+ count: number
19
+ }
20
+
21
+ interface ItemListToolbarProps {
22
+ quickViews: ItemListQuickView[]
23
+ activeQuickView: string | null
24
+ onQuickViewChange: (viewId: string | null) => void
25
+ filterCategories: ItemListFilterCategory[]
26
+ selectedFilters: Record<string, string[]>
27
+ onToggleFilter: (categoryId: string, option: string) => void
28
+ onClearFilters: () => void
29
+ display: ItemListDisplayState
30
+ onDisplayChange: (next: ItemListDisplayState) => void
31
+ onResetDisplay: () => void
32
+ searchSlot?: React.ReactNode
33
+ }
34
+
35
+ export function ItemListToolbar({
36
+ quickViews,
37
+ activeQuickView,
38
+ onQuickViewChange,
39
+ filterCategories,
40
+ selectedFilters,
41
+ onToggleFilter,
42
+ onClearFilters,
43
+ display,
44
+ onDisplayChange,
45
+ onResetDisplay,
46
+ searchSlot,
47
+ }: ItemListToolbarProps) {
48
+ return (
49
+ <div className="flex flex-wrap items-center justify-between gap-3 border-b border-border bg-muted/20 px-4 py-3">
50
+ <div className="flex min-w-0 flex-1 items-center gap-2 overflow-x-auto">
51
+ {quickViews.map((quickView) => {
52
+ const isActive = quickView.id === activeQuickView
53
+ return (
54
+ <button
55
+ key={quickView.id}
56
+ type="button"
57
+ onClick={() => onQuickViewChange(isActive ? null : quickView.id)}
58
+ className={cn(
59
+ "flex shrink-0 items-center gap-1.5 rounded-md border px-2.5 py-1.5 text-xs font-medium transition-colors",
60
+ isActive
61
+ ? "border-emerald-700 bg-emerald-600 text-white"
62
+ : "border-border bg-background text-muted-foreground hover:bg-muted hover:text-foreground"
63
+ )}
64
+ >
65
+ {quickView.label}
66
+ <span
67
+ className={cn(
68
+ "rounded px-1.5 py-0 text-[10px] font-semibold",
69
+ isActive
70
+ ? "bg-emerald-700/80 text-white"
71
+ : "bg-muted text-muted-foreground"
72
+ )}
73
+ >
74
+ {quickView.count}
75
+ </span>
76
+ </button>
77
+ )
78
+ })}
79
+ </div>
80
+
81
+ <div className="flex shrink-0 items-center gap-2">
82
+ {searchSlot}
83
+ <ItemListFilter
84
+ categories={filterCategories}
85
+ selectedFilters={selectedFilters}
86
+ onToggleFilter={onToggleFilter}
87
+ onClearFilters={onClearFilters}
88
+ />
89
+ <ItemListDisplay
90
+ value={display}
91
+ onChange={onDisplayChange}
92
+ onReset={onResetDisplay}
93
+ />
94
+ </div>
95
+ </div>
96
+ )
97
+ }