@wealthx/shadcn 1.1.0 → 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 -154
  2. package/CHANGELOG.md +6 -0
  3. package/dist/{chunk-6OJF6XRN.mjs → chunk-24FUO7TD.mjs} +4 -8
  4. package/dist/{chunk-4AJ5HWHD.mjs → chunk-2I5S2AMY.mjs} +3 -3
  5. package/dist/chunk-2SF672SZ.mjs +161 -0
  6. package/dist/{chunk-GPRJQ24C.mjs → chunk-34NWQURD.mjs} +2 -2
  7. package/dist/{chunk-MQ72DIBH.mjs → chunk-3GF7OVTP.mjs} +14 -5
  8. package/dist/chunk-3WMX6KWS.mjs +245 -0
  9. package/dist/{chunk-PMKODV6M.mjs → chunk-462HMNO4.mjs} +6 -10
  10. package/dist/chunk-4CX4SBRO.mjs +153 -0
  11. package/dist/chunk-4MN6UQHG.mjs +443 -0
  12. package/dist/{chunk-GLW2UO6O.mjs → chunk-5QQVZTVZ.mjs} +82 -61
  13. package/dist/{chunk-BGP2N52Z.mjs → chunk-66MI7Q4B.mjs} +5 -5
  14. package/dist/chunk-6FCGKSZX.mjs +268 -0
  15. package/dist/{chunk-CGOKTPXU.mjs → chunk-6JQFUE5I.mjs} +20 -23
  16. package/dist/{chunk-Z3MK2KKZ.mjs → chunk-7DHU4VGG.mjs} +7 -3
  17. package/dist/{chunk-VZ2NR7L3.mjs → chunk-7PYJD5JI.mjs} +35 -27
  18. package/dist/{chunk-JU2RUWHF.mjs → chunk-7XJHLGUV.mjs} +1 -1
  19. package/dist/{chunk-BMFN37JH.mjs → chunk-7YAU5CY6.mjs} +1 -1
  20. package/dist/chunk-A56YQQHG.mjs +402 -0
  21. package/dist/chunk-AH52LG6N.mjs +315 -0
  22. package/dist/{chunk-SLWCCURD.mjs → chunk-CLIN5525.mjs} +8 -4
  23. package/dist/{chunk-3VQNJ235.mjs → chunk-CSDO6VBW.mjs} +7 -0
  24. package/dist/chunk-D4ILTPOG.mjs +293 -0
  25. package/dist/{chunk-HS7TFG7V.mjs → chunk-D6ID6M4V.mjs} +1 -1
  26. package/dist/chunk-DOH3EHX7.mjs +378 -0
  27. package/dist/{chunk-MJIEMGRD.mjs → chunk-EFRENWEJ.mjs} +9 -17
  28. package/dist/{chunk-YBXCIF5Q.mjs → chunk-ERGGHC2V.mjs} +36 -49
  29. package/dist/{chunk-OXQQNQZI.mjs → chunk-FEZKMUCF.mjs} +10 -1
  30. package/dist/{chunk-55CEW76V.mjs → chunk-FH6QVUVZ.mjs} +1 -1
  31. package/dist/chunk-FMAXJ2SI.mjs +71 -0
  32. package/dist/chunk-FZIXGLMV.mjs +173 -0
  33. package/dist/{chunk-DS2AMHN2.mjs → chunk-GYMYRIZP.mjs} +2 -2
  34. package/dist/{chunk-KQDD5MU3.mjs → chunk-H45TKD34.mjs} +5 -5
  35. package/dist/{chunk-BBJBJSXQ.mjs → chunk-J5UICVJS.mjs} +1 -1
  36. package/dist/{chunk-RL772EH7.mjs → chunk-JHJHG4GO.mjs} +4 -12
  37. package/dist/{chunk-RN67642N.mjs → chunk-KMCGSZTX.mjs} +47 -41
  38. package/dist/{chunk-FHNT55I5.mjs → chunk-KUDCQ4FI.mjs} +4 -4
  39. package/dist/chunk-LE6YFY6D.mjs +209 -0
  40. package/dist/{chunk-NLLKTU4B.mjs → chunk-LLVQKSU3.mjs} +21 -17
  41. package/dist/{chunk-KKHTJNMM.mjs → chunk-MARPPFOJ.mjs} +8 -4
  42. package/dist/{chunk-6AFMNC42.mjs → chunk-N2PT566P.mjs} +15 -11
  43. package/dist/chunk-NLCKVHWB.mjs +161 -0
  44. package/dist/{chunk-YN5SYTOO.mjs → chunk-NQPOYKAQ.mjs} +9 -5
  45. package/dist/{chunk-ZZV5JVNW.mjs → chunk-NSLMILBT.mjs} +3 -7
  46. package/dist/chunk-NXA3CZ7A.mjs +248 -0
  47. package/dist/chunk-OGOYQ7BG.mjs +150 -0
  48. package/dist/{chunk-3NQGYJEZ.mjs → chunk-P6AM5V7O.mjs} +10 -18
  49. package/dist/{chunk-CZ3BW5GL.mjs → chunk-P76HMUI6.mjs} +5 -11
  50. package/dist/chunk-PCPLO5HT.mjs +671 -0
  51. package/dist/chunk-PG6K5XEC.mjs +475 -0
  52. package/dist/{chunk-5JGQAAQV.mjs → chunk-PJHPSRYD.mjs} +84 -62
  53. package/dist/{chunk-DDPA2XXS.mjs → chunk-PMB3A7V3.mjs} +2 -2
  54. package/dist/chunk-PR6V5XKM.mjs +209 -0
  55. package/dist/{chunk-46OFHMQA.mjs → chunk-Q76O3RIQ.mjs} +10 -6
  56. package/dist/chunk-QVKWW6KE.mjs +272 -0
  57. package/dist/chunk-RGU7HOEC.mjs +140 -0
  58. package/dist/{chunk-JF4PHPD5.mjs → chunk-RGVKLTLH.mjs} +4 -4
  59. package/dist/{chunk-VG6UF6UT.mjs → chunk-RP3SQYA3.mjs} +2 -2
  60. package/dist/chunk-RRBS6D63.mjs +163 -0
  61. package/dist/{chunk-UEL4RD5P.mjs → chunk-SMQ3DG25.mjs} +80 -67
  62. package/dist/chunk-SPJ5KXW7.mjs +199 -0
  63. package/dist/chunk-SYOD63OZ.mjs +225 -0
  64. package/dist/chunk-UFYSFDER.mjs +42 -0
  65. package/dist/chunk-VACKZOMY.mjs +190 -0
  66. package/dist/chunk-VLQZANBF.mjs +42 -0
  67. package/dist/chunk-WA6O6EUR.mjs +1885 -0
  68. package/dist/{chunk-E3K6O4FZ.mjs → chunk-WAZD7NFU.mjs} +5 -2
  69. package/dist/chunk-WG6JGJXB.mjs +165 -0
  70. package/dist/{chunk-I64K754C.mjs → chunk-WNGWBVLV.mjs} +2 -2
  71. package/dist/{chunk-3U7SD3MS.mjs → chunk-WOEHFRGB.mjs} +3 -3
  72. package/dist/{chunk-DKZRJOMF.mjs → chunk-XIRTEFKH.mjs} +12 -12
  73. package/dist/chunk-Y6DWJSKZ.mjs +79 -0
  74. package/dist/chunk-YKPROFLB.mjs +161 -0
  75. package/dist/{chunk-CJ46PDXE.mjs → chunk-ZRO5JO3H.mjs} +106 -66
  76. package/dist/{chunk-VYMHBV6D.mjs → chunk-ZU4NV6RG.mjs} +5 -3
  77. package/dist/components/ui/accordion.js +40 -4
  78. package/dist/components/ui/accordion.mjs +2 -2
  79. package/dist/components/ui/add-column-modal.js +789 -0
  80. package/dist/components/ui/add-column-modal.mjs +17 -0
  81. package/dist/components/ui/add-lead-modal.js +647 -0
  82. package/dist/components/ui/add-lead-modal.mjs +16 -0
  83. package/dist/components/ui/ai-assistant-drawer.js +686 -0
  84. package/dist/components/ui/ai-assistant-drawer.mjs +16 -0
  85. package/dist/components/ui/alert-dialog.js +37 -5
  86. package/dist/components/ui/alert-dialog.mjs +4 -4
  87. package/dist/components/ui/alert.js +37 -11
  88. package/dist/components/ui/alert.mjs +2 -2
  89. package/dist/components/ui/avatar.js +36 -8
  90. package/dist/components/ui/avatar.mjs +2 -2
  91. package/dist/components/ui/backoffice-alert-history-chart.js +624 -0
  92. package/dist/components/ui/backoffice-alert-history-chart.mjs +16 -0
  93. package/dist/components/ui/backoffice-contact-history-chart.js +687 -0
  94. package/dist/components/ui/backoffice-contact-history-chart.mjs +16 -0
  95. package/dist/components/ui/badge.js +37 -2
  96. package/dist/components/ui/badge.mjs +2 -2
  97. package/dist/components/ui/borrowing-capacity-line-chart.js +639 -0
  98. package/dist/components/ui/borrowing-capacity-line-chart.mjs +16 -0
  99. package/dist/components/ui/button.js +35 -3
  100. package/dist/components/ui/button.mjs +2 -2
  101. package/dist/components/ui/calendar.js +43 -19
  102. package/dist/components/ui/calendar.mjs +3 -3
  103. package/dist/components/ui/card.js +40 -4
  104. package/dist/components/ui/card.mjs +2 -2
  105. package/dist/components/ui/cash-balance-line-chart.js +627 -0
  106. package/dist/components/ui/cash-balance-line-chart.mjs +16 -0
  107. package/dist/components/ui/cashflow-bar-chart.js +123 -69
  108. package/dist/components/ui/cashflow-bar-chart.mjs +8 -8
  109. package/dist/components/ui/checkbox.js +36 -5
  110. package/dist/components/ui/checkbox.mjs +2 -3
  111. package/dist/components/ui/chip.js +37 -2
  112. package/dist/components/ui/chip.mjs +3 -3
  113. package/dist/components/ui/combobox.js +68 -49
  114. package/dist/components/ui/combobox.mjs +2 -2
  115. package/dist/components/ui/data-table.js +160 -88
  116. package/dist/components/ui/data-table.mjs +10 -11
  117. package/dist/components/ui/date-picker.js +44 -20
  118. package/dist/components/ui/date-picker.mjs +6 -7
  119. package/dist/components/ui/dialog.js +44 -12
  120. package/dist/components/ui/dialog.mjs +4 -4
  121. package/dist/components/ui/drawer.js +46 -10
  122. package/dist/components/ui/drawer.mjs +3 -3
  123. package/dist/components/ui/dropdown-menu.js +40 -16
  124. package/dist/components/ui/dropdown-menu.mjs +3 -3
  125. package/dist/components/ui/empty.js +41 -5
  126. package/dist/components/ui/empty.mjs +2 -2
  127. package/dist/components/ui/expense-bar-chart.js +165 -66
  128. package/dist/components/ui/expense-bar-chart.mjs +8 -8
  129. package/dist/components/ui/field.js +53 -21
  130. package/dist/components/ui/field.mjs +4 -4
  131. package/dist/components/ui/financial-cards.js +1002 -0
  132. package/dist/components/ui/financial-cards.mjs +24 -0
  133. package/dist/components/ui/financial-drawers.js +637 -0
  134. package/dist/components/ui/financial-drawers.mjs +17 -0
  135. package/dist/components/ui/financial-primitives.js +218 -0
  136. package/dist/components/ui/financial-primitives.mjs +22 -0
  137. package/dist/components/ui/financial-sections.js +1422 -0
  138. package/dist/components/ui/financial-sections.mjs +30 -0
  139. package/dist/components/ui/form-primitives.js +682 -0
  140. package/dist/components/ui/form-primitives.mjs +19 -0
  141. package/dist/components/ui/income-bar-chart.js +163 -65
  142. package/dist/components/ui/income-bar-chart.mjs +8 -8
  143. package/dist/components/ui/input-group.js +43 -7
  144. package/dist/components/ui/input-group.mjs +5 -5
  145. package/dist/components/ui/input-otp.js +39 -3
  146. package/dist/components/ui/input-otp.mjs +2 -2
  147. package/dist/components/ui/input.js +34 -2
  148. package/dist/components/ui/input.mjs +2 -2
  149. package/dist/components/ui/kanban-column.js +1143 -0
  150. package/dist/components/ui/kanban-column.mjs +20 -0
  151. package/dist/components/ui/label.js +35 -7
  152. package/dist/components/ui/label.mjs +2 -2
  153. package/dist/components/ui/opportunity-card.js +960 -0
  154. package/dist/components/ui/opportunity-card.mjs +20 -0
  155. package/dist/components/ui/opportunity-edit-modals.js +3360 -0
  156. package/dist/components/ui/opportunity-edit-modals.mjs +37 -0
  157. package/dist/components/ui/opportunity-summary-tab.js +4365 -0
  158. package/dist/components/ui/opportunity-summary-tab.mjs +34 -0
  159. package/dist/components/ui/pagination.js +35 -3
  160. package/dist/components/ui/pagination.mjs +3 -3
  161. package/dist/components/ui/pipeline-alerts.js +103 -0
  162. package/dist/components/ui/pipeline-alerts.mjs +8 -0
  163. package/dist/components/ui/pipeline-board.js +1408 -0
  164. package/dist/components/ui/pipeline-board.mjs +24 -0
  165. package/dist/components/ui/pipeline-chart.js +216 -0
  166. package/dist/components/ui/pipeline-chart.mjs +10 -0
  167. package/dist/components/ui/pipeline-dialogs.js +1183 -0
  168. package/dist/components/ui/pipeline-dialogs.mjs +23 -0
  169. package/dist/components/ui/pipeline-primitives.js +300 -0
  170. package/dist/components/ui/pipeline-primitives.mjs +11 -0
  171. package/dist/components/ui/popover.js +45 -4
  172. package/dist/components/ui/popover.mjs +3 -3
  173. package/dist/components/ui/progress.js +33 -1
  174. package/dist/components/ui/progress.mjs +2 -2
  175. package/dist/components/ui/property-cashflow-doughnut-chart.js +523 -0
  176. package/dist/components/ui/property-cashflow-doughnut-chart.mjs +16 -0
  177. package/dist/components/ui/property-debt-equity-doughnut-chart.js +521 -0
  178. package/dist/components/ui/property-debt-equity-doughnut-chart.mjs +16 -0
  179. package/dist/components/ui/property-mobile-estimate-line-chart.js +682 -0
  180. package/dist/components/ui/property-mobile-estimate-line-chart.mjs +16 -0
  181. package/dist/components/ui/radio-group.js +33 -1
  182. package/dist/components/ui/radio-group.mjs +2 -2
  183. package/dist/components/ui/select.js +66 -26
  184. package/dist/components/ui/select.mjs +3 -3
  185. package/dist/components/ui/separator.js +33 -1
  186. package/dist/components/ui/separator.mjs +2 -2
  187. package/dist/components/ui/sheet.js +37 -9
  188. package/dist/components/ui/sheet.mjs +3 -3
  189. package/dist/components/ui/skeleton.js +33 -1
  190. package/dist/components/ui/skeleton.mjs +2 -2
  191. package/dist/components/ui/slider.js +86 -102
  192. package/dist/components/ui/slider.mjs +2 -2
  193. package/dist/components/ui/spinner.js +33 -1
  194. package/dist/components/ui/spinner.mjs +2 -2
  195. package/dist/components/ui/stage-timeline.js +579 -0
  196. package/dist/components/ui/stage-timeline.mjs +15 -0
  197. package/dist/components/ui/switch.js +37 -4
  198. package/dist/components/ui/switch.mjs +2 -3
  199. package/dist/components/ui/table.js +37 -5
  200. package/dist/components/ui/table.mjs +2 -2
  201. package/dist/components/ui/tabs.js +36 -12
  202. package/dist/components/ui/tabs.mjs +2 -2
  203. package/dist/components/ui/textarea.js +34 -2
  204. package/dist/components/ui/textarea.mjs +2 -2
  205. package/dist/components/ui/toggle-group.js +35 -4
  206. package/dist/components/ui/toggle-group.mjs +3 -4
  207. package/dist/components/ui/toggle.js +35 -4
  208. package/dist/components/ui/toggle.mjs +2 -3
  209. package/dist/components/ui/tooltip.js +51 -22
  210. package/dist/components/ui/tooltip.mjs +3 -3
  211. package/dist/components/ui/transactions-expense-categories-doughnut-chart.js +528 -0
  212. package/dist/components/ui/transactions-expense-categories-doughnut-chart.mjs +16 -0
  213. package/dist/components/ui/transactions-income-expense-bar-chart.js +76 -38
  214. package/dist/components/ui/transactions-income-expense-bar-chart.mjs +8 -8
  215. package/dist/components/ui/transactions-liabilities-breakdown-doughnut-chart.js +528 -0
  216. package/dist/components/ui/transactions-liabilities-breakdown-doughnut-chart.mjs +16 -0
  217. package/dist/index.js +11616 -3831
  218. package/dist/index.mjs +333 -161
  219. package/dist/lib/theme-provider.js +10 -1
  220. package/dist/lib/theme-provider.mjs +1 -1
  221. package/dist/lib/typography.js +8 -0
  222. package/dist/lib/typography.mjs +3 -1
  223. package/dist/lib/utils.js +33 -1
  224. package/dist/lib/utils.mjs +1 -1
  225. package/dist/styles.css +1 -1
  226. package/package.json +140 -5
  227. package/src/components/index.tsx +296 -42
  228. package/src/components/ui/accordion.tsx +6 -3
  229. package/src/components/ui/add-column-modal.tsx +339 -0
  230. package/src/components/ui/add-lead-modal.tsx +290 -0
  231. package/src/components/ui/ai-assistant-drawer.tsx +408 -0
  232. package/src/components/ui/alert-dialog.tsx +80 -54
  233. package/src/components/ui/alert.tsx +28 -28
  234. package/src/components/ui/avatar.tsx +30 -29
  235. package/src/components/ui/backoffice-alert-history-chart.tsx +260 -0
  236. package/src/components/ui/backoffice-contact-history-chart.tsx +325 -0
  237. package/src/components/ui/badge.tsx +17 -15
  238. package/src/components/ui/borrowing-capacity-line-chart.tsx +357 -0
  239. package/src/components/ui/button.tsx +30 -27
  240. package/src/components/ui/calendar.tsx +53 -67
  241. package/src/components/ui/card.tsx +27 -24
  242. package/src/components/ui/cash-balance-line-chart.tsx +302 -0
  243. package/src/components/ui/cashflow-bar-chart.tsx +104 -77
  244. package/src/components/ui/chart-shared.tsx +176 -15
  245. package/src/components/ui/checkbox.tsx +30 -26
  246. package/src/components/ui/combobox.tsx +78 -72
  247. package/src/components/ui/data-table.tsx +160 -99
  248. package/src/components/ui/date-picker.tsx +0 -2
  249. package/src/components/ui/dialog.tsx +70 -60
  250. package/src/components/ui/drawer.tsx +57 -48
  251. package/src/components/ui/dropdown-menu.tsx +90 -82
  252. package/src/components/ui/empty.tsx +31 -27
  253. package/src/components/ui/expense-bar-chart.tsx +83 -65
  254. package/src/components/ui/field.tsx +70 -62
  255. package/src/components/ui/financial-cards.tsx +830 -0
  256. package/src/components/ui/financial-drawers.tsx +339 -0
  257. package/src/components/ui/financial-primitives.tsx +331 -0
  258. package/src/components/ui/financial-sections.tsx +672 -0
  259. package/src/components/ui/form-primitives.tsx +536 -0
  260. package/src/components/ui/income-bar-chart.tsx +79 -60
  261. package/src/components/ui/input-group.tsx +41 -34
  262. package/src/components/ui/input-otp.tsx +29 -24
  263. package/src/components/ui/input.tsx +8 -8
  264. package/src/components/ui/kanban-column.tsx +333 -0
  265. package/src/components/ui/label.tsx +9 -12
  266. package/src/components/ui/opportunity-card.tsx +616 -0
  267. package/src/components/ui/opportunity-edit-modals.tsx +2528 -0
  268. package/src/components/ui/opportunity-summary-tab.tsx +579 -0
  269. package/src/components/ui/pipeline-alerts.tsx +74 -0
  270. package/src/components/ui/pipeline-board.tsx +268 -0
  271. package/src/components/ui/pipeline-chart.tsx +173 -0
  272. package/src/components/ui/pipeline-dialogs.tsx +303 -0
  273. package/src/components/ui/pipeline-primitives.tsx +108 -0
  274. package/src/components/ui/popover.tsx +41 -36
  275. package/src/components/ui/property-cashflow-doughnut-chart.tsx +188 -0
  276. package/src/components/ui/property-debt-equity-doughnut-chart.tsx +185 -0
  277. package/src/components/ui/property-mobile-estimate-line-chart.tsx +393 -0
  278. package/src/components/ui/select.tsx +65 -52
  279. package/src/components/ui/sheet.tsx +55 -52
  280. package/src/components/ui/slider.tsx +54 -77
  281. package/src/components/ui/stage-timeline.tsx +205 -0
  282. package/src/components/ui/switch.tsx +42 -29
  283. package/src/components/ui/table.tsx +28 -28
  284. package/src/components/ui/tabs.tsx +22 -28
  285. package/src/components/ui/textarea.tsx +8 -8
  286. package/src/components/ui/toggle-group.tsx +0 -2
  287. package/src/components/ui/toggle.tsx +13 -15
  288. package/src/components/ui/tooltip.tsx +30 -28
  289. package/src/components/ui/transactions-expense-categories-doughnut-chart.tsx +191 -0
  290. package/src/components/ui/transactions-income-expense-bar-chart.tsx +45 -38
  291. package/src/components/ui/transactions-liabilities-breakdown-doughnut-chart.tsx +191 -0
  292. package/src/lib/theme-provider.tsx +10 -0
  293. package/src/lib/typography.ts +9 -0
  294. package/src/lib/utils.ts +41 -3
  295. package/src/styles/globals.css +371 -124
  296. package/src/styles/styles-css.ts +1 -1
  297. package/tsup.config.ts +27 -0
  298. package/dist/chunk-3EQP72AW.mjs +0 -58
  299. package/dist/chunk-K74JRTJR.mjs +0 -105
  300. package/dist/chunk-V7CNWJT3.mjs +0 -10
@@ -0,0 +1,191 @@
1
+ import React, { useMemo } from "react";
2
+ import {
3
+ Chart as ChartJS,
4
+ ArcElement,
5
+ Tooltip,
6
+ type ChartOptions,
7
+ type ChartData,
8
+ } from "chart.js";
9
+ import { Chart } from "react-chartjs-2";
10
+ import { useThemeVars } from "@/lib/theme-provider";
11
+ import { Card, CardContent, CardHeader, CardTitle } from "./card";
12
+ import { Empty, EmptyDescription } from "./empty";
13
+ import { Skeleton } from "./skeleton";
14
+ import { cn } from "@/lib/utils";
15
+ import {
16
+ formatAbbrev,
17
+ hexToRgba,
18
+ DATASET_ALPHAS,
19
+ FALLBACK_PRIMARY,
20
+ DoughnutLegendRow,
21
+ } from "./chart-shared";
22
+
23
+ ChartJS.register(ArcElement, Tooltip);
24
+
25
+ // ---------------------------------------------------------------------------
26
+ // Types
27
+ // ---------------------------------------------------------------------------
28
+
29
+ export interface ExpenseCategorySegment {
30
+ label: string;
31
+ /** Spend amount in dollars */
32
+ value: number;
33
+ }
34
+
35
+ export interface TransactionsExpenseCategoriesDoughnutChartProps {
36
+ segments?: ExpenseCategorySegment[] | null;
37
+ title?: string;
38
+ showLegend?: boolean;
39
+ /** Chart canvas height in pixels */
40
+ height?: number;
41
+ /** Card max-width in pixels or CSS string */
42
+ width?: number | string;
43
+ className?: string;
44
+ /** Show skeleton loading state instead of the chart */
45
+ isLoading?: boolean;
46
+ }
47
+
48
+ // ---------------------------------------------------------------------------
49
+ // ---------------------------------------------------------------------------
50
+ // Component
51
+ // ---------------------------------------------------------------------------
52
+
53
+ export function TransactionsExpenseCategoriesDoughnutChart({
54
+ segments,
55
+ title = "Expense Categories",
56
+ showLegend = true,
57
+ height = 200,
58
+ width = "100%",
59
+ className,
60
+ isLoading = false,
61
+ }: TransactionsExpenseCategoriesDoughnutChartProps) {
62
+ const themeVars = useThemeVars();
63
+ const brandPrimary: string =
64
+ (themeVars["--theme-primary"] as string | undefined) || FALLBACK_PRIMARY;
65
+ const fontFamily: string =
66
+ (themeVars["--font-sans"] as string | undefined) || "Figtree, sans-serif";
67
+
68
+ const hasData = !!segments?.length && segments.some((s) => s.value > 0);
69
+
70
+ const total = useMemo(
71
+ () => segments?.reduce((sum, s) => sum + s.value, 0) ?? 0,
72
+ [segments],
73
+ );
74
+
75
+ const colors = useMemo(
76
+ () =>
77
+ (segments ?? []).map((_, i) =>
78
+ hexToRgba(brandPrimary, DATASET_ALPHAS[i % DATASET_ALPHAS.length]),
79
+ ),
80
+ [segments, brandPrimary],
81
+ );
82
+
83
+ const chartData = useMemo<ChartData<"doughnut">>(
84
+ () => ({
85
+ labels: segments?.map((s) => s.label) ?? [],
86
+ datasets: [
87
+ {
88
+ data: segments?.map((s) => s.value) ?? [],
89
+ backgroundColor: colors,
90
+ borderWidth: 0,
91
+ hoverOffset: 4,
92
+ },
93
+ ],
94
+ }),
95
+ [segments, colors],
96
+ );
97
+
98
+ const options = useMemo<ChartOptions<"doughnut">>(
99
+ () => ({
100
+ responsive: true,
101
+ maintainAspectRatio: false,
102
+ cutout: "68%",
103
+ animation: { duration: 600, easing: "easeOutQuart" },
104
+ plugins: {
105
+ legend: { display: false },
106
+ tooltip: {
107
+ padding: 10,
108
+ cornerRadius: 0,
109
+ titleFont: { size: 11, weight: 600 },
110
+ bodyFont: { size: 12, weight: 500 },
111
+ callbacks: {
112
+ label: (ctx) => {
113
+ const val = ctx.raw as number;
114
+ const pct =
115
+ total > 0 ? `${((val / total) * 100).toFixed(1)}%` : "0%";
116
+ return ` ${ctx.label}: ${formatAbbrev(val)} (${pct})`;
117
+ },
118
+ },
119
+ },
120
+ },
121
+ }),
122
+ [total],
123
+ );
124
+
125
+ return (
126
+ <Card
127
+ className={cn("w-full py-4 sm:py-6 gap-2", className)}
128
+ style={{ maxWidth: width, fontFamily }}
129
+ >
130
+ <CardHeader className="px-3 sm:px-6">
131
+ <CardTitle className="text-sm sm:text-base">{title}</CardTitle>
132
+ </CardHeader>
133
+
134
+ <CardContent className="px-3 sm:px-6">
135
+ {isLoading ? (
136
+ <Skeleton style={{ height, width: "100%" }} />
137
+ ) : !hasData ? (
138
+ <Empty className="flex-none p-4" style={{ height }}>
139
+ <EmptyDescription>No data available</EmptyDescription>
140
+ </Empty>
141
+ ) : (
142
+ <div className="flex flex-col gap-4">
143
+ <div style={{ height, width: "100%", position: "relative" }}>
144
+ <Chart
145
+ key={brandPrimary}
146
+ type="doughnut"
147
+ data={chartData}
148
+ options={options}
149
+ aria-label={title}
150
+ />
151
+ <div
152
+ style={{
153
+ position: "absolute",
154
+ top: "50%",
155
+ left: "50%",
156
+ transform: "translate(-50%, -50%)",
157
+ textAlign: "center",
158
+ pointerEvents: "none",
159
+ }}
160
+ >
161
+ <div className="text-base font-semibold leading-tight">
162
+ {formatAbbrev(total)}
163
+ </div>
164
+ <div className="text-[11px] text-muted-foreground leading-none mt-1">
165
+ Total
166
+ </div>
167
+ </div>
168
+ </div>
169
+ {showLegend && (
170
+ <div className="flex flex-col gap-2">
171
+ {segments!.map((s, i) => (
172
+ <DoughnutLegendRow
173
+ key={s.label}
174
+ label={s.label}
175
+ color={colors[i]}
176
+ value={s.value}
177
+ percent={
178
+ total > 0
179
+ ? `${((s.value / total) * 100).toFixed(1)}%`
180
+ : "0%"
181
+ }
182
+ />
183
+ ))}
184
+ </div>
185
+ )}
186
+ </div>
187
+ )}
188
+ </CardContent>
189
+ </Card>
190
+ );
191
+ }
@@ -15,7 +15,11 @@ import { Card, CardContent, CardHeader, CardTitle } from "./card";
15
15
  import { Empty, EmptyDescription } from "./empty";
16
16
  import { Spinner } from "./spinner";
17
17
  import { cn } from "@/lib/utils";
18
- import { FALLBACK_TICK } from "./chart-shared";
18
+ import {
19
+ FALLBACK_TICK,
20
+ FALLBACK_PRIMARY,
21
+ FALLBACK_SECONDARY,
22
+ } from "./chart-shared";
19
23
 
20
24
  ChartJS.register(CategoryScale, LinearScale, BarElement, Tooltip);
21
25
 
@@ -23,11 +27,6 @@ ChartJS.register(CategoryScale, LinearScale, BarElement, Tooltip);
23
27
  // Constants
24
28
  // ---------------------------------------------------------------------------
25
29
 
26
- const FALLBACK_PRIMARY = "#33FF99";
27
- const FALLBACK_SECONDARY = "#162029";
28
- /** Dark neutral used for dollar value labels — not a brand color. */
29
- const VALUE_LABEL_COLOR = "#162029";
30
-
31
30
  // ---------------------------------------------------------------------------
32
31
  // Helpers
33
32
  // ---------------------------------------------------------------------------
@@ -75,7 +74,10 @@ export function TransactionsIncomeExpenseBarChart({
75
74
  const brandPrimary =
76
75
  (themeVars["--theme-primary"] as string | undefined) || FALLBACK_PRIMARY;
77
76
  const brandSecondary =
78
- (themeVars["--theme-secondary"] as string | undefined) || FALLBACK_SECONDARY;
77
+ (themeVars["--theme-secondary"] as string | undefined) ||
78
+ FALLBACK_SECONDARY;
79
+ const fontFamily: string =
80
+ (themeVars["--font-sans"] as string | undefined) || "Figtree, sans-serif";
79
81
 
80
82
  const hasData = totalIncome != null && totalExpense != null;
81
83
  const incomeVal = totalIncome ?? 0;
@@ -104,7 +106,7 @@ export function TransactionsIncomeExpenseBarChart({
104
106
  offset: 10,
105
107
  clamp: false,
106
108
  font: { weight: "bold", size: 14 },
107
- color: VALUE_LABEL_COLOR,
109
+ color: FALLBACK_SECONDARY,
108
110
  textAlign: "left",
109
111
  // Returns array for multi-line: dollar value on line 1, blank on line 2
110
112
  formatter: (v: number) => [formatDollar(v), ""],
@@ -119,8 +121,10 @@ export function TransactionsIncomeExpenseBarChart({
119
121
  textAlign: "left",
120
122
  // Returns array for multi-line: blank on line 1, bar label on line 2
121
123
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
122
- formatter: (_: number, ctx: any) =>
123
- ["", String(ctx.chart.data.labels?.[ctx.dataIndex] ?? "")],
124
+ formatter: (_: number, ctx: any) => [
125
+ "",
126
+ String(ctx.chart.data.labels?.[ctx.dataIndex] ?? ""),
127
+ ],
124
128
  },
125
129
  },
126
130
  } as unknown as never,
@@ -129,40 +133,43 @@ export function TransactionsIncomeExpenseBarChart({
129
133
  };
130
134
  }, [hasData, incomeVal, expenseVal, brandPrimary, brandSecondary]);
131
135
 
132
- const options = useMemo<ChartOptions<"bar">>(() => ({
133
- indexAxis: "y",
134
- responsive: true,
135
- maintainAspectRatio: false,
136
- animation: { duration: 800, easing: "easeOutQuart" },
137
- layout: {
138
- // Right padding reserves space for the datalabels rendered outside the bar area
139
- padding: { right: 180, left: 0, top: 10, bottom: 10 },
140
- },
141
- plugins: {
142
- legend: { display: false },
143
- tooltip: { enabled: false },
144
- },
145
- scales: {
146
- y: {
147
- display: true,
148
- grid: { display: false },
149
- border: { display: false },
150
- ticks: { display: false },
136
+ const options = useMemo<ChartOptions<"bar">>(
137
+ () => ({
138
+ indexAxis: "y",
139
+ responsive: true,
140
+ maintainAspectRatio: false,
141
+ animation: { duration: 800, easing: "easeOutQuart" },
142
+ layout: {
143
+ // Right padding reserves space for the datalabels rendered outside the bar area
144
+ padding: { right: 180, left: 0, top: 10, bottom: 10 },
145
+ },
146
+ plugins: {
147
+ legend: { display: false },
148
+ tooltip: { enabled: false },
151
149
  },
152
- x: {
153
- display: true,
154
- suggestedMax: maxVal * 1.3,
155
- grid: { display: false },
156
- border: { display: false },
157
- ticks: { display: false },
150
+ scales: {
151
+ y: {
152
+ display: true,
153
+ grid: { display: false },
154
+ border: { display: false },
155
+ ticks: { display: false },
156
+ },
157
+ x: {
158
+ display: true,
159
+ suggestedMax: maxVal * 1.3,
160
+ grid: { display: false },
161
+ border: { display: false },
162
+ ticks: { display: false },
163
+ },
158
164
  },
159
- },
160
- }), [maxVal]);
165
+ }),
166
+ [maxVal],
167
+ );
161
168
 
162
169
  return (
163
170
  <Card
164
171
  className={cn("w-full py-4 sm:py-6 gap-2", className)}
165
- style={{ maxWidth: width }}
172
+ style={{ maxWidth: width, fontFamily }}
166
173
  >
167
174
  <CardHeader className="px-3 sm:px-6">
168
175
  <CardTitle className="text-sm sm:text-base">{title}</CardTitle>
@@ -0,0 +1,191 @@
1
+ import React, { useMemo } from "react";
2
+ import {
3
+ Chart as ChartJS,
4
+ ArcElement,
5
+ Tooltip,
6
+ type ChartOptions,
7
+ type ChartData,
8
+ } from "chart.js";
9
+ import { Chart } from "react-chartjs-2";
10
+ import { useThemeVars } from "@/lib/theme-provider";
11
+ import { Card, CardContent, CardHeader, CardTitle } from "./card";
12
+ import { Empty, EmptyDescription } from "./empty";
13
+ import { Skeleton } from "./skeleton";
14
+ import { cn } from "@/lib/utils";
15
+ import {
16
+ formatAbbrev,
17
+ hexToRgba,
18
+ DATASET_ALPHAS,
19
+ FALLBACK_PRIMARY,
20
+ DoughnutLegendRow,
21
+ } from "./chart-shared";
22
+
23
+ ChartJS.register(ArcElement, Tooltip);
24
+
25
+ // ---------------------------------------------------------------------------
26
+ // Types
27
+ // ---------------------------------------------------------------------------
28
+
29
+ export interface LiabilitySegment {
30
+ label: string;
31
+ /** Value in dollars */
32
+ value: number;
33
+ }
34
+
35
+ export interface TransactionsLiabilitiesBreakdownChartProps {
36
+ segments?: LiabilitySegment[] | null;
37
+ title?: string;
38
+ showLegend?: boolean;
39
+ /** Chart canvas height in pixels */
40
+ height?: number;
41
+ /** Card max-width in pixels or CSS string */
42
+ width?: number | string;
43
+ className?: string;
44
+ /** Show skeleton loading state instead of the chart */
45
+ isLoading?: boolean;
46
+ }
47
+
48
+ // ---------------------------------------------------------------------------
49
+ // ---------------------------------------------------------------------------
50
+ // Component
51
+ // ---------------------------------------------------------------------------
52
+
53
+ export function TransactionsLiabilitiesBreakdownChart({
54
+ segments,
55
+ title = "Liabilities Breakdown",
56
+ showLegend = true,
57
+ height = 200,
58
+ width = "100%",
59
+ className,
60
+ isLoading = false,
61
+ }: TransactionsLiabilitiesBreakdownChartProps) {
62
+ const themeVars = useThemeVars();
63
+ const brandPrimary: string =
64
+ (themeVars["--theme-primary"] as string | undefined) || FALLBACK_PRIMARY;
65
+ const fontFamily: string =
66
+ (themeVars["--font-sans"] as string | undefined) || "Figtree, sans-serif";
67
+
68
+ const hasData = !!segments?.length && segments.some((s) => s.value > 0);
69
+
70
+ const total = useMemo(
71
+ () => segments?.reduce((sum, s) => sum + s.value, 0) ?? 0,
72
+ [segments],
73
+ );
74
+
75
+ const colors = useMemo(
76
+ () =>
77
+ (segments ?? []).map((_, i) =>
78
+ hexToRgba(brandPrimary, DATASET_ALPHAS[i % DATASET_ALPHAS.length]),
79
+ ),
80
+ [segments, brandPrimary],
81
+ );
82
+
83
+ const chartData = useMemo<ChartData<"doughnut">>(
84
+ () => ({
85
+ labels: segments?.map((s) => s.label) ?? [],
86
+ datasets: [
87
+ {
88
+ data: segments?.map((s) => s.value) ?? [],
89
+ backgroundColor: colors,
90
+ borderWidth: 0,
91
+ hoverOffset: 4,
92
+ },
93
+ ],
94
+ }),
95
+ [segments, colors],
96
+ );
97
+
98
+ const options = useMemo<ChartOptions<"doughnut">>(
99
+ () => ({
100
+ responsive: true,
101
+ maintainAspectRatio: false,
102
+ cutout: "68%",
103
+ animation: { duration: 600, easing: "easeOutQuart" },
104
+ plugins: {
105
+ legend: { display: false },
106
+ tooltip: {
107
+ padding: 10,
108
+ cornerRadius: 0,
109
+ titleFont: { size: 11, weight: 600 },
110
+ bodyFont: { size: 12, weight: 500 },
111
+ callbacks: {
112
+ label: (ctx) => {
113
+ const val = ctx.raw as number;
114
+ const pct =
115
+ total > 0 ? `${((val / total) * 100).toFixed(1)}%` : "0%";
116
+ return ` ${ctx.label}: ${formatAbbrev(val)} (${pct})`;
117
+ },
118
+ },
119
+ },
120
+ },
121
+ }),
122
+ [total],
123
+ );
124
+
125
+ return (
126
+ <Card
127
+ className={cn("w-full py-4 sm:py-6 gap-2", className)}
128
+ style={{ maxWidth: width, fontFamily }}
129
+ >
130
+ <CardHeader className="px-3 sm:px-6">
131
+ <CardTitle className="text-sm sm:text-base">{title}</CardTitle>
132
+ </CardHeader>
133
+
134
+ <CardContent className="px-3 sm:px-6">
135
+ {isLoading ? (
136
+ <Skeleton style={{ height, width: "100%" }} />
137
+ ) : !hasData ? (
138
+ <Empty className="flex-none p-4" style={{ height }}>
139
+ <EmptyDescription>No data available</EmptyDescription>
140
+ </Empty>
141
+ ) : (
142
+ <div className="flex flex-col gap-4">
143
+ <div style={{ height, width: "100%", position: "relative" }}>
144
+ <Chart
145
+ key={brandPrimary}
146
+ type="doughnut"
147
+ data={chartData}
148
+ options={options}
149
+ aria-label={title}
150
+ />
151
+ <div
152
+ style={{
153
+ position: "absolute",
154
+ top: "50%",
155
+ left: "50%",
156
+ transform: "translate(-50%, -50%)",
157
+ textAlign: "center",
158
+ pointerEvents: "none",
159
+ }}
160
+ >
161
+ <div className="text-base font-semibold leading-tight">
162
+ {formatAbbrev(total)}
163
+ </div>
164
+ <div className="text-[11px] text-muted-foreground leading-none mt-1">
165
+ Total
166
+ </div>
167
+ </div>
168
+ </div>
169
+ {showLegend && (
170
+ <div className="flex flex-col gap-2">
171
+ {segments!.map((s, i) => (
172
+ <DoughnutLegendRow
173
+ key={s.label}
174
+ label={s.label}
175
+ color={colors[i]}
176
+ value={s.value}
177
+ percent={
178
+ total > 0
179
+ ? `${((s.value / total) * 100).toFixed(1)}%`
180
+ : "0%"
181
+ }
182
+ />
183
+ ))}
184
+ </div>
185
+ )}
186
+ </div>
187
+ )}
188
+ </CardContent>
189
+ </Card>
190
+ );
191
+ }
@@ -93,6 +93,16 @@ export function ThemeProvider({
93
93
  // Legacy compat (used by existing WealthX apps)
94
94
  "--theme-primary": primary,
95
95
  "--theme-secondary": secondary,
96
+
97
+ // Pipeline stage palette — 5 opacity levels of primary, tenant-adaptive.
98
+ // Defined here (not :root) so they react to tenant theme switches.
99
+ "--color-stage-1": primaryOklch,
100
+ "--color-stage-2": `color-mix(in oklch, ${primaryOklch} 80%, transparent)`,
101
+ "--color-stage-3": `color-mix(in oklch, ${primaryOklch} 60%, transparent)`,
102
+ "--color-stage-4": `color-mix(in oklch, ${primaryOklch} 40%, transparent)`,
103
+ "--color-stage-5": `color-mix(in oklch, ${primaryOklch} 20%, transparent)`,
104
+ // WCAG-computed contrast text for any primary-shaded background
105
+ "--color-stage-fg": primaryFgOklch,
96
106
  };
97
107
  }, [primary, secondary, fontFamily, injectCssVariables]);
98
108
 
@@ -12,6 +12,7 @@
12
12
  // .text-h1 … .text-h6
13
13
  // .text-body-large, .text-body-medium, .text-body-small
14
14
  // .text-label-large, .text-label-medium, .text-label-small
15
+ // .text-button, .text-button-xs
15
16
  // .text-caption, .text-overline, .text-code
16
17
 
17
18
  export const FONT_FAMILY_SANS =
@@ -65,6 +66,12 @@ export const TYPOGRAPHY_LABEL = {
65
66
  small: style(600, "0.75rem", "1rem", { letterSpacing: "0.03125rem" }),
66
67
  } as const;
67
68
 
69
+ /** Button — interactive control text (weight 500 / Medium per Figma) */
70
+ export const TYPOGRAPHY_BUTTON = {
71
+ default: style(500, "0.875rem", "1.25rem"),
72
+ xs: style(500, "0.75rem", "1.25rem"),
73
+ } as const;
74
+
68
75
  /** Utility — caption, overline, code */
69
76
  export const TYPOGRAPHY_UTILITY = {
70
77
  caption: style(400, "0.75rem", "1rem"),
@@ -80,6 +87,7 @@ export const TYPOGRAPHY = {
80
87
  heading: TYPOGRAPHY_HEADING,
81
88
  body: TYPOGRAPHY_BODY,
82
89
  label: TYPOGRAPHY_LABEL,
90
+ button: TYPOGRAPHY_BUTTON,
83
91
  utility: TYPOGRAPHY_UTILITY,
84
92
  } as const;
85
93
 
@@ -119,6 +127,7 @@ export function getTypographyCssVars(
119
127
  ["", TYPOGRAPHY_HEADING as unknown as Record<string, TypographyStyle>],
120
128
  ["body", TYPOGRAPHY_BODY as unknown as Record<string, TypographyStyle>],
121
129
  ["label", TYPOGRAPHY_LABEL as unknown as Record<string, TypographyStyle>],
130
+ ["button", TYPOGRAPHY_BUTTON as unknown as Record<string, TypographyStyle>],
122
131
  ["", TYPOGRAPHY_UTILITY as unknown as Record<string, TypographyStyle>],
123
132
  ];
124
133
 
package/src/lib/utils.ts CHANGED
@@ -1,6 +1,44 @@
1
- import { clsx, type ClassValue } from "clsx"
2
- import { twMerge } from "tailwind-merge"
1
+ import { clsx, type ClassValue } from "clsx";
2
+ import { extendTailwindMerge } from "tailwind-merge";
3
+
4
+ /**
5
+ * Custom twMerge that recognises WealthX typography tokens as font-size
6
+ * utilities so they don't conflict with text-color utilities
7
+ * (e.g. text-button vs text-primary-foreground).
8
+ */
9
+ const twMerge = extendTailwindMerge({
10
+ extend: {
11
+ classGroups: {
12
+ "font-size": [
13
+ {
14
+ text: [
15
+ "display-large",
16
+ "display-medium",
17
+ "display-small",
18
+ "h1",
19
+ "h2",
20
+ "h3",
21
+ "h4",
22
+ "h5",
23
+ "h6",
24
+ "body-large",
25
+ "body-medium",
26
+ "body-small",
27
+ "label-large",
28
+ "label-medium",
29
+ "label-small",
30
+ "button",
31
+ "button-xs",
32
+ "caption",
33
+ "overline",
34
+ "code",
35
+ ],
36
+ },
37
+ ],
38
+ },
39
+ },
40
+ });
3
41
 
4
42
  export function cn(...inputs: ClassValue[]): string {
5
- return twMerge(clsx(inputs))
43
+ return twMerge(clsx(inputs));
6
44
  }