@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,843 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import {
5
+ AlertCircle,
6
+ ChevronDown,
7
+ Mail,
8
+ MessageSquare,
9
+ Phone,
10
+ SearchX,
11
+ } from "lucide-react"
12
+
13
+ import { cn } from "../lib/utils"
14
+ import { Badge } from "./badge"
15
+ import {
16
+ ItemListToolbar,
17
+ type ItemListQuickView,
18
+ } from "./item-list-toolbar"
19
+ import { type ItemListDisplayState } from "./item-list-display"
20
+ import { type ItemListFilterCategory } from "./item-list-filter"
21
+
22
+ type QueueStage =
23
+ | "Referrals"
24
+ | "E&B Verified"
25
+ | "Contacted"
26
+ | "Pending Intake"
27
+ | "Pending Scheduling"
28
+
29
+ type QueueRisk = "At Risk" | "Watch" | "Stable"
30
+
31
+ interface QueueItem {
32
+ id: string
33
+ patient: string
34
+ source: string
35
+ specialty: string
36
+ stage: QueueStage
37
+ risk: QueueRisk
38
+ attempts: number
39
+ owner: string
40
+ aging: string
41
+ contactSignals: {
42
+ phone: boolean
43
+ email: boolean
44
+ message: boolean
45
+ }
46
+ }
47
+
48
+ const QUEUE_ITEMS: QueueItem[] = [
49
+ {
50
+ id: "REF-1894",
51
+ patient: "James Liu",
52
+ source: "Cedars",
53
+ specialty: "Oncology",
54
+ stage: "E&B Verified",
55
+ risk: "At Risk",
56
+ attempts: 2,
57
+ owner: "Jessica Wong",
58
+ aging: "Aging 18h",
59
+ contactSignals: { phone: true, email: false, message: false },
60
+ },
61
+ {
62
+ id: "REF-1903",
63
+ patient: "Michael Brown",
64
+ source: "Providence",
65
+ specialty: "Orthopedics",
66
+ stage: "E&B Verified",
67
+ risk: "Stable",
68
+ attempts: 1,
69
+ owner: "Sarah Johnson",
70
+ aging: "New today",
71
+ contactSignals: { phone: false, email: true, message: false },
72
+ },
73
+ {
74
+ id: "REF-1910",
75
+ patient: "Samantha Rodriguez",
76
+ source: "Kaiser",
77
+ specialty: "Rheumatology",
78
+ stage: "E&B Verified",
79
+ risk: "At Risk",
80
+ attempts: 4,
81
+ owner: "Michael Chen",
82
+ aging: "Aging 36h",
83
+ contactSignals: { phone: true, email: true, message: true },
84
+ },
85
+ {
86
+ id: "REF-1916",
87
+ patient: "Christopher Davis",
88
+ source: "USC",
89
+ specialty: "Gastroenterology",
90
+ stage: "E&B Verified",
91
+ risk: "Stable",
92
+ attempts: 1,
93
+ owner: "Daniel Kim",
94
+ aging: "New 6h ago",
95
+ contactSignals: { phone: false, email: true, message: true },
96
+ },
97
+ {
98
+ id: "REF-1924",
99
+ patient: "Lisa Anderson",
100
+ source: "CHLA",
101
+ specialty: "Pediatric Surgery",
102
+ stage: "E&B Verified",
103
+ risk: "Watch",
104
+ attempts: 1,
105
+ owner: "Jennifer Davis",
106
+ aging: "New 8h ago",
107
+ contactSignals: { phone: true, email: false, message: true },
108
+ },
109
+ {
110
+ id: "REF-1901",
111
+ patient: "Sarah Chen",
112
+ source: "CHLA",
113
+ specialty: "Pediatrics",
114
+ stage: "Contacted",
115
+ risk: "Stable",
116
+ attempts: 1,
117
+ owner: "Daniel Kim",
118
+ aging: "New today",
119
+ contactSignals: { phone: true, email: false, message: false },
120
+ },
121
+ {
122
+ id: "REF-1908",
123
+ patient: "David Smith",
124
+ source: "USC",
125
+ specialty: "Dermatology",
126
+ stage: "Contacted",
127
+ risk: "Stable",
128
+ attempts: 2,
129
+ owner: "Jennifer Davis",
130
+ aging: "New 3h ago",
131
+ contactSignals: { phone: false, email: true, message: true },
132
+ },
133
+ {
134
+ id: "REF-1913",
135
+ patient: "Olivia Martinez",
136
+ source: "Providence",
137
+ specialty: "Cardiology",
138
+ stage: "Contacted",
139
+ risk: "At Risk",
140
+ attempts: 3,
141
+ owner: "Jessica Wong",
142
+ aging: "Aging 24h",
143
+ contactSignals: { phone: true, email: true, message: false },
144
+ },
145
+ {
146
+ id: "REF-1919",
147
+ patient: "Kevin Nguyen",
148
+ source: "Kaiser",
149
+ specialty: "Nephrology",
150
+ stage: "Contacted",
151
+ risk: "Stable",
152
+ attempts: 1,
153
+ owner: "Sarah Johnson",
154
+ aging: "New 5h ago",
155
+ contactSignals: { phone: true, email: false, message: true },
156
+ },
157
+ {
158
+ id: "REF-1923",
159
+ patient: "Rachel Kim",
160
+ source: "UCLA Health",
161
+ specialty: "Hematology",
162
+ stage: "Contacted",
163
+ risk: "Watch",
164
+ attempts: 2,
165
+ owner: "Michael Chen",
166
+ aging: "New 7h ago",
167
+ contactSignals: { phone: false, email: true, message: true },
168
+ },
169
+ {
170
+ id: "REF-1926",
171
+ patient: "Brian Foster",
172
+ source: "Cedars",
173
+ specialty: "Vascular Surgery",
174
+ stage: "Contacted",
175
+ risk: "Stable",
176
+ attempts: 1,
177
+ owner: "Daniel Kim",
178
+ aging: "New today",
179
+ contactSignals: { phone: false, email: false, message: true },
180
+ },
181
+ {
182
+ id: "REF-1931",
183
+ patient: "Jasmine Patel",
184
+ source: "Dignity Health",
185
+ specialty: "Pulmonology",
186
+ stage: "Pending Intake",
187
+ risk: "Watch",
188
+ attempts: 2,
189
+ owner: "Sarah Johnson",
190
+ aging: "Aging 14h",
191
+ contactSignals: { phone: true, email: true, message: false },
192
+ },
193
+ {
194
+ id: "REF-1933",
195
+ patient: "Evan Torres",
196
+ source: "Sutter",
197
+ specialty: "Neurology",
198
+ stage: "Pending Intake",
199
+ risk: "Stable",
200
+ attempts: 1,
201
+ owner: "Jessica Wong",
202
+ aging: "New 2h ago",
203
+ contactSignals: { phone: false, email: true, message: true },
204
+ },
205
+ {
206
+ id: "REF-1940",
207
+ patient: "Natalie Brooks",
208
+ source: "MemorialCare",
209
+ specialty: "Pain Management",
210
+ stage: "Pending Scheduling",
211
+ risk: "Watch",
212
+ attempts: 2,
213
+ owner: "Jennifer Davis",
214
+ aging: "Aging 20h",
215
+ contactSignals: { phone: true, email: false, message: true },
216
+ },
217
+ {
218
+ id: "REF-1942",
219
+ patient: "Carlos Rivera",
220
+ source: "Providence",
221
+ specialty: "Urology",
222
+ stage: "Pending Scheduling",
223
+ risk: "Stable",
224
+ attempts: 1,
225
+ owner: "Daniel Kim",
226
+ aging: "New 5h ago",
227
+ contactSignals: { phone: false, email: true, message: false },
228
+ },
229
+ {
230
+ id: "REF-1946",
231
+ patient: "Alyssa Wright",
232
+ source: "UCLA Health",
233
+ specialty: "Referrals",
234
+ stage: "Referrals",
235
+ risk: "Stable",
236
+ attempts: 1,
237
+ owner: "Michael Chen",
238
+ aging: "New today",
239
+ contactSignals: { phone: false, email: true, message: true },
240
+ },
241
+ {
242
+ id: "REF-1948",
243
+ patient: "Marcus Lopez",
244
+ source: "Cedars",
245
+ specialty: "Referrals",
246
+ stage: "Referrals",
247
+ risk: "At Risk",
248
+ attempts: 4,
249
+ owner: "Jessica Wong",
250
+ aging: "Aging 30h",
251
+ contactSignals: { phone: true, email: true, message: false },
252
+ },
253
+ ]
254
+
255
+ const STAGE_ORDER: QueueStage[] = [
256
+ "Referrals",
257
+ "E&B Verified",
258
+ "Contacted",
259
+ "Pending Intake",
260
+ "Pending Scheduling",
261
+ ]
262
+
263
+ const RISK_ORDER: QueueRisk[] = ["At Risk", "Watch", "Stable"]
264
+
265
+ const FILTER_CATEGORIES: ItemListFilterCategory[] = [
266
+ {
267
+ id: "stage",
268
+ label: "Queue stage",
269
+ options: STAGE_ORDER,
270
+ },
271
+ {
272
+ id: "risk",
273
+ label: "Risk level",
274
+ options: RISK_ORDER,
275
+ },
276
+ {
277
+ id: "owner",
278
+ label: "Assignee",
279
+ options: [...new Set(QUEUE_ITEMS.map((item) => item.owner))],
280
+ },
281
+ ]
282
+
283
+ interface QuickViewDefinition {
284
+ id: string
285
+ label: string
286
+ matches: (item: QueueItem) => boolean
287
+ }
288
+
289
+ const QUICK_VIEW_DEFINITIONS: QuickViewDefinition[] = [
290
+ {
291
+ id: "referrals",
292
+ label: "Referrals",
293
+ matches: (item) => item.stage === "Referrals",
294
+ },
295
+ {
296
+ id: "contact-attempted",
297
+ label: "Contact Attempted",
298
+ matches: (item) =>
299
+ item.stage === "E&B Verified" || item.stage === "Contacted",
300
+ },
301
+ {
302
+ id: "pending-intake",
303
+ label: "Pending Intake",
304
+ matches: (item) => item.stage === "Pending Intake",
305
+ },
306
+ {
307
+ id: "pending-scheduling",
308
+ label: "Pending Scheduling",
309
+ matches: (item) => item.stage === "Pending Scheduling",
310
+ },
311
+ {
312
+ id: "needs-escalation",
313
+ label: "Needs Escalation",
314
+ matches: (item) => item.attempts >= 4 || item.risk === "At Risk",
315
+ },
316
+ ]
317
+
318
+ const DEFAULT_DISPLAY_STATE: ItemListDisplayState = {
319
+ viewMode: "list",
320
+ grouping: "stage",
321
+ subGrouping: "none",
322
+ ordering: "priority",
323
+ orderingDirection: "desc",
324
+ showContactSignals: true,
325
+ showOwner: true,
326
+ showStatus: true,
327
+ }
328
+
329
+ function toggleFilterValue(
330
+ current: Record<string, string[]>,
331
+ categoryId: string,
332
+ option: string,
333
+ ) {
334
+ const selected = current[categoryId] ?? []
335
+ const hasOption = selected.includes(option)
336
+
337
+ return {
338
+ ...current,
339
+ [categoryId]: hasOption
340
+ ? selected.filter((value) => value !== option)
341
+ : [...selected, option],
342
+ }
343
+ }
344
+
345
+ function matchesCategoryFilter(
346
+ item: QueueItem,
347
+ categoryId: string,
348
+ selectedOptions: string[],
349
+ ) {
350
+ if (selectedOptions.length === 0) {
351
+ return true
352
+ }
353
+
354
+ switch (categoryId) {
355
+ case "stage":
356
+ return selectedOptions.includes(item.stage)
357
+ case "risk":
358
+ return selectedOptions.includes(item.risk)
359
+ case "owner":
360
+ return selectedOptions.includes(item.owner)
361
+ default:
362
+ return true
363
+ }
364
+ }
365
+
366
+ function getRiskDotColor(risk: QueueRisk) {
367
+ switch (risk) {
368
+ case "At Risk":
369
+ return "bg-red-500"
370
+ case "Watch":
371
+ return "bg-orange-400"
372
+ case "Stable":
373
+ default:
374
+ return "bg-muted-foreground/30"
375
+ }
376
+ }
377
+
378
+ const AVATAR_COLORS = [
379
+ "bg-blue-100 text-blue-700",
380
+ "bg-emerald-100 text-emerald-700",
381
+ "bg-purple-100 text-purple-700",
382
+ "bg-amber-100 text-amber-700",
383
+ "bg-rose-100 text-rose-700",
384
+ "bg-cyan-100 text-cyan-700",
385
+ ]
386
+
387
+ function getAvatarColor(name: string) {
388
+ return AVATAR_COLORS[name.charCodeAt(0) % AVATAR_COLORS.length]
389
+ }
390
+
391
+ function WorkItemCard({ item, display }: { item: QueueItem; display: ItemListDisplayState }) {
392
+ return (
393
+ <div className="rounded-lg border border-border bg-card p-3 shadow-sm transition-shadow hover:shadow-md">
394
+ <div className="mb-2 flex items-center justify-between">
395
+ <span className="font-mono text-[10px] text-muted-foreground">{item.id}</span>
396
+ <div
397
+ className={cn(
398
+ "h-2 w-2 rounded-full",
399
+ getRiskDotColor(item.risk),
400
+ )}
401
+ />
402
+ </div>
403
+ <div className="mb-1 text-sm font-medium text-foreground">
404
+ {item.patient}
405
+ </div>
406
+ <div className="mb-2 text-xs text-muted-foreground">
407
+ via {item.source} &rarr; {item.specialty}
408
+ </div>
409
+ <div className="flex items-center justify-between">
410
+ <div className="flex items-center gap-1.5">
411
+ {item.risk === "At Risk" ? (
412
+ <Badge
413
+ variant="outline"
414
+ className="h-5 gap-1 rounded-full border-red-200 bg-red-50 px-1.5 text-[10px] font-medium text-red-700 shadow-none"
415
+ >
416
+ <AlertCircle className="h-3 w-3" />
417
+ At Risk
418
+ </Badge>
419
+ ) : null}
420
+ <span
421
+ className={cn(
422
+ "inline-flex h-5 items-center rounded border px-1.5 text-[10px] font-semibold",
423
+ item.attempts >= 4
424
+ ? "border-red-200 bg-red-50 text-red-700"
425
+ : item.attempts === 3
426
+ ? "border-amber-200 bg-amber-50 text-amber-700"
427
+ : "border-border bg-muted text-muted-foreground",
428
+ )}
429
+ >
430
+ {item.attempts >= 4 ? "4+" : item.attempts}
431
+ </span>
432
+ </div>
433
+ <span className="text-[11px] text-muted-foreground">{item.aging}</span>
434
+ </div>
435
+ {display.showOwner ? (
436
+ <div className="mt-2 flex items-center gap-1.5 border-t border-border/60 pt-2">
437
+ <div
438
+ className={cn(
439
+ "flex h-5 w-5 items-center justify-center rounded text-[9px] font-semibold",
440
+ getAvatarColor(item.owner),
441
+ )}
442
+ >
443
+ {item.owner.charAt(0)}
444
+ </div>
445
+ <span className="text-xs text-muted-foreground">{item.owner}</span>
446
+ </div>
447
+ ) : null}
448
+ </div>
449
+ )
450
+ }
451
+
452
+ export function ItemList() {
453
+ const [selectedFilters, setSelectedFilters] = React.useState<
454
+ Record<string, string[]>
455
+ >({})
456
+ const [activeQuickView, setActiveQuickView] = React.useState<string | null>(
457
+ "contact-attempted",
458
+ )
459
+ const [display, setDisplay] =
460
+ React.useState<ItemListDisplayState>(DEFAULT_DISPLAY_STATE)
461
+ const [expandedGroups, setExpandedGroups] = React.useState<
462
+ Record<string, boolean>
463
+ >({})
464
+
465
+ const quickViews = React.useMemo<ItemListQuickView[]>(
466
+ () =>
467
+ QUICK_VIEW_DEFINITIONS.map((definition) => ({
468
+ id: definition.id,
469
+ label: definition.label,
470
+ count: QUEUE_ITEMS.filter(definition.matches).length,
471
+ })),
472
+ [],
473
+ )
474
+
475
+ const filteredItems = React.useMemo(() => {
476
+ const quickViewFilter = QUICK_VIEW_DEFINITIONS.find(
477
+ (definition) => definition.id === activeQuickView,
478
+ )
479
+
480
+ return QUEUE_ITEMS.filter((item) => {
481
+ if (quickViewFilter && !quickViewFilter.matches(item)) {
482
+ return false
483
+ }
484
+
485
+ return Object.entries(selectedFilters).every(([categoryId, options]) =>
486
+ matchesCategoryFilter(item, categoryId, options),
487
+ )
488
+ })
489
+ }, [activeQuickView, selectedFilters])
490
+
491
+ const groupedItems = React.useMemo(() => {
492
+ const groups = new Map<string, QueueItem[]>()
493
+
494
+ for (const item of filteredItems) {
495
+ const key =
496
+ display.grouping === "stage"
497
+ ? item.stage
498
+ : display.grouping === "owner"
499
+ ? item.owner
500
+ : item.risk
501
+ const current = groups.get(key) ?? []
502
+ current.push(item)
503
+ groups.set(key, current)
504
+ }
505
+
506
+ const entries = Array.from(groups.entries())
507
+
508
+ entries.sort(([groupA], [groupB]) => {
509
+ if (display.grouping === "stage") {
510
+ return (
511
+ STAGE_ORDER.indexOf(groupA as QueueStage) -
512
+ STAGE_ORDER.indexOf(groupB as QueueStage)
513
+ )
514
+ }
515
+
516
+ if (display.grouping === "risk") {
517
+ return (
518
+ RISK_ORDER.indexOf(groupA as QueueRisk) -
519
+ RISK_ORDER.indexOf(groupB as QueueRisk)
520
+ )
521
+ }
522
+
523
+ return groupA.localeCompare(groupB)
524
+ })
525
+
526
+ return entries.map(([group, items]) => ({
527
+ key: group.toLowerCase().replace(/\s+/g, "-"),
528
+ label: group,
529
+ items,
530
+ }))
531
+ }, [display.grouping, filteredItems])
532
+
533
+ React.useEffect(() => {
534
+ setExpandedGroups((previous) => {
535
+ const next: Record<string, boolean> = {}
536
+ groupedItems.forEach((group) => {
537
+ next[group.key] = previous[group.key] ?? true
538
+ })
539
+ return next
540
+ })
541
+ }, [groupedItems])
542
+
543
+ return (
544
+ <div className="overflow-hidden bg-background">
545
+ <div className="border-b border-border px-6 py-4">
546
+ <h3 className="text-xl font-semibold tracking-tight text-foreground">
547
+ Patient Inventory
548
+ </h3>
549
+ </div>
550
+
551
+ <ItemListToolbar
552
+ quickViews={quickViews}
553
+ activeQuickView={activeQuickView}
554
+ onQuickViewChange={setActiveQuickView}
555
+ filterCategories={FILTER_CATEGORIES}
556
+ selectedFilters={selectedFilters}
557
+ onToggleFilter={(categoryId, option) =>
558
+ setSelectedFilters((previous) =>
559
+ toggleFilterValue(previous, categoryId, option),
560
+ )
561
+ }
562
+ onClearFilters={() => setSelectedFilters({})}
563
+ display={display}
564
+ onDisplayChange={setDisplay}
565
+ onResetDisplay={() => setDisplay(DEFAULT_DISPLAY_STATE)}
566
+ />
567
+
568
+ <div className="flex-1 overflow-auto">
569
+ {groupedItems.length > 0 ? (
570
+ display.viewMode === "board" ? (
571
+ /* Board View */
572
+ <div className="flex gap-4 overflow-x-auto p-4">
573
+ {groupedItems.map((group) => (
574
+ <div
575
+ key={group.key}
576
+ className="flex w-[280px] shrink-0 flex-col"
577
+ >
578
+ <div className="mb-3 flex items-center justify-between px-1">
579
+ <h4 className="truncate text-sm font-semibold uppercase text-muted-foreground">
580
+ {group.label}
581
+ </h4>
582
+ <span className="rounded-full bg-muted px-2 py-0.5 text-xs font-medium text-muted-foreground">
583
+ {group.items.length}
584
+ </span>
585
+ </div>
586
+ <div className="flex flex-col gap-3">
587
+ {group.items.map((item) => (
588
+ <WorkItemCard
589
+ key={item.id}
590
+ item={item}
591
+ display={display}
592
+ />
593
+ ))}
594
+ {group.items.length === 0 ? (
595
+ <div className="flex h-24 items-center justify-center rounded-lg border-2 border-dashed border-border bg-muted/30">
596
+ <span className="text-xs text-muted-foreground/50">
597
+ No items
598
+ </span>
599
+ </div>
600
+ ) : null}
601
+ </div>
602
+ </div>
603
+ ))}
604
+ </div>
605
+ ) : (
606
+ /* List View */
607
+ groupedItems.map((group) => {
608
+ const isExpanded = expandedGroups[group.key]
609
+
610
+ return (
611
+ <div
612
+ key={group.key}
613
+ className="last:border-b-0"
614
+ >
615
+ <button
616
+ type="button"
617
+ onClick={() =>
618
+ setExpandedGroups((previous) => ({
619
+ ...previous,
620
+ [group.key]: !previous[group.key],
621
+ }))
622
+ }
623
+ className="flex w-full cursor-pointer select-none items-center gap-2 border-b border-border bg-muted/30 px-4 py-2 text-left transition-colors hover:bg-muted/50"
624
+ >
625
+ <ChevronDown
626
+ className={cn(
627
+ "h-3.5 w-3.5 text-muted-foreground transition-transform",
628
+ !isExpanded && "-rotate-90",
629
+ )}
630
+ />
631
+ <span className="text-[11px] font-bold uppercase tracking-wider text-muted-foreground">
632
+ {group.label}
633
+ </span>
634
+ <span className="rounded border border-border bg-background px-1.5 py-0 text-[10px] font-medium text-muted-foreground">
635
+ {group.items.length}
636
+ </span>
637
+ </button>
638
+
639
+ {isExpanded ? (
640
+ <div>
641
+ {group.items.map((item) => (
642
+ <div
643
+ key={item.id}
644
+ className="group flex cursor-pointer items-center gap-3 border-b border-border/50 px-4 py-2.5 text-sm transition-colors hover:bg-muted/40"
645
+ >
646
+ <span className="w-[80px] shrink-0 font-mono text-xs text-muted-foreground/80">
647
+ {item.id}
648
+ </span>
649
+
650
+ <div className="shrink-0">
651
+ <div
652
+ className={cn(
653
+ "h-2 w-2 rounded-full",
654
+ getRiskDotColor(item.risk),
655
+ )}
656
+ />
657
+ </div>
658
+
659
+ <div className="min-w-0 flex-1 truncate font-semibold text-sm text-foreground">
660
+ {item.patient}{" "}
661
+ <span className="font-normal text-muted-foreground">
662
+ via {item.source} &rarr; {item.specialty}
663
+ </span>
664
+ </div>
665
+
666
+ <div className="hidden w-[80px] shrink-0 items-center md:flex">
667
+ {item.risk === "At Risk" ? (
668
+ <Badge
669
+ variant="outline"
670
+ className="h-5 gap-1 rounded-full border-red-200 bg-red-50 px-1.5 text-[10px] font-medium text-red-700 shadow-none"
671
+ >
672
+ <AlertCircle className="h-3 w-3" />
673
+ At Risk
674
+ </Badge>
675
+ ) : null}
676
+ </div>
677
+
678
+ {display.showContactSignals ? (
679
+ <div className="hidden items-center gap-3 text-muted-foreground/40 md:flex">
680
+ <Phone className="h-3.5 w-3.5 cursor-pointer transition-colors hover:text-foreground" />
681
+ <Mail className="h-3.5 w-3.5 cursor-pointer transition-colors hover:text-foreground" />
682
+ <MessageSquare className="h-3.5 w-3.5 cursor-pointer transition-colors hover:text-foreground" />
683
+ </div>
684
+ ) : null}
685
+
686
+ <div className="hidden w-[60px] shrink-0 items-center justify-center md:flex">
687
+ <span
688
+ className={cn(
689
+ "inline-flex h-5 items-center justify-center rounded border px-2 text-xs font-semibold",
690
+ item.attempts >= 4
691
+ ? "border-red-300 bg-red-100 text-red-700"
692
+ : item.attempts === 3
693
+ ? "border-amber-300 bg-amber-100 text-amber-700"
694
+ : "border-border bg-muted text-muted-foreground",
695
+ )}
696
+ >
697
+ {item.attempts >= 4 ? "4+" : item.attempts}
698
+ </span>
699
+ </div>
700
+
701
+ {display.showOwner ? (
702
+ <div className="hidden w-[120px] shrink-0 items-center gap-2 lg:flex">
703
+ <span className="truncate text-xs font-medium text-foreground">
704
+ {item.owner}
705
+ </span>
706
+ </div>
707
+ ) : null}
708
+
709
+ {display.showStatus ? (
710
+ <div className="hidden w-[140px] shrink-0 items-center justify-end gap-2 sm:flex">
711
+ <span className="truncate rounded bg-muted px-2 py-0.5 text-xs text-muted-foreground">
712
+ {item.stage}
713
+ </span>
714
+ </div>
715
+ ) : null}
716
+
717
+ <div className="w-[80px] shrink-0 text-right text-xs text-muted-foreground">
718
+ {item.aging}
719
+ </div>
720
+ </div>
721
+ ))}
722
+ </div>
723
+ ) : null}
724
+ </div>
725
+ )
726
+ })
727
+ )
728
+ ) : (
729
+ <div className="flex h-56 flex-col items-center justify-center gap-1 text-muted-foreground">
730
+ <SearchX className="h-7 w-7 opacity-40" />
731
+ <p className="text-sm font-medium">No queue items found</p>
732
+ <p className="text-xs">
733
+ Try a different quick view or clear your filters.
734
+ </p>
735
+ </div>
736
+ )}
737
+ </div>
738
+ </div>
739
+ )
740
+ }
741
+
742
+ // ---------------------------------------------------------------------------
743
+ // GroupedListView -- shared grouped row list used by ItemList and Inbox
744
+ // ---------------------------------------------------------------------------
745
+
746
+ export interface GroupedListGroup<T> {
747
+ key: string
748
+ label: string
749
+ items: T[]
750
+ }
751
+
752
+ export interface GroupedListViewProps<T> {
753
+ groups: GroupedListGroup<T>[]
754
+ renderRow: (item: T, index: number) => React.ReactNode
755
+ getItemKey: (item: T) => string
756
+ selectedKey?: string
757
+ onItemClick?: (item: T) => void
758
+ emptyMessage?: string
759
+ className?: string
760
+ }
761
+
762
+ export function GroupedListView<T>({
763
+ groups,
764
+ renderRow,
765
+ getItemKey,
766
+ selectedKey,
767
+ onItemClick,
768
+ emptyMessage = "No items found",
769
+ className,
770
+ }: GroupedListViewProps<T>) {
771
+ const [expandedGroups, setExpandedGroups] = React.useState<Record<string, boolean>>(() => {
772
+ const init: Record<string, boolean> = {}
773
+ groups.forEach((g) => { init[g.key] = true })
774
+ return init
775
+ })
776
+
777
+ React.useEffect(() => {
778
+ setExpandedGroups((prev) => {
779
+ const next: Record<string, boolean> = {}
780
+ groups.forEach((g) => { next[g.key] = prev[g.key] ?? true })
781
+ return next
782
+ })
783
+ }, [groups])
784
+
785
+ if (groups.length === 0 || groups.every((g) => g.items.length === 0)) {
786
+ return (
787
+ <div className={cn("flex h-56 flex-col items-center justify-center gap-1 text-muted-foreground", className)}>
788
+ <SearchX className="h-7 w-7 opacity-40" />
789
+ <p className="text-sm font-medium">{emptyMessage}</p>
790
+ </div>
791
+ )
792
+ }
793
+
794
+ return (
795
+ <div className={className}>
796
+ {groups.map((group) => {
797
+ const isExpanded = expandedGroups[group.key] ?? true
798
+ return (
799
+ <div key={group.key}>
800
+ <button
801
+ type="button"
802
+ onClick={() =>
803
+ setExpandedGroups((prev) => ({ ...prev, [group.key]: !prev[group.key] }))
804
+ }
805
+ className="flex w-full cursor-pointer select-none items-center gap-2 border-b border-border bg-muted/30 px-4 py-2 text-left transition-colors hover:bg-muted/50"
806
+ >
807
+ <ChevronDown
808
+ className={cn(
809
+ "h-3.5 w-3.5 text-muted-foreground transition-transform",
810
+ !isExpanded && "-rotate-90",
811
+ )}
812
+ />
813
+ <span className="text-[11px] font-bold uppercase tracking-wider text-muted-foreground">
814
+ {group.label}
815
+ </span>
816
+ <span className="rounded border border-border bg-background px-1.5 py-0 text-[10px] font-medium text-muted-foreground">
817
+ {group.items.length}
818
+ </span>
819
+ </button>
820
+ {isExpanded &&
821
+ group.items.map((item, idx) => {
822
+ const key = getItemKey(item)
823
+ return (
824
+ <div
825
+ key={key}
826
+ onClick={() => onItemClick?.(item)}
827
+ className={cn(
828
+ "flex items-center gap-3 px-4 py-2.5 border-b border-border/50 text-[13px] cursor-pointer transition-colors",
829
+ selectedKey === key
830
+ ? "bg-muted/30 border-l-2 border-l-primary"
831
+ : "border-l-2 border-l-transparent hover:bg-muted/40",
832
+ )}
833
+ >
834
+ {renderRow(item, idx)}
835
+ </div>
836
+ )
837
+ })}
838
+ </div>
839
+ )
840
+ })}
841
+ </div>
842
+ )
843
+ }