@wealthx/shadcn 1.1.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (300) hide show
  1. package/.turbo/turbo-build.log +235 -154
  2. package/CHANGELOG.md +12 -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-GPRJQ24C.mjs → chunk-34NWQURD.mjs} +2 -2
  6. package/dist/{chunk-MQ72DIBH.mjs → chunk-3GF7OVTP.mjs} +14 -5
  7. package/dist/chunk-3WMX6KWS.mjs +245 -0
  8. package/dist/{chunk-PMKODV6M.mjs → chunk-462HMNO4.mjs} +6 -10
  9. package/dist/chunk-4CX4SBRO.mjs +153 -0
  10. package/dist/chunk-4MN6UQHG.mjs +443 -0
  11. package/dist/chunk-4Y6R4WEC.mjs +250 -0
  12. package/dist/{chunk-BGP2N52Z.mjs → chunk-66MI7Q4B.mjs} +5 -5
  13. package/dist/{chunk-CGOKTPXU.mjs → chunk-6JQFUE5I.mjs} +20 -23
  14. package/dist/{chunk-Z3MK2KKZ.mjs → chunk-7DHU4VGG.mjs} +7 -3
  15. package/dist/chunk-7MMXNK3C.mjs +317 -0
  16. package/dist/{chunk-VZ2NR7L3.mjs → chunk-7PYJD5JI.mjs} +35 -27
  17. package/dist/{chunk-JU2RUWHF.mjs → chunk-7XJHLGUV.mjs} +1 -1
  18. package/dist/{chunk-BMFN37JH.mjs → chunk-7YAU5CY6.mjs} +1 -1
  19. package/dist/chunk-A56YQQHG.mjs +402 -0
  20. package/dist/{chunk-GLW2UO6O.mjs → chunk-BL3DXM2X.mjs} +84 -62
  21. package/dist/{chunk-SLWCCURD.mjs → chunk-CLIN5525.mjs} +8 -4
  22. package/dist/{chunk-3VQNJ235.mjs → chunk-CSDO6VBW.mjs} +7 -0
  23. package/dist/chunk-D4ILTPOG.mjs +293 -0
  24. package/dist/{chunk-HS7TFG7V.mjs → chunk-D6ID6M4V.mjs} +1 -1
  25. package/dist/chunk-DOH3EHX7.mjs +378 -0
  26. package/dist/{chunk-MJIEMGRD.mjs → chunk-EFRENWEJ.mjs} +9 -17
  27. package/dist/{chunk-YBXCIF5Q.mjs → chunk-ERGGHC2V.mjs} +36 -49
  28. package/dist/{chunk-OXQQNQZI.mjs → chunk-FEZKMUCF.mjs} +10 -1
  29. package/dist/{chunk-55CEW76V.mjs → chunk-FH6QVUVZ.mjs} +1 -1
  30. package/dist/chunk-FMAXJ2SI.mjs +71 -0
  31. package/dist/chunk-FZIXGLMV.mjs +173 -0
  32. package/dist/chunk-GGM2UYGG.mjs +273 -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-JNQORUPP.mjs} +49 -42
  38. package/dist/{chunk-5JGQAAQV.mjs → chunk-K3JYD4IU.mjs} +86 -63
  39. package/dist/{chunk-FHNT55I5.mjs → chunk-KUDCQ4FI.mjs} +4 -4
  40. package/dist/{chunk-UEL4RD5P.mjs → chunk-LHYCMLVA.mjs} +82 -68
  41. package/dist/{chunk-NLLKTU4B.mjs → chunk-LLVQKSU3.mjs} +21 -17
  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-YN5SYTOO.mjs → chunk-NQPOYKAQ.mjs} +9 -5
  45. package/dist/{chunk-ZZV5JVNW.mjs → chunk-NSLMILBT.mjs} +3 -7
  46. package/dist/chunk-OGOYQ7BG.mjs +150 -0
  47. package/dist/chunk-OPNQAVVH.mjs +162 -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-DDPA2XXS.mjs → chunk-PMB3A7V3.mjs} +2 -2
  53. package/dist/chunk-PR6V5XKM.mjs +209 -0
  54. package/dist/{chunk-46OFHMQA.mjs → chunk-Q76O3RIQ.mjs} +10 -6
  55. package/dist/chunk-RGU7HOEC.mjs +140 -0
  56. package/dist/{chunk-JF4PHPD5.mjs → chunk-RGVKLTLH.mjs} +4 -4
  57. package/dist/{chunk-VG6UF6UT.mjs → chunk-RP3SQYA3.mjs} +2 -2
  58. package/dist/chunk-RYCLWMZ7.mjs +162 -0
  59. package/dist/chunk-SIZMLSRU.mjs +162 -0
  60. package/dist/chunk-SPJ5KXW7.mjs +199 -0
  61. package/dist/chunk-SWGT756Z.mjs +210 -0
  62. package/dist/chunk-SYOD63OZ.mjs +225 -0
  63. package/dist/chunk-TS2ZX2VS.mjs +270 -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-VPBN3WOO.mjs +164 -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-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 +640 -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 +628 -0
  106. package/dist/components/ui/cash-balance-line-chart.mjs +16 -0
  107. package/dist/components/ui/cashflow-bar-chart.js +124 -70
  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 +166 -67
  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 +164 -66
  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 +683 -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 +77 -39
  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 +11620 -3832
  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 +261 -0
  236. package/src/components/ui/backoffice-contact-history-chart.tsx +326 -0
  237. package/src/components/ui/badge.tsx +17 -15
  238. package/src/components/ui/borrowing-capacity-line-chart.tsx +359 -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 +304 -0
  243. package/src/components/ui/cashflow-bar-chart.tsx +106 -78
  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 +85 -66
  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 +81 -61
  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 +189 -0
  276. package/src/components/ui/property-debt-equity-doughnut-chart.tsx +186 -0
  277. package/src/components/ui/property-mobile-estimate-line-chart.tsx +395 -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 +192 -0
  290. package/src/components/ui/transactions-income-expense-bar-chart.tsx +47 -39
  291. package/src/components/ui/transactions-liabilities-breakdown-doughnut-chart.tsx +192 -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,672 @@
1
+ import React, { useState } from "react";
2
+ import { Button } from "./button";
3
+ import { Separator } from "./separator";
4
+ import {
5
+ Accordion,
6
+ AccordionItem,
7
+ AccordionTrigger,
8
+ AccordionContent,
9
+ } from "./accordion";
10
+ import {
11
+ PropertyCard,
12
+ DebtCard,
13
+ OtherLiabilityCard,
14
+ AlertCard,
15
+ type PropertyCardProps,
16
+ type DebtCardProps,
17
+ type OtherLiabilityCardProps,
18
+ type AlertCardProps,
19
+ type AlertActionType,
20
+ type AlertSeverity,
21
+ } from "./financial-cards";
22
+ import {
23
+ FinancialCardHeader,
24
+ FinancialDetailField,
25
+ FinancialLineItem,
26
+ FinancialSectionLabel,
27
+ FinancialSubtotalFrame,
28
+ FinancialSubtotalBlock,
29
+ } from "./financial-primitives";
30
+
31
+ /**
32
+ * Financial section components — WealthX DS (Level 4)
33
+ *
34
+ * Composed from Level 3 cards into full page sections.
35
+ * Used inside the Summary Report Drawer and Opportunity Details Drawer.
36
+ *
37
+ * ```
38
+ * Level 2 → FinancialDetailField, FinancialLvrBar … (financial-primitives)
39
+ * Level 3 → PropertyCard, DebtCard, AlertCard … (financial-cards)
40
+ * Level 4 → PropertyHoldingsSection, DebtSection … ← here
41
+ * Level 5 → SummaryReportDrawer, OpportunityDetailsDrawer …
42
+ * ```
43
+ *
44
+ * Component inventory:
45
+ * FinancialViewSection — bordered card wrapping income + properties + current liabilities
46
+ * PropertyHoldingsSection — grid of PropertyCards under a section header
47
+ * DebtSection — grid of DebtCards under a section header
48
+ * OtherLiabilitiesSection — grid of OtherLiabilityCards under a section header
49
+ * IncomeExpenseSection — 3-column income / expenses / future-payments grid
50
+ * AlertAccordion — collapsible accordion wrapping a grid of AlertCards
51
+ * LoanScenarioSection — read-only Loan Quiz card (4-column grid)
52
+ * FinancialBottomSummary — 3 stacked summary cards: cashflow / assets / net position
53
+ */
54
+
55
+ // ---------------------------------------------------------------------------
56
+ // PropertyHoldingsSection
57
+ // ---------------------------------------------------------------------------
58
+
59
+ export interface PropertyHoldingsSectionProps {
60
+ /** Section heading. Defaults to "Property Holdings". */
61
+ title?: string;
62
+ /** Each item maps 1:1 to a PropertyCard. */
63
+ items: PropertyCardProps[];
64
+ /**
65
+ * When true renders columns without individual card borders, separated by a 1px divider.
66
+ * Use inside `FinancialViewSection` to match the Figma Financial View card layout.
67
+ */
68
+ borderless?: boolean;
69
+ }
70
+
71
+ /** Two-column grid of PropertyCards under a section header. */
72
+ export function PropertyHoldingsSection({
73
+ title = "Property Holdings",
74
+ items,
75
+ borderless = false,
76
+ }: PropertyHoldingsSectionProps) {
77
+ if (items.length === 0) return null;
78
+ return (
79
+ <div>
80
+ <FinancialCardHeader>{title}</FinancialCardHeader>
81
+ {borderless ? (
82
+ <div className="grid grid-cols-2">
83
+ {items.map((item, i) => (
84
+ <div
85
+ key={`${item.address}-${i}`}
86
+ className={i > 0 ? "border-l border-border pl-5" : "pr-5"}
87
+ >
88
+ <PropertyCard {...item} borderless />
89
+ </div>
90
+ ))}
91
+ </div>
92
+ ) : (
93
+ <div className="grid grid-cols-2 gap-5">
94
+ {items.map((item, i) => (
95
+ <PropertyCard key={`${item.address}-${i}`} {...item} />
96
+ ))}
97
+ </div>
98
+ )}
99
+ </div>
100
+ );
101
+ }
102
+
103
+ // ---------------------------------------------------------------------------
104
+ // DebtSection
105
+ // ---------------------------------------------------------------------------
106
+
107
+ export interface DebtSectionProps {
108
+ /** Section heading. Defaults to "Mortgages & Investment Loans". */
109
+ title?: string;
110
+ /** Each item maps 1:1 to a DebtCard. */
111
+ items: DebtCardProps[];
112
+ }
113
+
114
+ /** Two-column grid of DebtCards under a section header. */
115
+ export function DebtSection({
116
+ title = "Mortgages & Investment Loans",
117
+ items,
118
+ }: DebtSectionProps) {
119
+ if (items.length === 0) return null;
120
+ return (
121
+ <div>
122
+ <FinancialCardHeader>{title}</FinancialCardHeader>
123
+ <div className="grid grid-cols-2 gap-3">
124
+ {items.map((item, i) => (
125
+ <DebtCard key={item.lenderName ?? i} {...item} />
126
+ ))}
127
+ </div>
128
+ </div>
129
+ );
130
+ }
131
+
132
+ // ---------------------------------------------------------------------------
133
+ // OtherLiabilitiesSection
134
+ // ---------------------------------------------------------------------------
135
+
136
+ export interface OtherLiabilitiesSectionProps {
137
+ /** Section heading. Defaults to "Other Liabilities". */
138
+ title?: string;
139
+ /** Each item maps 1:1 to an OtherLiabilityCard. */
140
+ items: OtherLiabilityCardProps[];
141
+ }
142
+
143
+ /** Two-column grid of OtherLiabilityCards under a section header. */
144
+ export function OtherLiabilitiesSection({
145
+ title = "Other Liabilities",
146
+ items,
147
+ }: OtherLiabilitiesSectionProps) {
148
+ if (items.length === 0) return null;
149
+ return (
150
+ <div>
151
+ <FinancialCardHeader>{title}</FinancialCardHeader>
152
+ <div className="grid grid-cols-2 gap-5">
153
+ {items.map((item, i) => (
154
+ <OtherLiabilityCard
155
+ key={`${item.lenderName ?? item.type}-${i}`}
156
+ {...item}
157
+ />
158
+ ))}
159
+ </div>
160
+ </div>
161
+ );
162
+ }
163
+
164
+ // ---------------------------------------------------------------------------
165
+ // IncomeExpenseSection
166
+ // ---------------------------------------------------------------------------
167
+
168
+ export interface SummaryLineItem {
169
+ label: string;
170
+ /** Formatted value string e.g. "$9,500". Renders "—" when falsy. */
171
+ value?: string | null;
172
+ /**
173
+ * When true renders the value in `text-destructive`.
174
+ * Use for expenses, liabilities, or negative cashflow rows.
175
+ */
176
+ destructive?: boolean;
177
+ }
178
+
179
+ export interface SummaryColumn {
180
+ /** Column heading override. */
181
+ label?: string;
182
+ items: SummaryLineItem[];
183
+ /** Formatted monthly average displayed in the tinted footer. */
184
+ monthlyAverage?: string;
185
+ /** Formatted 12-month total displayed in the tinted footer. */
186
+ totalLast12Months?: string;
187
+ }
188
+
189
+ export interface IncomeExpenseSectionProps {
190
+ income?: SummaryColumn;
191
+ expenses?: SummaryColumn;
192
+ futurePayments?: SummaryColumn;
193
+ /** Override the "Future Payments" column heading. */
194
+ futurePaymentsHeading?: string;
195
+ }
196
+
197
+ /**
198
+ * Three-column layout: income | expenses | future payments.
199
+ *
200
+ * Each column has a header, a scrollable list of `FinancialLineItem` rows,
201
+ * and a brand-tinted `FinancialSubtotalFrame` footer.
202
+ */
203
+ export function IncomeExpenseSection({
204
+ income,
205
+ expenses,
206
+ futurePayments,
207
+ futurePaymentsHeading = "Future Payments",
208
+ }: IncomeExpenseSectionProps) {
209
+ return (
210
+ <div className="grid grid-cols-3">
211
+ {/* Income */}
212
+ <div className="flex flex-col pr-5">
213
+ <FinancialCardHeader>
214
+ {income?.label ?? "Average Monthly Income"}
215
+ </FinancialCardHeader>
216
+ <div className="flex flex-1 flex-col pb-4">
217
+ {(income?.items ?? []).map((item) => (
218
+ <FinancialLineItem
219
+ key={item.label}
220
+ label={item.label}
221
+ value={item.value}
222
+ destructive={item.destructive}
223
+ />
224
+ ))}
225
+ </div>
226
+ <FinancialSubtotalFrame>
227
+ <FinancialSubtotalBlock
228
+ monthlyAverage={income?.monthlyAverage}
229
+ totalLast12Months={income?.totalLast12Months}
230
+ />
231
+ </FinancialSubtotalFrame>
232
+ </div>
233
+
234
+ {/* Expenses */}
235
+ <div className="flex flex-col border-x border-border px-5">
236
+ <FinancialCardHeader>
237
+ {expenses?.label ?? "Monthly Expenses and Commitments"}
238
+ </FinancialCardHeader>
239
+ <div className="flex flex-1 flex-col pb-4">
240
+ {(expenses?.items ?? []).map((item) => (
241
+ <FinancialLineItem
242
+ key={item.label}
243
+ label={item.label}
244
+ value={item.value}
245
+ destructive={item.destructive}
246
+ />
247
+ ))}
248
+ </div>
249
+ <FinancialSubtotalFrame>
250
+ <FinancialSubtotalBlock
251
+ monthlyAverage={expenses?.monthlyAverage}
252
+ totalLast12Months={expenses?.totalLast12Months}
253
+ />
254
+ </FinancialSubtotalFrame>
255
+ </div>
256
+
257
+ {/* Future Payments */}
258
+ <div className="flex flex-col pl-5">
259
+ <FinancialCardHeader>
260
+ {futurePayments?.label ?? futurePaymentsHeading}
261
+ </FinancialCardHeader>
262
+ <div className="flex flex-1 flex-col pb-4">
263
+ {(futurePayments?.items ?? []).map((item) => (
264
+ <FinancialLineItem
265
+ key={item.label}
266
+ label={item.label}
267
+ value={item.value}
268
+ destructive={item.destructive}
269
+ />
270
+ ))}
271
+ </div>
272
+ <FinancialSubtotalFrame>
273
+ <FinancialSubtotalBlock
274
+ monthlyAverage={futurePayments?.monthlyAverage}
275
+ totalLast12Months={futurePayments?.totalLast12Months}
276
+ />
277
+ </FinancialSubtotalFrame>
278
+ </div>
279
+ </div>
280
+ );
281
+ }
282
+
283
+ // ---------------------------------------------------------------------------
284
+ // AlertAccordion
285
+ // ---------------------------------------------------------------------------
286
+
287
+ const SEVERITY_BG: Record<AlertSeverity, string> = {
288
+ NEED_ACTION: "bg-destructive text-destructive-foreground",
289
+ WATCH: "bg-warning text-warning-foreground",
290
+ INSIGHT: "bg-success text-success-foreground",
291
+ };
292
+
293
+ export interface AlertAccordionSavePayload {
294
+ alertId: string;
295
+ action: AlertActionType;
296
+ }
297
+
298
+ export interface AlertAccordionProps {
299
+ /** List of alerts — each maps 1:1 to an AlertCard. */
300
+ alerts: AlertCardProps[];
301
+ /**
302
+ * Called when the user clicks Save.
303
+ * Receives only alerts with a pending dismiss or snooze action selected.
304
+ * When omitted the Save button is never shown.
305
+ */
306
+ onSaveActions?: (actions: AlertAccordionSavePayload[]) => void;
307
+ /** When `true` the Save button shows "Saving…" and is disabled. */
308
+ isSaving?: boolean;
309
+ }
310
+
311
+ /**
312
+ * Collapsible accordion wrapping a 3-column grid of AlertCards.
313
+ *
314
+ * - The trigger header shows the section title + severity count badges.
315
+ * - A Save button appears when the user has pending dismiss/snooze selections
316
+ * and `onSaveActions` is provided.
317
+ * - Returns `null` when `alerts` is empty.
318
+ */
319
+ export function AlertAccordion({
320
+ alerts,
321
+ onSaveActions,
322
+ isSaving = false,
323
+ }: AlertAccordionProps) {
324
+ const [actionMap, setActionMap] = useState<Record<string, AlertActionType>>(
325
+ {},
326
+ );
327
+
328
+ const handleActionChange = (id: string) => (action: AlertActionType) => {
329
+ setActionMap((prev) => ({ ...prev, [id]: action }));
330
+ };
331
+
332
+ const handleSave = (e: React.MouseEvent) => {
333
+ e.stopPropagation();
334
+ onSaveActions?.(
335
+ Object.entries(actionMap).map(([alertId, action]) => ({
336
+ alertId,
337
+ action,
338
+ })),
339
+ );
340
+ };
341
+
342
+ const countBySeverity = alerts.reduce<Record<string, number>>((acc, a) => {
343
+ acc[a.severityCode] = (acc[a.severityCode] || 0) + 1;
344
+ return acc;
345
+ }, {});
346
+
347
+ const hasPendingActions = Object.keys(actionMap).length > 0;
348
+
349
+ if (alerts.length === 0) return null;
350
+
351
+ return (
352
+ <Accordion>
353
+ <AccordionItem value="alerts" className="border-0">
354
+ <AccordionTrigger className="min-h-14 cursor-pointer border border-primary/20 bg-primary/10 px-4 py-2.5 hover:bg-primary/10 hover:no-underline">
355
+ <div className="flex w-full items-center justify-between">
356
+ <div className="flex items-center gap-2.5">
357
+ <span className="text-label-medium text-foreground">Alerts</span>
358
+ {(["INSIGHT", "WATCH", "NEED_ACTION"] as const).map((code) =>
359
+ countBySeverity[code] ? (
360
+ <span
361
+ key={code}
362
+ className={`flex h-6 w-6 items-center justify-center rounded-full text-xs font-semibold leading-none ${SEVERITY_BG[code]}`}
363
+ >
364
+ {countBySeverity[code]}
365
+ </span>
366
+ ) : null,
367
+ )}
368
+ </div>
369
+ {hasPendingActions && onSaveActions && (
370
+ <Button
371
+ variant="outline"
372
+ size="sm"
373
+ onClick={handleSave}
374
+ disabled={isSaving}
375
+ >
376
+ {isSaving ? "Saving..." : "Save"}
377
+ </Button>
378
+ )}
379
+ </div>
380
+ </AccordionTrigger>
381
+ <AccordionContent className="p-0 pb-0">
382
+ <div className="grid grid-cols-3 gap-2.5 border border-t-0 border-primary/20 p-3">
383
+ {alerts.map((alert) => (
384
+ <AlertCard
385
+ key={alert.id}
386
+ {...alert}
387
+ selectedAction={actionMap[alert.id] ?? null}
388
+ onActionChange={handleActionChange(alert.id)}
389
+ />
390
+ ))}
391
+ </div>
392
+ </AccordionContent>
393
+ </AccordionItem>
394
+ </Accordion>
395
+ );
396
+ }
397
+
398
+ // ---------------------------------------------------------------------------
399
+ // LoanScenarioSection
400
+ // ---------------------------------------------------------------------------
401
+
402
+ export interface LoanScenarioSectionProps {
403
+ /** Override the section heading. Defaults to "Loan Scenario (Loan Quiz)". */
404
+ title?: string;
405
+ /** "Lending Type" — e.g. "Home Loan", "Investment Loan" */
406
+ lendingType?: string;
407
+ purposeOfLoan?: string;
408
+ loanAmount?: string;
409
+ propertyEstimate?: string;
410
+ estLvr?: string;
411
+ /** Cash or deposit available e.g. "$120,000" */
412
+ cashDeposit?: string;
413
+ propertyAddress?: string;
414
+ duration?: string;
415
+ importantFeatures?: string;
416
+ topThreePriorities?: string;
417
+ }
418
+
419
+ /**
420
+ * Read-only Loan Quiz card — 4-column grid matching the LoanQuiz component in backoffice.
421
+ *
422
+ * Row 1: Lending Type | Purpose of Loan | Loan Amount | Property Estimate
423
+ * Row 2: EST LVR | Cash/Deposit | Property Address | Duration
424
+ * Row 3: Important Features | Top Three Priorities
425
+ *
426
+ * Mirrors `LoanQuiz.tsx` in backoffice OpportunityDetailsDrawer.
427
+ */
428
+ export function LoanScenarioSection({
429
+ title = "Loan Scenario (Loan Quiz)",
430
+ lendingType,
431
+ purposeOfLoan,
432
+ loanAmount,
433
+ propertyEstimate,
434
+ estLvr,
435
+ cashDeposit,
436
+ propertyAddress,
437
+ duration,
438
+ importantFeatures,
439
+ topThreePriorities,
440
+ }: LoanScenarioSectionProps) {
441
+ return (
442
+ <div className="border border-border p-4">
443
+ <div className="mb-4">
444
+ <FinancialSectionLabel>{title}</FinancialSectionLabel>
445
+ </div>
446
+ <div className="grid grid-cols-4 gap-x-6 gap-y-4">
447
+ {/* Row 1 */}
448
+ <FinancialDetailField label="Lending Type" value={lendingType} />
449
+ <FinancialDetailField label="Purpose of Loan" value={purposeOfLoan} />
450
+ <FinancialDetailField label="Loan Amount" value={loanAmount} />
451
+ <FinancialDetailField
452
+ label="Property Estimate"
453
+ value={propertyEstimate}
454
+ />
455
+ {/* Row 2 */}
456
+ <FinancialDetailField label="EST LVR" value={estLvr} />
457
+ <FinancialDetailField label="Cash/Deposit" value={cashDeposit} />
458
+ <FinancialDetailField
459
+ label="Property Address"
460
+ value={propertyAddress}
461
+ />
462
+ <FinancialDetailField label="Duration" value={duration} />
463
+ {/* Row 3 */}
464
+ <FinancialDetailField
465
+ label="Important Features"
466
+ value={importantFeatures}
467
+ />
468
+ <FinancialDetailField
469
+ label="Top Three Priorities"
470
+ value={topThreePriorities}
471
+ />
472
+ </div>
473
+ </div>
474
+ );
475
+ }
476
+
477
+ // ---------------------------------------------------------------------------
478
+ // FinancialBottomSummary
479
+ // ---------------------------------------------------------------------------
480
+
481
+ export interface BottomSummaryLineItem {
482
+ label: string;
483
+ value?: string | null;
484
+ destructive?: boolean;
485
+ }
486
+
487
+ export interface FinancialBottomSummaryProps {
488
+ /** Incoming vs Outgoing Summary card items. */
489
+ cashflowItems?: BottomSummaryLineItem[];
490
+ /** Net surplus/deficit value e.g. "$1,050". */
491
+ netSurplus?: string;
492
+ /** Whether net surplus is negative (renders in destructive color). */
493
+ netSurplusDestructive?: boolean;
494
+ /** Assets card items. */
495
+ assetItems?: BottomSummaryLineItem[];
496
+ /** Total assets value e.g. "$3,630,000". */
497
+ totalAssets?: string;
498
+ /** Total liabilities value e.g. "$1,263,200". Renders in destructive. */
499
+ totalLiabilities?: string;
500
+ /** Net position value e.g. "$2,366,800". */
501
+ netPosition?: string;
502
+ /** Whether net position is negative. */
503
+ netPositionDestructive?: boolean;
504
+ }
505
+
506
+ /**
507
+ * Three summary cards stacked vertically:
508
+ * 1. Incoming vs Outgoing Summary — cashflow line items + net surplus
509
+ * 2. Assets — asset line items + total
510
+ * 3. Net Position — total liabilities + assets + net position
511
+ *
512
+ * Mirrors `financial-bottom-summary.tsx` in backoffice.
513
+ */
514
+ export function FinancialBottomSummary({
515
+ cashflowItems = [],
516
+ netSurplus,
517
+ netSurplusDestructive = false,
518
+ assetItems = [],
519
+ totalAssets,
520
+ totalLiabilities,
521
+ netPosition,
522
+ netPositionDestructive = false,
523
+ }: FinancialBottomSummaryProps) {
524
+ return (
525
+ <div className="border border-border">
526
+ {/* Card 1: Incoming vs Outgoing Summary */}
527
+ <div className="p-4">
528
+ <div className="mb-3">
529
+ <FinancialSectionLabel>
530
+ Incoming vs Outgoing Summary
531
+ </FinancialSectionLabel>
532
+ </div>
533
+ <div className="flex flex-col">
534
+ {cashflowItems.map((item) => (
535
+ <FinancialLineItem
536
+ key={item.label}
537
+ label={item.label}
538
+ value={item.value}
539
+ destructive={item.destructive}
540
+ />
541
+ ))}
542
+ </div>
543
+ <FinancialSubtotalFrame>
544
+ <FinancialSubtotalBlock
545
+ monthlyAverage={netSurplus}
546
+ label={netSurplusDestructive ? "Net Deficit" : "Net Surplus"}
547
+ />
548
+ </FinancialSubtotalFrame>
549
+ </div>
550
+
551
+ <Separator />
552
+
553
+ {/* Card 2: Assets */}
554
+ <div className="p-4">
555
+ <div className="mb-3">
556
+ <FinancialSectionLabel>Assets</FinancialSectionLabel>
557
+ </div>
558
+ <div className="flex flex-col">
559
+ {assetItems.map((item) => (
560
+ <FinancialLineItem
561
+ key={item.label}
562
+ label={item.label}
563
+ value={item.value}
564
+ destructive={item.destructive}
565
+ />
566
+ ))}
567
+ </div>
568
+ <FinancialSubtotalFrame>
569
+ <FinancialSubtotalBlock
570
+ monthlyAverage={totalAssets}
571
+ label="Total Assets"
572
+ />
573
+ </FinancialSubtotalFrame>
574
+ </div>
575
+
576
+ <Separator />
577
+
578
+ {/* Card 3: Net Position */}
579
+ <div className="p-4">
580
+ <div className="mb-3">
581
+ <FinancialSectionLabel>Net Position</FinancialSectionLabel>
582
+ </div>
583
+ <div className="flex flex-col">
584
+ <FinancialLineItem
585
+ label="Total Liabilities"
586
+ value={totalLiabilities}
587
+ destructive
588
+ />
589
+ <FinancialLineItem label="Total Assets" value={totalAssets} />
590
+ </div>
591
+ <FinancialSubtotalFrame>
592
+ <FinancialSubtotalBlock
593
+ monthlyAverage={netPosition}
594
+ label={netPositionDestructive ? "Net Deficit" : "Net Position"}
595
+ />
596
+ </FinancialSubtotalFrame>
597
+ </div>
598
+ </div>
599
+ );
600
+ }
601
+
602
+ // ---------------------------------------------------------------------------
603
+ // FinancialViewSection
604
+ // ---------------------------------------------------------------------------
605
+
606
+ export interface FinancialViewSectionProps {
607
+ /** Override the "Financial View" section label. */
608
+ title?: string;
609
+ income?: IncomeExpenseSectionProps["income"];
610
+ expenses?: IncomeExpenseSectionProps["expenses"];
611
+ futurePayments?: IncomeExpenseSectionProps["futurePayments"];
612
+ futurePaymentsHeading?: string;
613
+ /** Section heading for property columns. Defaults to "Property Assets and Liabilities". */
614
+ propertyTitle?: string;
615
+ /** Each item renders as a borderless property column with a 1px divider. */
616
+ properties?: PropertyCardProps[];
617
+ /** Section heading for current liabilities. Defaults to "Current Liabilities". */
618
+ currentLiabilitiesTitle?: string;
619
+ /** Each item maps 1:1 to a DebtCard. */
620
+ currentLiabilities?: DebtCardProps[];
621
+ }
622
+
623
+ /**
624
+ * Composite Financial View card — wraps income, property and current liabilities
625
+ * inside a single bordered container, matching the Figma Financial View design.
626
+ *
627
+ * Property columns render without individual card borders (borderless),
628
+ * separated by a 1px divider — same visual language as the income columns.
629
+ * Current Liabilities (DebtCards) retain their individual borders.
630
+ */
631
+ export function FinancialViewSection({
632
+ title = "Financial View",
633
+ income,
634
+ expenses,
635
+ futurePayments,
636
+ futurePaymentsHeading,
637
+ propertyTitle = "Property Assets and Liabilities",
638
+ properties = [],
639
+ currentLiabilitiesTitle = "Current Liabilities",
640
+ currentLiabilities = [],
641
+ }: FinancialViewSectionProps) {
642
+ return (
643
+ <div className="border border-border p-4 flex flex-col gap-4">
644
+ <FinancialSectionLabel>{title}</FinancialSectionLabel>
645
+ <IncomeExpenseSection
646
+ income={income}
647
+ expenses={expenses}
648
+ futurePayments={futurePayments}
649
+ futurePaymentsHeading={futurePaymentsHeading}
650
+ />
651
+ {properties.length > 0 && (
652
+ <>
653
+ <Separator />
654
+ <PropertyHoldingsSection
655
+ title={propertyTitle}
656
+ items={properties}
657
+ borderless
658
+ />
659
+ </>
660
+ )}
661
+ {currentLiabilities.length > 0 && (
662
+ <>
663
+ <Separator />
664
+ <DebtSection
665
+ title={currentLiabilitiesTitle}
666
+ items={currentLiabilities}
667
+ />
668
+ </>
669
+ )}
670
+ </div>
671
+ );
672
+ }