@wealthx/shadcn 1.0.2 → 1.2.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 (300) hide show
  1. package/.turbo/turbo-build.log +235 -138
  2. package/CHANGELOG.md +12 -0
  3. package/README.md +82 -0
  4. package/dist/{chunk-6OJF6XRN.mjs → chunk-24FUO7TD.mjs} +4 -8
  5. package/dist/{chunk-4AJ5HWHD.mjs → chunk-2I5S2AMY.mjs} +3 -3
  6. package/dist/chunk-2SF672SZ.mjs +161 -0
  7. package/dist/{chunk-GPRJQ24C.mjs → chunk-34NWQURD.mjs} +2 -2
  8. package/dist/{chunk-MQ72DIBH.mjs → chunk-3GF7OVTP.mjs} +14 -5
  9. package/dist/chunk-3WMX6KWS.mjs +245 -0
  10. package/dist/{chunk-PMKODV6M.mjs → chunk-462HMNO4.mjs} +6 -10
  11. package/dist/chunk-4CX4SBRO.mjs +153 -0
  12. package/dist/chunk-4MN6UQHG.mjs +443 -0
  13. package/dist/chunk-5QQVZTVZ.mjs +233 -0
  14. package/dist/{chunk-BGP2N52Z.mjs → chunk-66MI7Q4B.mjs} +5 -5
  15. package/dist/chunk-6FCGKSZX.mjs +268 -0
  16. package/dist/{chunk-CGOKTPXU.mjs → chunk-6JQFUE5I.mjs} +20 -23
  17. package/dist/{chunk-Z3MK2KKZ.mjs → chunk-7DHU4VGG.mjs} +7 -3
  18. package/dist/{chunk-VZ2NR7L3.mjs → chunk-7PYJD5JI.mjs} +35 -27
  19. package/dist/{chunk-JU2RUWHF.mjs → chunk-7XJHLGUV.mjs} +1 -1
  20. package/dist/{chunk-BMFN37JH.mjs → chunk-7YAU5CY6.mjs} +1 -1
  21. package/dist/chunk-A56YQQHG.mjs +402 -0
  22. package/dist/chunk-AH52LG6N.mjs +315 -0
  23. package/dist/{chunk-SLWCCURD.mjs → chunk-CLIN5525.mjs} +8 -4
  24. package/dist/{chunk-3VQNJ235.mjs → chunk-CSDO6VBW.mjs} +7 -0
  25. package/dist/chunk-D4ILTPOG.mjs +293 -0
  26. package/dist/{chunk-HS7TFG7V.mjs → chunk-D6ID6M4V.mjs} +1 -1
  27. package/dist/chunk-DOH3EHX7.mjs +378 -0
  28. package/dist/{chunk-MJIEMGRD.mjs → chunk-EFRENWEJ.mjs} +9 -17
  29. package/dist/chunk-ERGGHC2V.mjs +185 -0
  30. package/dist/{chunk-OXQQNQZI.mjs → chunk-FEZKMUCF.mjs} +10 -1
  31. package/dist/{chunk-55CEW76V.mjs → chunk-FH6QVUVZ.mjs} +1 -1
  32. package/dist/chunk-FMAXJ2SI.mjs +71 -0
  33. package/dist/chunk-FZIXGLMV.mjs +173 -0
  34. package/dist/{chunk-DS2AMHN2.mjs → chunk-GYMYRIZP.mjs} +2 -2
  35. package/dist/{chunk-KQDD5MU3.mjs → chunk-H45TKD34.mjs} +5 -5
  36. package/dist/{chunk-BBJBJSXQ.mjs → chunk-J5UICVJS.mjs} +1 -1
  37. package/dist/{chunk-RL772EH7.mjs → chunk-JHJHG4GO.mjs} +4 -12
  38. package/dist/chunk-KMCGSZTX.mjs +177 -0
  39. package/dist/{chunk-FHNT55I5.mjs → chunk-KUDCQ4FI.mjs} +4 -4
  40. package/dist/chunk-LE6YFY6D.mjs +209 -0
  41. package/dist/{chunk-HUVTPUV2.mjs → chunk-LLVQKSU3.mjs} +23 -19
  42. package/dist/{chunk-KKHTJNMM.mjs → chunk-MARPPFOJ.mjs} +8 -4
  43. package/dist/{chunk-6AFMNC42.mjs → chunk-N2PT566P.mjs} +15 -11
  44. package/dist/chunk-NLCKVHWB.mjs +161 -0
  45. package/dist/{chunk-YN5SYTOO.mjs → chunk-NQPOYKAQ.mjs} +9 -5
  46. package/dist/{chunk-ZZV5JVNW.mjs → chunk-NSLMILBT.mjs} +3 -7
  47. package/dist/chunk-NXA3CZ7A.mjs +248 -0
  48. package/dist/chunk-OGOYQ7BG.mjs +150 -0
  49. package/dist/{chunk-3NQGYJEZ.mjs → chunk-P6AM5V7O.mjs} +10 -18
  50. package/dist/{chunk-CZ3BW5GL.mjs → chunk-P76HMUI6.mjs} +5 -11
  51. package/dist/chunk-PCPLO5HT.mjs +671 -0
  52. package/dist/chunk-PG6K5XEC.mjs +475 -0
  53. package/dist/chunk-PJHPSRYD.mjs +234 -0
  54. package/dist/{chunk-DDPA2XXS.mjs → chunk-PMB3A7V3.mjs} +2 -2
  55. package/dist/chunk-PR6V5XKM.mjs +209 -0
  56. package/dist/{chunk-46OFHMQA.mjs → chunk-Q76O3RIQ.mjs} +10 -6
  57. package/dist/chunk-QVKWW6KE.mjs +272 -0
  58. package/dist/chunk-RGU7HOEC.mjs +140 -0
  59. package/dist/{chunk-JF4PHPD5.mjs → chunk-RGVKLTLH.mjs} +4 -4
  60. package/dist/{chunk-VG6UF6UT.mjs → chunk-RP3SQYA3.mjs} +2 -2
  61. package/dist/chunk-RRBS6D63.mjs +163 -0
  62. package/dist/chunk-SMQ3DG25.mjs +285 -0
  63. package/dist/chunk-SPJ5KXW7.mjs +199 -0
  64. package/dist/chunk-SYOD63OZ.mjs +225 -0
  65. package/dist/chunk-UFYSFDER.mjs +42 -0
  66. package/dist/chunk-VACKZOMY.mjs +190 -0
  67. package/dist/chunk-VLQZANBF.mjs +42 -0
  68. package/dist/chunk-WA6O6EUR.mjs +1885 -0
  69. package/dist/{chunk-E3K6O4FZ.mjs → chunk-WAZD7NFU.mjs} +5 -2
  70. package/dist/chunk-WG6JGJXB.mjs +165 -0
  71. package/dist/{chunk-I64K754C.mjs → chunk-WNGWBVLV.mjs} +2 -2
  72. package/dist/{chunk-3U7SD3MS.mjs → chunk-WOEHFRGB.mjs} +3 -3
  73. package/dist/{chunk-DKZRJOMF.mjs → chunk-XIRTEFKH.mjs} +12 -12
  74. package/dist/chunk-Y6DWJSKZ.mjs +79 -0
  75. package/dist/chunk-YKPROFLB.mjs +161 -0
  76. package/dist/{chunk-K76E2TQU.mjs → chunk-ZRO5JO3H.mjs} +107 -67
  77. package/dist/{chunk-VYMHBV6D.mjs → chunk-ZU4NV6RG.mjs} +5 -3
  78. package/dist/components/ui/accordion.js +40 -4
  79. package/dist/components/ui/accordion.mjs +2 -2
  80. package/dist/components/ui/add-column-modal.js +789 -0
  81. package/dist/components/ui/add-column-modal.mjs +17 -0
  82. package/dist/components/ui/add-lead-modal.js +647 -0
  83. package/dist/components/ui/add-lead-modal.mjs +16 -0
  84. package/dist/components/ui/ai-assistant-drawer.js +686 -0
  85. package/dist/components/ui/ai-assistant-drawer.mjs +16 -0
  86. package/dist/components/ui/alert-dialog.js +37 -5
  87. package/dist/components/ui/alert-dialog.mjs +4 -4
  88. package/dist/components/ui/alert.js +37 -11
  89. package/dist/components/ui/alert.mjs +2 -2
  90. package/dist/components/ui/avatar.js +36 -8
  91. package/dist/components/ui/avatar.mjs +2 -2
  92. package/dist/components/ui/backoffice-alert-history-chart.js +624 -0
  93. package/dist/components/ui/backoffice-alert-history-chart.mjs +16 -0
  94. package/dist/components/ui/backoffice-contact-history-chart.js +687 -0
  95. package/dist/components/ui/backoffice-contact-history-chart.mjs +16 -0
  96. package/dist/components/ui/badge.js +37 -2
  97. package/dist/components/ui/badge.mjs +2 -2
  98. package/dist/components/ui/borrowing-capacity-line-chart.js +639 -0
  99. package/dist/components/ui/borrowing-capacity-line-chart.mjs +16 -0
  100. package/dist/components/ui/button.js +35 -3
  101. package/dist/components/ui/button.mjs +2 -2
  102. package/dist/components/ui/calendar.js +43 -19
  103. package/dist/components/ui/calendar.mjs +3 -3
  104. package/dist/components/ui/card.js +40 -4
  105. package/dist/components/ui/card.mjs +2 -2
  106. package/dist/components/ui/cash-balance-line-chart.js +627 -0
  107. package/dist/components/ui/cash-balance-line-chart.mjs +16 -0
  108. package/dist/components/ui/cashflow-bar-chart.js +650 -0
  109. package/dist/components/ui/cashflow-bar-chart.mjs +16 -0
  110. package/dist/components/ui/checkbox.js +36 -5
  111. package/dist/components/ui/checkbox.mjs +2 -3
  112. package/dist/components/ui/chip.js +37 -2
  113. package/dist/components/ui/chip.mjs +3 -3
  114. package/dist/components/ui/combobox.js +280 -0
  115. package/dist/components/ui/combobox.mjs +28 -0
  116. package/dist/components/ui/data-table.js +160 -88
  117. package/dist/components/ui/data-table.mjs +10 -11
  118. package/dist/components/ui/date-picker.js +44 -20
  119. package/dist/components/ui/date-picker.mjs +6 -7
  120. package/dist/components/ui/dialog.js +44 -12
  121. package/dist/components/ui/dialog.mjs +4 -4
  122. package/dist/components/ui/drawer.js +46 -10
  123. package/dist/components/ui/drawer.mjs +3 -3
  124. package/dist/components/ui/dropdown-menu.js +40 -16
  125. package/dist/components/ui/dropdown-menu.mjs +3 -3
  126. package/dist/components/ui/empty.js +41 -5
  127. package/dist/components/ui/empty.mjs +2 -2
  128. package/dist/components/ui/expense-bar-chart.js +642 -0
  129. package/dist/components/ui/expense-bar-chart.mjs +16 -0
  130. package/dist/components/ui/field.js +53 -21
  131. package/dist/components/ui/field.mjs +4 -4
  132. package/dist/components/ui/financial-cards.js +1002 -0
  133. package/dist/components/ui/financial-cards.mjs +24 -0
  134. package/dist/components/ui/financial-drawers.js +637 -0
  135. package/dist/components/ui/financial-drawers.mjs +17 -0
  136. package/dist/components/ui/financial-primitives.js +218 -0
  137. package/dist/components/ui/financial-primitives.mjs +22 -0
  138. package/dist/components/ui/financial-sections.js +1422 -0
  139. package/dist/components/ui/financial-sections.mjs +30 -0
  140. package/dist/components/ui/form-primitives.js +682 -0
  141. package/dist/components/ui/form-primitives.mjs +19 -0
  142. package/dist/components/ui/income-bar-chart.js +641 -0
  143. package/dist/components/ui/income-bar-chart.mjs +16 -0
  144. package/dist/components/ui/input-group.js +43 -7
  145. package/dist/components/ui/input-group.mjs +5 -5
  146. package/dist/components/ui/input-otp.js +39 -3
  147. package/dist/components/ui/input-otp.mjs +2 -2
  148. package/dist/components/ui/input.js +34 -2
  149. package/dist/components/ui/input.mjs +2 -2
  150. package/dist/components/ui/kanban-column.js +1143 -0
  151. package/dist/components/ui/kanban-column.mjs +20 -0
  152. package/dist/components/ui/label.js +35 -7
  153. package/dist/components/ui/label.mjs +2 -2
  154. package/dist/components/ui/opportunity-card.js +960 -0
  155. package/dist/components/ui/opportunity-card.mjs +20 -0
  156. package/dist/components/ui/opportunity-edit-modals.js +3360 -0
  157. package/dist/components/ui/opportunity-edit-modals.mjs +37 -0
  158. package/dist/components/ui/opportunity-summary-tab.js +4365 -0
  159. package/dist/components/ui/opportunity-summary-tab.mjs +34 -0
  160. package/dist/components/ui/pagination.js +35 -3
  161. package/dist/components/ui/pagination.mjs +3 -3
  162. package/dist/components/ui/pipeline-alerts.js +103 -0
  163. package/dist/components/ui/pipeline-alerts.mjs +8 -0
  164. package/dist/components/ui/pipeline-board.js +1408 -0
  165. package/dist/components/ui/pipeline-board.mjs +24 -0
  166. package/dist/components/ui/pipeline-chart.js +216 -0
  167. package/dist/components/ui/pipeline-chart.mjs +10 -0
  168. package/dist/components/ui/pipeline-dialogs.js +1183 -0
  169. package/dist/components/ui/pipeline-dialogs.mjs +23 -0
  170. package/dist/components/ui/pipeline-primitives.js +300 -0
  171. package/dist/components/ui/pipeline-primitives.mjs +11 -0
  172. package/dist/components/ui/popover.js +45 -4
  173. package/dist/components/ui/popover.mjs +3 -3
  174. package/dist/components/ui/progress.js +33 -1
  175. package/dist/components/ui/progress.mjs +2 -2
  176. package/dist/components/ui/property-cashflow-doughnut-chart.js +523 -0
  177. package/dist/components/ui/property-cashflow-doughnut-chart.mjs +16 -0
  178. package/dist/components/ui/property-debt-equity-doughnut-chart.js +521 -0
  179. package/dist/components/ui/property-debt-equity-doughnut-chart.mjs +16 -0
  180. package/dist/components/ui/property-mobile-estimate-line-chart.js +682 -0
  181. package/dist/components/ui/property-mobile-estimate-line-chart.mjs +16 -0
  182. package/dist/components/ui/radio-group.js +33 -1
  183. package/dist/components/ui/radio-group.mjs +2 -2
  184. package/dist/components/ui/select.js +66 -26
  185. package/dist/components/ui/select.mjs +3 -3
  186. package/dist/components/ui/separator.js +33 -1
  187. package/dist/components/ui/separator.mjs +2 -2
  188. package/dist/components/ui/sheet.js +37 -9
  189. package/dist/components/ui/sheet.mjs +3 -3
  190. package/dist/components/ui/skeleton.js +33 -1
  191. package/dist/components/ui/skeleton.mjs +2 -2
  192. package/dist/components/ui/slider.js +86 -102
  193. package/dist/components/ui/slider.mjs +2 -2
  194. package/dist/components/ui/spinner.js +33 -1
  195. package/dist/components/ui/spinner.mjs +2 -2
  196. package/dist/components/ui/stage-timeline.js +579 -0
  197. package/dist/components/ui/stage-timeline.mjs +15 -0
  198. package/dist/components/ui/switch.js +37 -4
  199. package/dist/components/ui/switch.mjs +2 -3
  200. package/dist/components/ui/table.js +37 -5
  201. package/dist/components/ui/table.mjs +2 -2
  202. package/dist/components/ui/tabs.js +36 -12
  203. package/dist/components/ui/tabs.mjs +2 -2
  204. package/dist/components/ui/textarea.js +34 -2
  205. package/dist/components/ui/textarea.mjs +2 -2
  206. package/dist/components/ui/toggle-group.js +35 -4
  207. package/dist/components/ui/toggle-group.mjs +3 -4
  208. package/dist/components/ui/toggle.js +35 -4
  209. package/dist/components/ui/toggle.mjs +2 -3
  210. package/dist/components/ui/tooltip.js +51 -22
  211. package/dist/components/ui/tooltip.mjs +3 -3
  212. package/dist/components/ui/transactions-expense-categories-doughnut-chart.js +528 -0
  213. package/dist/components/ui/transactions-expense-categories-doughnut-chart.mjs +16 -0
  214. package/dist/components/ui/transactions-income-expense-bar-chart.js +516 -0
  215. package/dist/components/ui/transactions-income-expense-bar-chart.mjs +16 -0
  216. package/dist/components/ui/transactions-liabilities-breakdown-doughnut-chart.js +528 -0
  217. package/dist/components/ui/transactions-liabilities-breakdown-doughnut-chart.mjs +16 -0
  218. package/dist/index.js +11613 -2868
  219. package/dist/index.mjs +377 -164
  220. package/dist/lib/theme-provider.js +10 -1
  221. package/dist/lib/theme-provider.mjs +1 -1
  222. package/dist/lib/typography.js +8 -0
  223. package/dist/lib/typography.mjs +3 -1
  224. package/dist/lib/utils.js +33 -1
  225. package/dist/lib/utils.mjs +1 -1
  226. package/dist/styles.css +1 -1
  227. package/package.json +169 -6
  228. package/src/components/index.tsx +323 -13
  229. package/src/components/ui/accordion.tsx +6 -3
  230. package/src/components/ui/add-column-modal.tsx +339 -0
  231. package/src/components/ui/add-lead-modal.tsx +290 -0
  232. package/src/components/ui/ai-assistant-drawer.tsx +408 -0
  233. package/src/components/ui/alert-dialog.tsx +80 -54
  234. package/src/components/ui/alert.tsx +28 -28
  235. package/src/components/ui/avatar.tsx +30 -29
  236. package/src/components/ui/backoffice-alert-history-chart.tsx +260 -0
  237. package/src/components/ui/backoffice-contact-history-chart.tsx +325 -0
  238. package/src/components/ui/badge.tsx +17 -15
  239. package/src/components/ui/borrowing-capacity-line-chart.tsx +357 -0
  240. package/src/components/ui/button.tsx +30 -27
  241. package/src/components/ui/calendar.tsx +53 -67
  242. package/src/components/ui/card.tsx +27 -24
  243. package/src/components/ui/cash-balance-line-chart.tsx +302 -0
  244. package/src/components/ui/cashflow-bar-chart.tsx +363 -0
  245. package/src/components/ui/chart-shared.tsx +261 -0
  246. package/src/components/ui/checkbox.tsx +30 -26
  247. package/src/components/ui/combobox.tsx +223 -0
  248. package/src/components/ui/data-table.tsx +160 -99
  249. package/src/components/ui/date-picker.tsx +0 -2
  250. package/src/components/ui/dialog.tsx +70 -60
  251. package/src/components/ui/drawer.tsx +57 -48
  252. package/src/components/ui/dropdown-menu.tsx +90 -82
  253. package/src/components/ui/empty.tsx +31 -27
  254. package/src/components/ui/expense-bar-chart.tsx +296 -0
  255. package/src/components/ui/field.tsx +70 -62
  256. package/src/components/ui/financial-cards.tsx +830 -0
  257. package/src/components/ui/financial-drawers.tsx +339 -0
  258. package/src/components/ui/financial-primitives.tsx +331 -0
  259. package/src/components/ui/financial-sections.tsx +672 -0
  260. package/src/components/ui/form-primitives.tsx +536 -0
  261. package/src/components/ui/income-bar-chart.tsx +297 -0
  262. package/src/components/ui/input-group.tsx +41 -34
  263. package/src/components/ui/input-otp.tsx +29 -24
  264. package/src/components/ui/input.tsx +8 -8
  265. package/src/components/ui/kanban-column.tsx +333 -0
  266. package/src/components/ui/label.tsx +9 -12
  267. package/src/components/ui/opportunity-card.tsx +616 -0
  268. package/src/components/ui/opportunity-edit-modals.tsx +2528 -0
  269. package/src/components/ui/opportunity-summary-tab.tsx +579 -0
  270. package/src/components/ui/pipeline-alerts.tsx +74 -0
  271. package/src/components/ui/pipeline-board.tsx +268 -0
  272. package/src/components/ui/pipeline-chart.tsx +173 -0
  273. package/src/components/ui/pipeline-dialogs.tsx +303 -0
  274. package/src/components/ui/pipeline-primitives.tsx +108 -0
  275. package/src/components/ui/popover.tsx +41 -36
  276. package/src/components/ui/property-cashflow-doughnut-chart.tsx +188 -0
  277. package/src/components/ui/property-debt-equity-doughnut-chart.tsx +185 -0
  278. package/src/components/ui/property-mobile-estimate-line-chart.tsx +393 -0
  279. package/src/components/ui/select.tsx +65 -52
  280. package/src/components/ui/sheet.tsx +55 -52
  281. package/src/components/ui/slider.tsx +54 -77
  282. package/src/components/ui/stage-timeline.tsx +205 -0
  283. package/src/components/ui/switch.tsx +42 -29
  284. package/src/components/ui/table.tsx +28 -28
  285. package/src/components/ui/tabs.tsx +22 -28
  286. package/src/components/ui/textarea.tsx +8 -8
  287. package/src/components/ui/toggle-group.tsx +0 -2
  288. package/src/components/ui/toggle.tsx +13 -15
  289. package/src/components/ui/tooltip.tsx +30 -28
  290. package/src/components/ui/transactions-expense-categories-doughnut-chart.tsx +191 -0
  291. package/src/components/ui/transactions-income-expense-bar-chart.tsx +205 -0
  292. package/src/components/ui/transactions-liabilities-breakdown-doughnut-chart.tsx +191 -0
  293. package/src/lib/theme-provider.tsx +10 -0
  294. package/src/lib/typography.ts +9 -0
  295. package/src/lib/utils.ts +41 -3
  296. package/src/styles/globals.css +371 -124
  297. package/src/styles/styles-css.ts +1 -1
  298. package/tsup.config.ts +32 -0
  299. package/dist/chunk-K74JRTJR.mjs +0 -105
  300. package/dist/chunk-V7CNWJT3.mjs +0 -10
@@ -0,0 +1,333 @@
1
+ import * as React from "react";
2
+ import { MoreVertical } from "lucide-react";
3
+ import { cn } from "@/lib/utils";
4
+ import { useThemeVars } from "@/lib/theme-provider";
5
+ import { Badge } from "@/components/ui/badge";
6
+ import { buttonVariants } from "@/components/ui/button";
7
+ import { Spinner } from "@/components/ui/spinner";
8
+ import {
9
+ DropdownMenu,
10
+ DropdownMenuContent,
11
+ DropdownMenuItem,
12
+ DropdownMenuSeparator,
13
+ DropdownMenuTrigger,
14
+ } from "@/components/ui/dropdown-menu";
15
+ import { LeadCard, OpportunityCard } from "@/components/ui/opportunity-card";
16
+ import type { OpportunityCardProps } from "@/components/ui/opportunity-card";
17
+
18
+ /**
19
+ * KanbanColumn — WealthX DS (L4 Section)
20
+ *
21
+ * Kanban column wrapper for the Pipeline board.
22
+ * Renders the stage header (name, count, total value, growth chip) and
23
+ * a scrollable list of OpportunityCard items.
24
+ *
25
+ * **Does NOT include:**
26
+ * - Drag-and-drop (react-dnd) — handled by the app's KanbanBoard wrapper.
27
+ * - Infinite scroll logic — the app passes `loaderRef` and manages IntersectionObserver.
28
+ *
29
+ * Data source: `Stage` from `loan-crm.ts` via `useInfiniteColumnOpportunities`.
30
+ */
31
+
32
+ // ---------------------------------------------------------------------------
33
+ // Types
34
+ // ---------------------------------------------------------------------------
35
+
36
+ export interface KanbanColumnStage {
37
+ id: string;
38
+ name: string;
39
+ /** Number of opportunities currently in this stage. */
40
+ count: number;
41
+ /** Total loan value across all opportunities in this stage. */
42
+ totalValue: number;
43
+ /**
44
+ * Growth metric for the column (e.g. new opps this week).
45
+ * Positive → success color; negative/zero → destructive color.
46
+ */
47
+ growth?: number | null;
48
+ /**
49
+ * Optional top-border accent color.
50
+ * Pass a CSS variable string: `"var(--color-destructive)"`.
51
+ * Defaults to `"var(--color-border)"`.
52
+ */
53
+ accentColor?: string | null;
54
+ }
55
+
56
+ export interface KanbanColumnProps {
57
+ // ── Column data ──────────────────────────────────────────────
58
+ stage: KanbanColumnStage;
59
+ /** Opportunity cards to render in this column. */
60
+ opportunities: OpportunityCardProps[];
61
+
62
+ // ── Column visual states (controlled by parent DnD) ─────────
63
+ /** Whether this column is currently being dragged. Reduces opacity. */
64
+ isDragging?: boolean;
65
+ /**
66
+ * Whether a card is hovering over this column and can be dropped.
67
+ * Shows a drop-target highlight.
68
+ */
69
+ isDropTarget?: boolean;
70
+ /**
71
+ * Whether this is a fixed/system column (Leads, On Hold, High Priority).
72
+ * Fixed columns show no Delete option in the menu.
73
+ */
74
+ isDefault?: boolean;
75
+
76
+ // ── Async / loading state ────────────────────────────────────
77
+ /** Shows a full-column skeleton/spinner on initial load. */
78
+ isLoading?: boolean;
79
+ /** Shows a small spinner at the bottom while fetching the next page. */
80
+ isLoadingMore?: boolean;
81
+ /** Whether there are more opportunities to load. */
82
+ hasMore?: boolean;
83
+ /**
84
+ * Ref for the loader sentinel element at the column bottom.
85
+ * Attach an IntersectionObserver in the app to call `fetchNextPage()` when visible.
86
+ */
87
+ loaderRef?: React.Ref<HTMLDivElement>;
88
+
89
+ // ── Column actions ───────────────────────────────────────────
90
+ onEditColumn?: () => void;
91
+ /** Only shown for non-default columns. */
92
+ onDeleteColumn?: () => void;
93
+
94
+ // ── Card callbacks (forwarded to each OpportunityCard) ───────
95
+ onTaskToggle?: (opportunityId: string, taskId: string) => void;
96
+ onMarkAsDone?: (opportunityId: string) => void;
97
+ onMoveToNextStage?: (opportunityId: string) => void;
98
+ onSendLoanApplication?: (opportunityId: string) => void;
99
+ onViewDetails?: (opportunityId: string) => void;
100
+ onChangePriority?: (opportunityId: string) => void;
101
+ onLaunchAssistant?: (opportunityId: string) => void;
102
+ onPutOnHold?: (opportunityId: string) => void;
103
+ onDeleteOpportunity?: (opportunityId: string) => void;
104
+ /** Opportunity ID currently being submitted — disables that card's actions. */
105
+ submittingOpportunityId?: string | null;
106
+
107
+ className?: string;
108
+ }
109
+
110
+ // ---------------------------------------------------------------------------
111
+ // Helpers
112
+ // ---------------------------------------------------------------------------
113
+
114
+ function formatTotalValue(value: number): string {
115
+ return new Intl.NumberFormat("en-AU", {
116
+ style: "currency",
117
+ currency: "AUD",
118
+ maximumFractionDigits: 0,
119
+ }).format(value);
120
+ }
121
+
122
+ function growthColor(growth: number): string {
123
+ // Use darkened text tokens — full-saturation tokens (#4CAF50, #F44336) fail 4.5:1 on white bg
124
+ return growth > 0
125
+ ? "var(--color-success-text)"
126
+ : "var(--color-destructive-text)";
127
+ }
128
+
129
+ // ---------------------------------------------------------------------------
130
+ // KanbanColumn
131
+ // ---------------------------------------------------------------------------
132
+
133
+ export function KanbanColumn({
134
+ stage,
135
+ opportunities,
136
+ isDragging = false,
137
+ isDropTarget = false,
138
+ isDefault = false,
139
+ isLoading = false,
140
+ isLoadingMore = false,
141
+ hasMore = false,
142
+ loaderRef,
143
+ onEditColumn,
144
+ onDeleteColumn,
145
+ onTaskToggle,
146
+ onMarkAsDone,
147
+ onMoveToNextStage,
148
+ onSendLoanApplication,
149
+ onViewDetails,
150
+ onChangePriority,
151
+ onLaunchAssistant,
152
+ onPutOnHold,
153
+ onDeleteOpportunity,
154
+ submittingOpportunityId,
155
+ className,
156
+ }: KanbanColumnProps) {
157
+ const themeVars = useThemeVars();
158
+ const accentColor = stage.accentColor ?? "var(--color-border)";
159
+ const hasMenu = onEditColumn || (!isDefault && onDeleteColumn);
160
+
161
+ return (
162
+ <div
163
+ className={cn(
164
+ "flex w-[292px] shrink-0 flex-col bg-muted/40",
165
+ isDragging && "opacity-50",
166
+ isDropTarget && "ring-2 ring-primary ring-inset",
167
+ className,
168
+ )}
169
+ style={{
170
+ ...(themeVars as React.CSSProperties),
171
+ borderTop: `2px solid ${accentColor}`,
172
+ }}
173
+ data-slot="kanban-column"
174
+ >
175
+ {/* ── Header ── */}
176
+ <div className="flex flex-col gap-2 border-b border-border bg-background px-3 py-3">
177
+ {/* Title row */}
178
+ <div className="flex items-center justify-between gap-2">
179
+ <h2 className="text-sm font-semibold text-foreground">
180
+ <span className="text-muted-foreground">{stage.count}</span>{" "}
181
+ {stage.name}
182
+ </h2>
183
+
184
+ {hasMenu && (
185
+ <DropdownMenu>
186
+ <DropdownMenuTrigger
187
+ className={cn(
188
+ buttonVariants({ variant: "ghost", size: "icon" }),
189
+ "-mr-1 size-7 shrink-0",
190
+ )}
191
+ aria-label="Column actions"
192
+ >
193
+ <MoreVertical className="size-4" />
194
+ </DropdownMenuTrigger>
195
+ <DropdownMenuContent align="end">
196
+ {onEditColumn && (
197
+ <DropdownMenuItem onClick={onEditColumn}>
198
+ Edit column settings
199
+ </DropdownMenuItem>
200
+ )}
201
+ {!isDefault && onDeleteColumn && (
202
+ <>
203
+ {onEditColumn && <DropdownMenuSeparator />}
204
+ <DropdownMenuItem
205
+ onClick={onDeleteColumn}
206
+ className="text-destructive focus:text-destructive"
207
+ >
208
+ Delete column
209
+ </DropdownMenuItem>
210
+ </>
211
+ )}
212
+ </DropdownMenuContent>
213
+ </DropdownMenu>
214
+ )}
215
+ </div>
216
+
217
+ {/* Stats row */}
218
+ <div className="flex items-center justify-between gap-2">
219
+ {stage.growth != null ? (
220
+ <Badge
221
+ variant="outline"
222
+ style={{ color: growthColor(stage.growth) }}
223
+ >
224
+ {stage.growth > 0 ? "+" : ""}
225
+ {stage.growth}
226
+ </Badge>
227
+ ) : (
228
+ <span />
229
+ )}
230
+ <span className="text-xs font-medium tabular-nums text-muted-foreground">
231
+ {formatTotalValue(stage.totalValue)}
232
+ </span>
233
+ </div>
234
+ </div>
235
+
236
+ {/* ── Body ── */}
237
+ <div className="flex flex-1 flex-col gap-2 overflow-y-auto p-2">
238
+ {/* Drop target hint — shown at top when a card is dragged over */}
239
+ {isDropTarget && (
240
+ <div className="border border-dashed border-primary/40 bg-primary/5 px-3 py-2 text-center text-xs text-primary">
241
+ Drop here → {stage.name}
242
+ </div>
243
+ )}
244
+
245
+ {isLoading ? (
246
+ <div className="flex flex-1 items-center justify-center py-8">
247
+ <Spinner className="size-5 text-muted-foreground" />
248
+ </div>
249
+ ) : opportunities.length === 0 ? (
250
+ <div className="flex flex-1 items-center justify-center py-8">
251
+ <p className="text-xs text-muted-foreground">
252
+ No opportunities in this stage
253
+ </p>
254
+ </div>
255
+ ) : (
256
+ <>
257
+ {opportunities.map((opp) =>
258
+ onSendLoanApplication ? (
259
+ <LeadCard
260
+ key={opp.id}
261
+ id={opp.id}
262
+ customerName={opp.customerName}
263
+ customerPhone={opp.customerPhone}
264
+ customerEmail={opp.customerEmail}
265
+ loanApplicationUrl={opp.loanApplicationUrl}
266
+ onSendLoanApplication={() => onSendLoanApplication(opp.id)}
267
+ onDelete={
268
+ onDeleteOpportunity
269
+ ? () => onDeleteOpportunity(opp.id)
270
+ : undefined
271
+ }
272
+ isSubmitting={submittingOpportunityId === opp.id}
273
+ />
274
+ ) : (
275
+ <OpportunityCard
276
+ key={opp.id}
277
+ {...opp}
278
+ onTaskToggle={
279
+ onTaskToggle
280
+ ? (taskId) => onTaskToggle(opp.id, taskId)
281
+ : undefined
282
+ }
283
+ onMarkAsDone={
284
+ onMarkAsDone ? () => onMarkAsDone(opp.id) : undefined
285
+ }
286
+ onMoveToNextStage={
287
+ onMoveToNextStage
288
+ ? () => onMoveToNextStage(opp.id)
289
+ : undefined
290
+ }
291
+ onViewDetails={
292
+ onViewDetails ? () => onViewDetails(opp.id) : undefined
293
+ }
294
+ onChangePriority={
295
+ onChangePriority
296
+ ? () => onChangePriority(opp.id)
297
+ : undefined
298
+ }
299
+ onLaunchAssistant={
300
+ onLaunchAssistant
301
+ ? () => onLaunchAssistant(opp.id)
302
+ : undefined
303
+ }
304
+ onPutOnHold={
305
+ onPutOnHold ? () => onPutOnHold(opp.id) : undefined
306
+ }
307
+ onDelete={
308
+ onDeleteOpportunity
309
+ ? () => onDeleteOpportunity(opp.id)
310
+ : undefined
311
+ }
312
+ isSubmitting={submittingOpportunityId === opp.id}
313
+ />
314
+ ),
315
+ )}
316
+
317
+ {/* Infinite-scroll loader sentinel */}
318
+ {hasMore && (
319
+ <div
320
+ ref={loaderRef}
321
+ className="flex min-h-[50px] items-center justify-center"
322
+ >
323
+ {isLoadingMore && (
324
+ <Spinner className="size-5 text-muted-foreground" />
325
+ )}
326
+ </div>
327
+ )}
328
+ </>
329
+ )}
330
+ </div>
331
+ </div>
332
+ );
333
+ }
@@ -1,24 +1,21 @@
1
- import { type ReactElement } from "react"
2
- import * as React from "react"
3
- import { cn } from "@/lib/utils"
1
+ import { type ReactElement } from "react";
2
+ import * as React from "react";
3
+ import { cn } from "@/lib/utils";
4
4
 
5
- export type LabelProps = React.ComponentProps<"label">
5
+ export type LabelProps = React.ComponentProps<"label">;
6
6
 
7
- function Label({
8
- className,
9
- ...props
10
- }: LabelProps): ReactElement {
7
+ function Label({ className, ...props }: LabelProps): ReactElement {
11
8
  return (
12
9
  // eslint-disable-next-line jsx-a11y/label-has-associated-control -- htmlFor is passed by the consumer
13
10
  <label
14
11
  className={cn(
15
- "flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
16
- className
12
+ "flex items-center gap-2 text-label-medium leading-none select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
13
+ className,
17
14
  )}
18
15
  data-slot="label"
19
16
  {...props}
20
17
  />
21
- )
18
+ );
22
19
  }
23
20
 
24
- export { Label }
21
+ export { Label };