@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,579 @@
1
+ import * as React from "react";
2
+ import { useState } from "react";
3
+ import { Pencil } from "lucide-react";
4
+ import { Tabs, TabsList, TabsTrigger, TabsContent } from "./tabs";
5
+ import {
6
+ Accordion,
7
+ AccordionItem,
8
+ AccordionTrigger,
9
+ AccordionContent,
10
+ } from "./accordion";
11
+ import {
12
+ LoanScenarioSection,
13
+ FinancialBottomSummary,
14
+ } from "./financial-sections";
15
+ import { AboutCard, IncomeCard, ExpensesCard } from "./financial-cards";
16
+ import {
17
+ EditLoanScenarioModal,
18
+ EditAssetsModal,
19
+ EditDebtsModal,
20
+ EditAboutApplicantModal,
21
+ EditIncomeModal,
22
+ EditExpensesModal,
23
+ } from "./opportunity-edit-modals";
24
+ import type {
25
+ LoanScenarioFormData,
26
+ AssetLineItem,
27
+ DebtLineItem,
28
+ AboutApplicantFormData,
29
+ IncomeFormData,
30
+ ExpensesFormData,
31
+ } from "./opportunity-edit-modals";
32
+
33
+ /**
34
+ * OpportunitySummaryTab — WealthX DS (Level 5)
35
+ *
36
+ * Full Summary tab for the OpportunityDetailsDrawer.
37
+ * Renders:
38
+ * - Loan Application accordion (shared, above applicant sub-tabs)
39
+ * - Joint / Main / Co applicant sub-tabs (accordion sections per tab)
40
+ * - All inline edit modals (managed internally)
41
+ *
42
+ * Pass as the `summary` slot of `OpportunityDetailsDrawer`.
43
+ *
44
+ * ```tsx
45
+ * <OpportunityDetailsDrawer
46
+ * tabs={{
47
+ * summary: (
48
+ * <OpportunitySummaryTab
49
+ * isJoint={true}
50
+ * loanScenario={loanScenario}
51
+ * onLoanScenarioChange={setLoanScenario}
52
+ * assets={assets}
53
+ * onAssetsChange={setAssets}
54
+ * // … other data + callbacks
55
+ * />
56
+ * ),
57
+ * }}
58
+ * />
59
+ * ```
60
+ */
61
+
62
+ // ---------------------------------------------------------------------------
63
+ // Types
64
+ // ---------------------------------------------------------------------------
65
+
66
+ export interface OpportunitySummaryTabProps {
67
+ /** Whether this is a joint application (shows Co-Applicant tab). */
68
+ isJoint?: boolean;
69
+
70
+ // ── Loan scenario ──────────────────────────────────────────────────────────
71
+ loanScenario: LoanScenarioFormData;
72
+ onLoanScenarioChange?: (data: LoanScenarioFormData) => void;
73
+
74
+ // ── Joint assets & debts ──────────────────────────────────────────────────
75
+ assets: AssetLineItem[];
76
+ onAssetsChange?: (items: AssetLineItem[]) => void;
77
+ debts: DebtLineItem[];
78
+ onDebtsChange?: (items: DebtLineItem[]) => void;
79
+
80
+ // ── Main applicant ────────────────────────────────────────────────────────
81
+ mainAbout: AboutApplicantFormData;
82
+ onMainAboutChange?: (data: AboutApplicantFormData) => void;
83
+ mainIncome: IncomeFormData;
84
+ onMainIncomeChange?: (data: IncomeFormData) => void;
85
+ mainExpenses: ExpensesFormData;
86
+ onMainExpensesChange?: (data: ExpensesFormData) => void;
87
+
88
+ // ── Co-applicant (only relevant when isJoint) ─────────────────────────────
89
+ coAbout?: AboutApplicantFormData;
90
+ onCoAboutChange?: (data: AboutApplicantFormData) => void;
91
+ coIncome?: IncomeFormData;
92
+ onCoIncomeChange?: (data: IncomeFormData) => void;
93
+ coExpenses?: ExpensesFormData;
94
+ onCoExpensesChange?: (data: ExpensesFormData) => void;
95
+ }
96
+
97
+ // ---------------------------------------------------------------------------
98
+ // Internal helpers
99
+ // ---------------------------------------------------------------------------
100
+
101
+ function formatCurrency(n: number) {
102
+ return "$" + n.toLocaleString("en-AU");
103
+ }
104
+
105
+ function toMonthly(amount: number, freq: "Monthly" | "Weekly") {
106
+ return freq === "Monthly" ? amount : (amount * 52) / 12;
107
+ }
108
+
109
+ /**
110
+ * Pencil icon button rendered inline inside an AccordionTrigger.
111
+ * Uses stopPropagation so the click opens the edit modal without
112
+ * toggling the accordion open/closed state.
113
+ */
114
+ function TriggerEditButton({
115
+ onClick,
116
+ title = "Edit",
117
+ }: {
118
+ onClick: () => void;
119
+ title?: string;
120
+ }) {
121
+ return (
122
+ <button
123
+ type="button"
124
+ className="ml-auto mr-2 inline-flex size-6 shrink-0 items-center justify-center text-muted-foreground hover:text-foreground"
125
+ onClick={(e) => {
126
+ e.stopPropagation();
127
+ onClick();
128
+ }}
129
+ aria-label={title}
130
+ title={title}
131
+ >
132
+ <Pencil className="size-3.5" />
133
+ </button>
134
+ );
135
+ }
136
+
137
+ // ---------------------------------------------------------------------------
138
+ // ApplicantAccordionTab (internal)
139
+ // ---------------------------------------------------------------------------
140
+
141
+ interface ApplicantAccordionTabProps {
142
+ accordionPrefix: string;
143
+ about: AboutApplicantFormData;
144
+ income: IncomeFormData;
145
+ expenses: ExpensesFormData;
146
+ onEditAbout: () => void;
147
+ onEditIncome: () => void;
148
+ onEditExpenses: () => void;
149
+ }
150
+
151
+ function ApplicantAccordionTab({
152
+ accordionPrefix,
153
+ about,
154
+ income,
155
+ expenses,
156
+ onEditAbout,
157
+ onEditIncome,
158
+ onEditExpenses,
159
+ }: ApplicantAccordionTabProps) {
160
+ const totalMonthlyIncome = formatCurrency(
161
+ income.items.reduce(
162
+ (sum, i) => sum + toMonthly(i.incomeAmount, i.frequency),
163
+ 0,
164
+ ),
165
+ );
166
+ const totalMonthlyExpenses = formatCurrency(
167
+ expenses.items.reduce(
168
+ (sum, e) => sum + toMonthly(e.amount, e.frequency),
169
+ 0,
170
+ ),
171
+ );
172
+
173
+ return (
174
+ <Accordion
175
+ openMultiple
176
+ defaultValue={[
177
+ `${accordionPrefix}-about`,
178
+ `${accordionPrefix}-income`,
179
+ `${accordionPrefix}-expenses`,
180
+ ]}
181
+ >
182
+ <AccordionItem value={`${accordionPrefix}-about`}>
183
+ <AccordionTrigger>
184
+ About
185
+ <TriggerEditButton onClick={onEditAbout} />
186
+ </AccordionTrigger>
187
+ <AccordionContent className="text-foreground">
188
+ <AboutCard
189
+ title={about.title}
190
+ firstName={about.firstName}
191
+ lastName={about.lastName}
192
+ phone={about.phone}
193
+ email={about.email}
194
+ gender={about.gender}
195
+ maritalStatus={about.maritalStatus}
196
+ citizenStatus={about.citizenStatus}
197
+ propertyInTrust={about.propertyInTrust}
198
+ companyOwnership={about.companyOwnership}
199
+ />
200
+ </AccordionContent>
201
+ </AccordionItem>
202
+
203
+ <AccordionItem value={`${accordionPrefix}-income`}>
204
+ <AccordionTrigger>
205
+ Income
206
+ <TriggerEditButton onClick={onEditIncome} />
207
+ </AccordionTrigger>
208
+ <AccordionContent className="text-foreground">
209
+ <IncomeCard
210
+ items={income.items.map((i) => ({
211
+ incomeType: i.incomeType,
212
+ jobTitle: i.jobTitle,
213
+ companyName: i.companyName,
214
+ amountLabel: `${formatCurrency(i.incomeAmount)} / ${i.frequency}`,
215
+ }))}
216
+ totalMonthly={totalMonthlyIncome}
217
+ />
218
+ </AccordionContent>
219
+ </AccordionItem>
220
+
221
+ <AccordionItem value={`${accordionPrefix}-expenses`}>
222
+ <AccordionTrigger>
223
+ Expenses
224
+ <TriggerEditButton onClick={onEditExpenses} />
225
+ </AccordionTrigger>
226
+ <AccordionContent className="text-foreground">
227
+ <ExpensesCard
228
+ items={expenses.items.map((e) => ({
229
+ expenseType: e.expenseType,
230
+ amountLabel: `${formatCurrency(e.amount)} / ${e.frequency}`,
231
+ }))}
232
+ totalMonthly={totalMonthlyExpenses}
233
+ />
234
+ </AccordionContent>
235
+ </AccordionItem>
236
+ </Accordion>
237
+ );
238
+ }
239
+
240
+ // ---------------------------------------------------------------------------
241
+ // OpportunitySummaryTab (exported)
242
+ // ---------------------------------------------------------------------------
243
+
244
+ export function OpportunitySummaryTab({
245
+ isJoint = false,
246
+ loanScenario,
247
+ onLoanScenarioChange,
248
+ assets,
249
+ onAssetsChange,
250
+ debts,
251
+ onDebtsChange,
252
+ mainAbout,
253
+ onMainAboutChange,
254
+ mainIncome,
255
+ onMainIncomeChange,
256
+ mainExpenses,
257
+ onMainExpensesChange,
258
+ coAbout,
259
+ onCoAboutChange,
260
+ coIncome,
261
+ onCoIncomeChange,
262
+ coExpenses,
263
+ onCoExpensesChange,
264
+ }: OpportunitySummaryTabProps) {
265
+ // ── Sub-tab state ──────────────────────────────────────────────────────────
266
+ const [summarySubTab, setSummarySubTab] = useState<"joint" | "main" | "co">(
267
+ "joint",
268
+ );
269
+
270
+ // ── Modal open state (internal) ───────────────────────────────────────────
271
+ const [editLoanOpen, setEditLoanOpen] = useState(false);
272
+ const [editAssetsOpen, setEditAssetsOpen] = useState(false);
273
+ const [editDebtsOpen, setEditDebtsOpen] = useState(false);
274
+ const [editMainAboutOpen, setEditMainAboutOpen] = useState(false);
275
+ const [editCoAboutOpen, setEditCoAboutOpen] = useState(false);
276
+ const [editMainIncomeOpen, setEditMainIncomeOpen] = useState(false);
277
+ const [editCoIncomeOpen, setEditCoIncomeOpen] = useState(false);
278
+ const [editMainExpensesOpen, setEditMainExpensesOpen] = useState(false);
279
+ const [editCoExpensesOpen, setEditCoExpensesOpen] = useState(false);
280
+
281
+ // ── Derived totals ─────────────────────────────────────────────────────────
282
+ const totalAssetsAmount = assets.reduce((sum, a) => sum + a.value, 0);
283
+ const totalDebtsAmount = debts.reduce((sum, d) => sum + d.amountOwing, 0);
284
+ const totalAssets = formatCurrency(totalAssetsAmount);
285
+ const totalDebts = formatCurrency(totalDebtsAmount);
286
+
287
+ const allIncomeItems = [...mainIncome.items, ...(coIncome?.items ?? [])];
288
+ const allExpenseItems = [...mainExpenses.items, ...(coExpenses?.items ?? [])];
289
+ const combinedMonthlyIncome = allIncomeItems.reduce(
290
+ (sum, i) => sum + toMonthly(i.incomeAmount, i.frequency),
291
+ 0,
292
+ );
293
+ const combinedMonthlyExpenses = allExpenseItems.reduce(
294
+ (sum, e) => sum + toMonthly(e.amount, e.frequency),
295
+ 0,
296
+ );
297
+ const netSurplus = combinedMonthlyIncome - combinedMonthlyExpenses;
298
+
299
+ const mainName = [mainAbout.firstName, mainAbout.lastName]
300
+ .filter(Boolean)
301
+ .join(" ");
302
+ const coName = coAbout
303
+ ? [coAbout.firstName, coAbout.lastName].filter(Boolean).join(" ")
304
+ : "Co-Applicant";
305
+
306
+ return (
307
+ <>
308
+ {/* ── Loan Application — shared section, sits above applicant sub-tabs ── */}
309
+ <Accordion openMultiple defaultValue={["loan-app"]}>
310
+ <AccordionItem value="loan-app">
311
+ <AccordionTrigger>
312
+ Loan Application
313
+ <TriggerEditButton onClick={() => setEditLoanOpen(true)} />
314
+ </AccordionTrigger>
315
+ <AccordionContent className="text-foreground">
316
+ <LoanScenarioSection
317
+ lendingType="Home Loan"
318
+ purposeOfLoan={loanScenario.loanPurpose}
319
+ loanAmount={formatCurrency(loanScenario.loanAmount)}
320
+ propertyEstimate={formatCurrency(loanScenario.propertyEstimate)}
321
+ estLvr="—"
322
+ cashDeposit={formatCurrency(loanScenario.cashEquity)}
323
+ propertyAddress="—"
324
+ duration={`${loanScenario.loanDuration} years`}
325
+ importantFeatures={
326
+ [
327
+ loanScenario.featureVariableRate && "Variable Rate",
328
+ loanScenario.featureFixedRate && "Fixed Rate",
329
+ loanScenario.featureRedrawFacility && "Redraw Facility",
330
+ loanScenario.feature100Offset && "100% Offset",
331
+ loanScenario.featureSplitLoan && "Split Loan",
332
+ loanScenario.featureInterestOnly && "Interest Only",
333
+ ]
334
+ .filter(Boolean)
335
+ .join(", ") || "—"
336
+ }
337
+ topThreePriorities={loanScenario.priorities.join(", ") || "—"}
338
+ />
339
+ <div className="mt-4 flex gap-4">
340
+ {[
341
+ { label: "Number of Applicants", value: isJoint ? "2" : "1" },
342
+ { label: "Dependants", value: "1" },
343
+ ].map(({ label, value }) => (
344
+ <div key={label} className="flex flex-col gap-1">
345
+ <span className="text-label-small uppercase text-muted-foreground">
346
+ {label}
347
+ </span>
348
+ <div className="border border-border px-4 py-2">
349
+ <span className="text-label-medium text-foreground">
350
+ {value}
351
+ </span>
352
+ </div>
353
+ </div>
354
+ ))}
355
+ </div>
356
+ </AccordionContent>
357
+ </AccordionItem>
358
+ </Accordion>
359
+
360
+ {/* ── Applicant sub-tabs ── */}
361
+ <Tabs
362
+ value={summarySubTab}
363
+ onValueChange={(v) => setSummarySubTab(v as "joint" | "main" | "co")}
364
+ className="pb-6"
365
+ >
366
+ <div className="sticky top-0 z-10 bg-background pb-1 pt-2">
367
+ <TabsList variant="line">
368
+ <TabsTrigger value="joint">Joint</TabsTrigger>
369
+ <TabsTrigger value="main">{mainName}</TabsTrigger>
370
+ {isJoint && <TabsTrigger value="co">{coName}</TabsTrigger>}
371
+ </TabsList>
372
+ </div>
373
+
374
+ {/* ── Joint tab: combined data from Main + Co ── */}
375
+ <TabsContent value="joint">
376
+ <Accordion
377
+ openMultiple
378
+ defaultValue={["joint-overview", "joint-documents"]}
379
+ >
380
+ {/* Financial Overview — cashflow + assets + net position */}
381
+ <AccordionItem value="joint-overview">
382
+ <AccordionTrigger>
383
+ Financial Overview
384
+ <div className="ml-auto mr-2 flex items-center gap-0.5">
385
+ <button
386
+ type="button"
387
+ className="inline-flex size-6 shrink-0 items-center justify-center text-muted-foreground hover:text-foreground"
388
+ onClick={(e) => {
389
+ e.stopPropagation();
390
+ setEditAssetsOpen(true);
391
+ }}
392
+ aria-label="Edit Assets"
393
+ title="Edit Assets"
394
+ >
395
+ <Pencil className="size-3.5" />
396
+ </button>
397
+ <button
398
+ type="button"
399
+ className="inline-flex size-6 shrink-0 items-center justify-center text-muted-foreground hover:text-foreground"
400
+ onClick={(e) => {
401
+ e.stopPropagation();
402
+ setEditDebtsOpen(true);
403
+ }}
404
+ aria-label="Edit Debts"
405
+ title="Edit Debts"
406
+ >
407
+ <Pencil className="size-3.5" />
408
+ </button>
409
+ </div>
410
+ </AccordionTrigger>
411
+ <AccordionContent className="text-foreground">
412
+ <FinancialBottomSummary
413
+ cashflowItems={[
414
+ {
415
+ label: "Monthly Combined Income",
416
+ value: formatCurrency(Math.round(combinedMonthlyIncome)),
417
+ },
418
+ {
419
+ label: "Monthly Combined Expenses",
420
+ value: formatCurrency(
421
+ Math.round(combinedMonthlyExpenses),
422
+ ),
423
+ destructive: true,
424
+ },
425
+ ]}
426
+ netSurplus={formatCurrency(Math.abs(Math.round(netSurplus)))}
427
+ netSurplusDestructive={netSurplus < 0}
428
+ assetItems={assets.map((a) => ({
429
+ label: a.assetType,
430
+ value: formatCurrency(a.value),
431
+ }))}
432
+ totalAssets={totalAssets}
433
+ totalLiabilities={totalDebts}
434
+ netPosition={formatCurrency(
435
+ Math.abs(Math.round(totalAssetsAmount - totalDebtsAmount)),
436
+ )}
437
+ netPositionDestructive={totalAssetsAmount < totalDebtsAmount}
438
+ />
439
+ </AccordionContent>
440
+ </AccordionItem>
441
+
442
+ {/* Documents */}
443
+ <AccordionItem value="joint-documents">
444
+ <AccordionTrigger>Documents</AccordionTrigger>
445
+ <AccordionContent>
446
+ <p className="text-sm text-muted-foreground">
447
+ No documents uploaded yet.
448
+ </p>
449
+ </AccordionContent>
450
+ </AccordionItem>
451
+ </Accordion>
452
+ </TabsContent>
453
+
454
+ {/* ── Main Applicant tab ── */}
455
+ <TabsContent value="main">
456
+ <ApplicantAccordionTab
457
+ accordionPrefix="main"
458
+ about={mainAbout}
459
+ income={mainIncome}
460
+ expenses={mainExpenses}
461
+ onEditAbout={() => setEditMainAboutOpen(true)}
462
+ onEditIncome={() => setEditMainIncomeOpen(true)}
463
+ onEditExpenses={() => setEditMainExpensesOpen(true)}
464
+ />
465
+ </TabsContent>
466
+
467
+ {/* ── Co-Applicant tab (joint applications only) ── */}
468
+ {isJoint && coAbout && coIncome && coExpenses && (
469
+ <TabsContent value="co">
470
+ <ApplicantAccordionTab
471
+ accordionPrefix="co"
472
+ about={coAbout}
473
+ income={coIncome}
474
+ expenses={coExpenses}
475
+ onEditAbout={() => setEditCoAboutOpen(true)}
476
+ onEditIncome={() => setEditCoIncomeOpen(true)}
477
+ onEditExpenses={() => setEditCoExpensesOpen(true)}
478
+ />
479
+ </TabsContent>
480
+ )}
481
+ </Tabs>
482
+
483
+ {/* ── Edit modals (internal state) ── */}
484
+ <EditLoanScenarioModal
485
+ open={editLoanOpen}
486
+ onOpenChange={setEditLoanOpen}
487
+ initialData={loanScenario}
488
+ onSave={(data) => {
489
+ onLoanScenarioChange?.(data);
490
+ setEditLoanOpen(false);
491
+ }}
492
+ />
493
+ <EditAssetsModal
494
+ open={editAssetsOpen}
495
+ onOpenChange={setEditAssetsOpen}
496
+ initialItems={assets}
497
+ onSave={(items) => {
498
+ onAssetsChange?.(items);
499
+ setEditAssetsOpen(false);
500
+ }}
501
+ />
502
+ <EditDebtsModal
503
+ open={editDebtsOpen}
504
+ onOpenChange={setEditDebtsOpen}
505
+ initialItems={debts}
506
+ onSave={(items) => {
507
+ onDebtsChange?.(items);
508
+ setEditDebtsOpen(false);
509
+ }}
510
+ />
511
+ <EditAboutApplicantModal
512
+ open={editMainAboutOpen}
513
+ onOpenChange={setEditMainAboutOpen}
514
+ applicantLabel="Main Applicant"
515
+ initialData={mainAbout}
516
+ onSave={(data) => {
517
+ onMainAboutChange?.(data);
518
+ setEditMainAboutOpen(false);
519
+ }}
520
+ />
521
+ {coAbout && (
522
+ <EditAboutApplicantModal
523
+ open={editCoAboutOpen}
524
+ onOpenChange={setEditCoAboutOpen}
525
+ applicantLabel="Co-Applicant"
526
+ initialData={coAbout}
527
+ onSave={(data) => {
528
+ onCoAboutChange?.(data);
529
+ setEditCoAboutOpen(false);
530
+ }}
531
+ />
532
+ )}
533
+ <EditIncomeModal
534
+ open={editMainIncomeOpen}
535
+ onOpenChange={setEditMainIncomeOpen}
536
+ applicantLabel="Main Applicant"
537
+ initialData={mainIncome}
538
+ onSave={(data) => {
539
+ onMainIncomeChange?.(data);
540
+ setEditMainIncomeOpen(false);
541
+ }}
542
+ />
543
+ {coIncome && (
544
+ <EditIncomeModal
545
+ open={editCoIncomeOpen}
546
+ onOpenChange={setEditCoIncomeOpen}
547
+ applicantLabel="Co-Applicant"
548
+ initialData={coIncome}
549
+ onSave={(data) => {
550
+ onCoIncomeChange?.(data);
551
+ setEditCoIncomeOpen(false);
552
+ }}
553
+ />
554
+ )}
555
+ <EditExpensesModal
556
+ open={editMainExpensesOpen}
557
+ onOpenChange={setEditMainExpensesOpen}
558
+ applicantLabel="Main Applicant"
559
+ initialData={mainExpenses}
560
+ onSave={(data) => {
561
+ onMainExpensesChange?.(data);
562
+ setEditMainExpensesOpen(false);
563
+ }}
564
+ />
565
+ {coExpenses && (
566
+ <EditExpensesModal
567
+ open={editCoExpensesOpen}
568
+ onOpenChange={setEditCoExpensesOpen}
569
+ applicantLabel="Co-Applicant"
570
+ initialData={coExpenses}
571
+ onSave={(data) => {
572
+ onCoExpensesChange?.(data);
573
+ setEditCoExpensesOpen(false);
574
+ }}
575
+ />
576
+ )}
577
+ </>
578
+ );
579
+ }
@@ -0,0 +1,74 @@
1
+ import { cn } from "@/lib/utils";
2
+
3
+ /**
4
+ * PipelineAlerts — WealthX DS
5
+ *
6
+ * Compact card showing opportunity counts broken down by priority level
7
+ * (High / Medium / Low). Displayed alongside the PipelineChart in the
8
+ * Loan CRM page header section.
9
+ *
10
+ * Data source: derived from pipeline board columns by counting opportunity
11
+ * priorities across all stages.
12
+ */
13
+
14
+ // ---------------------------------------------------------------------------
15
+ // Types
16
+ // ---------------------------------------------------------------------------
17
+
18
+ export interface PipelineAlertCounts {
19
+ high: number;
20
+ medium: number;
21
+ low: number;
22
+ }
23
+
24
+ export interface PipelineAlertsProps {
25
+ counts: PipelineAlertCounts;
26
+ className?: string;
27
+ }
28
+
29
+ // ---------------------------------------------------------------------------
30
+ // PipelineAlerts
31
+ // ---------------------------------------------------------------------------
32
+
33
+ const PRIORITY_ROWS: Array<{
34
+ key: keyof PipelineAlertCounts;
35
+ label: string;
36
+ dotColor: string;
37
+ }> = [
38
+ { key: "high", label: "High", dotColor: "var(--color-destructive)" },
39
+ { key: "medium", label: "Medium", dotColor: "var(--color-warning)" },
40
+ { key: "low", label: "Low", dotColor: "var(--color-primary)" },
41
+ ];
42
+
43
+ export function PipelineAlerts({ counts, className }: PipelineAlertsProps) {
44
+ return (
45
+ <div
46
+ className={cn(
47
+ "flex flex-col gap-2 border border-border bg-background px-4 py-3",
48
+ className,
49
+ )}
50
+ data-slot="pipeline-alerts"
51
+ >
52
+ <span className="text-sm font-semibold text-foreground">
53
+ Pipeline Alerts
54
+ </span>
55
+ <div className="flex flex-col gap-1.5">
56
+ {PRIORITY_ROWS.map(({ key, label, dotColor }) => (
57
+ <div key={key} className="flex items-center gap-2">
58
+ <span
59
+ className="size-2 shrink-0 rounded-full"
60
+ style={{ backgroundColor: dotColor }}
61
+ aria-hidden="true"
62
+ />
63
+ <span className="flex-1 text-xs text-muted-foreground">
64
+ {label}
65
+ </span>
66
+ <span className="min-w-[1.5rem] text-right text-xs font-medium tabular-nums text-foreground">
67
+ {counts[key]}
68
+ </span>
69
+ </div>
70
+ ))}
71
+ </div>
72
+ </div>
73
+ );
74
+ }