@wealthx/shadcn 1.3.2 → 1.3.4

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 (313) hide show
  1. package/.turbo/turbo-build.log +227 -191
  2. package/CHANGELOG.md +12 -0
  3. package/dist/{chunk-2UM72RJ7.mjs → chunk-2D3HQPFN.mjs} +12 -10
  4. package/dist/chunk-2EM2FRU6.mjs +613 -0
  5. package/dist/{chunk-FH6QVUVZ.mjs → chunk-2GIYVERS.mjs} +2 -2
  6. package/dist/chunk-2P7HP7LR.mjs +68 -0
  7. package/dist/{chunk-HISNT2MG.mjs → chunk-37AE3OM5.mjs} +5 -5
  8. package/dist/{chunk-HBZLGDIN.mjs → chunk-3ERBUVHC.mjs} +169 -110
  9. package/dist/{chunk-C7CQJNMR.mjs → chunk-3VDET466.mjs} +2 -2
  10. package/dist/{chunk-462HMNO4.mjs → chunk-4MM7LHM5.mjs} +2 -2
  11. package/dist/{chunk-QMY3AZJH.mjs → chunk-4Z66LMIQ.mjs} +2 -2
  12. package/dist/{chunk-U5X52X37.mjs → chunk-57ZXILTS.mjs} +6 -6
  13. package/dist/{chunk-2A5RRQGG.mjs → chunk-5NF6T2RS.mjs} +11 -20
  14. package/dist/{chunk-3OYFOX3X.mjs → chunk-5VOTTIXF.mjs} +2 -2
  15. package/dist/{chunk-LBMRIB3G.mjs → chunk-6AJUS7VX.mjs} +1 -1
  16. package/dist/{chunk-OODBHKG7.mjs → chunk-6HIOM2HL.mjs} +7 -4
  17. package/dist/{chunk-BDYZCBRT.mjs → chunk-6QAFGZC2.mjs} +2 -2
  18. package/dist/{chunk-U4NDAF2P.mjs → chunk-6TX73WG7.mjs} +1 -1
  19. package/dist/{chunk-GD4BJDJR.mjs → chunk-7BTFGCFC.mjs} +4 -4
  20. package/dist/{chunk-FAKPBKLT.mjs → chunk-7GWRPXHD.mjs} +4 -4
  21. package/dist/{chunk-NMOI6CQD.mjs → chunk-7YI3HEBH.mjs} +5 -5
  22. package/dist/{chunk-T4BJLT57.mjs → chunk-AE7MASLF.mjs} +5 -5
  23. package/dist/{chunk-VLQZANBF.mjs → chunk-AFML43VJ.mjs} +6 -1
  24. package/dist/chunk-BBXSNDS3.mjs +260 -0
  25. package/dist/chunk-BOW7U26Y.mjs +203 -0
  26. package/dist/{chunk-34NWQURD.mjs → chunk-BS75ICOO.mjs} +2 -2
  27. package/dist/chunk-D2NSIIXG.mjs +394 -0
  28. package/dist/{chunk-3GF7OVTP.mjs → chunk-DGNHGNYH.mjs} +2 -2
  29. package/dist/{chunk-VLARHE5V.mjs → chunk-DMXYRCHM.mjs} +6 -6
  30. package/dist/{chunk-OGOYQ7BG.mjs → chunk-DQB4EPIS.mjs} +1 -1
  31. package/dist/{chunk-MIZQHHUO.mjs → chunk-FL6DZFJK.mjs} +106 -38
  32. package/dist/{chunk-I3RZS7V2.mjs → chunk-FLL633WS.mjs} +19 -33
  33. package/dist/{chunk-PBL4OQV2.mjs → chunk-FTPBQVQ6.mjs} +4 -4
  34. package/dist/chunk-FYPSTTEJ.mjs +169 -0
  35. package/dist/{chunk-6O6KD7CE.mjs → chunk-G27TSQLQ.mjs} +6 -6
  36. package/dist/{chunk-66MI7Q4B.mjs → chunk-GT3RU6GA.mjs} +2 -2
  37. package/dist/{chunk-D6ID6M4V.mjs → chunk-GTAVSBDO.mjs} +2 -2
  38. package/dist/{chunk-24FUO7TD.mjs → chunk-H6NQTIF4.mjs} +2 -2
  39. package/dist/{chunk-7DHU4VGG.mjs → chunk-HK4HUQTV.mjs} +2 -2
  40. package/dist/chunk-I4KVSZCH.mjs +101 -0
  41. package/dist/{chunk-RGVKLTLH.mjs → chunk-IKXYTCSB.mjs} +2 -2
  42. package/dist/{chunk-Y6DWJSKZ.mjs → chunk-ISUA7DSB.mjs} +1 -1
  43. package/dist/{chunk-J5UICVJS.mjs → chunk-JPGL36WQ.mjs} +2 -2
  44. package/dist/{chunk-7XJHLGUV.mjs → chunk-JTK6VJXY.mjs} +2 -2
  45. package/dist/{chunk-7YAU5CY6.mjs → chunk-JVMXMFBB.mjs} +2 -2
  46. package/dist/{chunk-IAE3F7DR.mjs → chunk-JZY6TNIS.mjs} +21 -21
  47. package/dist/{chunk-K5A5L6T2.mjs → chunk-K4KOD3KR.mjs} +12 -12
  48. package/dist/{chunk-MBON7YRJ.mjs → chunk-K5QV4TT6.mjs} +3 -3
  49. package/dist/{chunk-IHMFS7NZ.mjs → chunk-K5VHK7CM.mjs} +21 -21
  50. package/dist/{chunk-RJI6GKVF.mjs → chunk-KCWNDYPZ.mjs} +5 -5
  51. package/dist/{chunk-UFYSFDER.mjs → chunk-KFH36NKF.mjs} +1 -1
  52. package/dist/{chunk-EBXQWIYG.mjs → chunk-KLTACJ2G.mjs} +5 -5
  53. package/dist/{chunk-3TTACBDP.mjs → chunk-KWD6GANL.mjs} +4 -4
  54. package/dist/{chunk-IOJRDS6V.mjs → chunk-L4NSRQ3T.mjs} +218 -147
  55. package/dist/{chunk-GYMYRIZP.mjs → chunk-LBTHZSBT.mjs} +2 -2
  56. package/dist/{chunk-AMQZRHEZ.mjs → chunk-LQULK2E3.mjs} +5 -5
  57. package/dist/{chunk-YJG55G2H.mjs → chunk-LR6LHDP3.mjs} +5 -5
  58. package/dist/{chunk-7PV3IWCN.mjs → chunk-M4VYX2PV.mjs} +19 -1
  59. package/dist/{chunk-P76HMUI6.mjs → chunk-MDUKXXIL.mjs} +2 -2
  60. package/dist/{chunk-LV35NGVG.mjs → chunk-N6Q5IPKT.mjs} +9 -9
  61. package/dist/{chunk-DOEO3CDL.mjs → chunk-NB3ZL36B.mjs} +1 -1
  62. package/dist/{chunk-XREGSKX3.mjs → chunk-NOOEKOWY.mjs} +5 -5
  63. package/dist/{chunk-NL3ZO62D.mjs → chunk-NT4FX27K.mjs} +1 -1
  64. package/dist/{chunk-QZ4RE6NA.mjs → chunk-NTYQWVLI.mjs} +6 -6
  65. package/dist/{chunk-ERGGHC2V.mjs → chunk-OEOOYMC2.mjs} +2 -2
  66. package/dist/{chunk-4GAWMKMI.mjs → chunk-OIKBW2QD.mjs} +291 -54
  67. package/dist/{chunk-DUJTAXMH.mjs → chunk-OKTJFDPN.mjs} +6 -6
  68. package/dist/chunk-OLKMCXAR.mjs +1219 -0
  69. package/dist/{chunk-EI5F6FMT.mjs → chunk-OWFQSXVD.mjs} +3 -3
  70. package/dist/{chunk-6DZEXFNB.mjs → chunk-P2N2PEFY.mjs} +3 -3
  71. package/dist/{chunk-NSLMILBT.mjs → chunk-P7CEBZM6.mjs} +2 -2
  72. package/dist/{chunk-7S5AESZO.mjs → chunk-PNRUH7JY.mjs} +6 -6
  73. package/dist/{chunk-ZU4NV6RG.mjs → chunk-PNSYFE3K.mjs} +2 -2
  74. package/dist/{chunk-JKGDCQTZ.mjs → chunk-QTRSCVQ3.mjs} +5 -5
  75. package/dist/{chunk-ABFDMHOR.mjs → chunk-QX7IFQSF.mjs} +5 -5
  76. package/dist/{chunk-CFMQP5QS.mjs → chunk-QXKGOMUX.mjs} +6 -6
  77. package/dist/{chunk-NQPOYKAQ.mjs → chunk-R2ON6CAN.mjs} +2 -2
  78. package/dist/{chunk-DBHJ5KC3.mjs → chunk-R4HCRDU5.mjs} +1 -1
  79. package/dist/{chunk-EWRB4PAD.mjs → chunk-RCAOCHWA.mjs} +14 -14
  80. package/dist/{chunk-EFRENWEJ.mjs → chunk-RSUIPKGX.mjs} +2 -2
  81. package/dist/{chunk-DGHAXJBN.mjs → chunk-S2FKV4M5.mjs} +5 -5
  82. package/dist/{chunk-RGU7HOEC.mjs → chunk-SET2ANTY.mjs} +5 -7
  83. package/dist/chunk-SFH2NJEJ.mjs +47 -0
  84. package/dist/{chunk-6AW4KJHE.mjs → chunk-SIVYAI3M.mjs} +12 -12
  85. package/dist/{chunk-5FQIKDKP.mjs → chunk-THVO2N47.mjs} +8 -8
  86. package/dist/{chunk-JMHR3YGZ.mjs → chunk-TLAWKTSA.mjs} +3 -3
  87. package/dist/{chunk-HVY6KCCF.mjs → chunk-TOWTPLRC.mjs} +68 -72
  88. package/dist/{chunk-6JQFUE5I.mjs → chunk-UALR6JGV.mjs} +2 -2
  89. package/dist/{chunk-N6TNTQL6.mjs → chunk-UJZ4UHWI.mjs} +9 -11
  90. package/dist/{chunk-MARPPFOJ.mjs → chunk-UNACI2YK.mjs} +2 -2
  91. package/dist/{chunk-3NCUZIFP.mjs → chunk-V6XGXYCJ.mjs} +7 -7
  92. package/dist/chunk-VB5M6OZQ.mjs +57 -0
  93. package/dist/{chunk-5IS7G74I.mjs → chunk-VY5NEUP7.mjs} +6 -6
  94. package/dist/{chunk-JHJHG4GO.mjs → chunk-WE4YKBDE.mjs} +2 -2
  95. package/dist/{chunk-BKNFWEH2.mjs → chunk-WL6WVV47.mjs} +3 -3
  96. package/dist/{chunk-FWCSY2DS.mjs → chunk-WNQUEZJF.mjs} +22 -1
  97. package/dist/{chunk-2Y7YJKPE.mjs → chunk-WZ6UJCBL.mjs} +1 -1
  98. package/dist/{chunk-UMTOX62O.mjs → chunk-XYPW2XA5.mjs} +13 -10
  99. package/dist/chunk-Y2MTAVAK.mjs +34 -0
  100. package/dist/{chunk-6CR5N2JW.mjs → chunk-YCWLFG27.mjs} +6 -6
  101. package/dist/{chunk-PU4YZQXV.mjs → chunk-YE67AALL.mjs} +12 -12
  102. package/dist/{chunk-M3FV7LOK.mjs → chunk-YEWNFK5S.mjs} +6 -1
  103. package/dist/{chunk-R3VSPKNP.mjs → chunk-YIZHS72Z.mjs} +11 -12
  104. package/dist/{chunk-7PYJD5JI.mjs → chunk-ZEDMKQK2.mjs} +2 -2
  105. package/dist/{chunk-N2PT566P.mjs → chunk-ZFCDYW6N.mjs} +4 -4
  106. package/dist/chunk-ZGQIVGIN.mjs +57 -0
  107. package/dist/{chunk-Q2BGOAMG.mjs → chunk-ZKWXDQDG.mjs} +4 -4
  108. package/dist/{chunk-GHC7LLUX.mjs → chunk-ZOWL2L5J.mjs} +5 -5
  109. package/dist/components/ui/accordion.mjs +3 -3
  110. package/dist/components/ui/add-column-modal.js +2 -2
  111. package/dist/components/ui/add-column-modal.mjs +10 -10
  112. package/dist/components/ui/add-lead-modal.js +424 -82
  113. package/dist/components/ui/add-lead-modal.mjs +12 -9
  114. package/dist/components/ui/advisor-card.js +2 -2
  115. package/dist/components/ui/advisor-card.mjs +8 -8
  116. package/dist/components/ui/ai-assistant-drawer.js +2 -2
  117. package/dist/components/ui/ai-assistant-drawer.mjs +9 -9
  118. package/dist/components/ui/ai-builder.js +958 -0
  119. package/dist/components/ui/ai-builder.mjs +25 -0
  120. package/dist/components/ui/ai-conversations.js +2045 -0
  121. package/dist/components/ui/ai-conversations.mjs +41 -0
  122. package/dist/components/ui/alert-dialog.js +2 -2
  123. package/dist/components/ui/alert-dialog.mjs +5 -5
  124. package/dist/components/ui/alert.mjs +3 -3
  125. package/dist/components/ui/appointment-action-dialogs.js +19 -3
  126. package/dist/components/ui/appointment-action-dialogs.mjs +15 -14
  127. package/dist/components/ui/appointment-availability-settings.js +181 -111
  128. package/dist/components/ui/appointment-availability-settings.mjs +20 -18
  129. package/dist/components/ui/appointment-book-dialog.js +113 -24
  130. package/dist/components/ui/appointment-book-dialog.mjs +21 -20
  131. package/dist/components/ui/appointment-calendar-view.js +19 -3
  132. package/dist/components/ui/appointment-calendar-view.mjs +10 -9
  133. package/dist/components/ui/appointment-detail-sheet.js +19 -3
  134. package/dist/components/ui/appointment-detail-sheet.mjs +18 -17
  135. package/dist/components/ui/appointment-gmail-connect.js +49 -89
  136. package/dist/components/ui/appointment-gmail-connect.mjs +8 -9
  137. package/dist/components/ui/appointment-mini-card.js +2 -2
  138. package/dist/components/ui/appointment-mini-card.mjs +6 -6
  139. package/dist/components/ui/appointment-time-slot-picker.mjs +6 -6
  140. package/dist/components/ui/appointment-upcoming-card.js +19 -3
  141. package/dist/components/ui/appointment-upcoming-card.mjs +15 -14
  142. package/dist/components/ui/auth-logo.js +95 -0
  143. package/dist/components/ui/auth-logo.mjs +8 -0
  144. package/dist/components/ui/auth-page-layout.js +108 -0
  145. package/dist/components/ui/auth-page-layout.mjs +8 -0
  146. package/dist/components/ui/avatar.mjs +3 -3
  147. package/dist/components/ui/backoffice-alert-history-chart.js +2 -2
  148. package/dist/components/ui/backoffice-alert-history-chart.mjs +9 -9
  149. package/dist/components/ui/backoffice-alerts-chart.js +2 -2
  150. package/dist/components/ui/backoffice-alerts-chart.mjs +11 -11
  151. package/dist/components/ui/backoffice-connections-chart.js +2 -2
  152. package/dist/components/ui/backoffice-connections-chart.mjs +11 -11
  153. package/dist/components/ui/backoffice-contact-history-chart.js +2 -2
  154. package/dist/components/ui/backoffice-contact-history-chart.mjs +9 -9
  155. package/dist/components/ui/badge.mjs +4 -4
  156. package/dist/components/ui/borrowing-capacity-line-chart.js +145 -132
  157. package/dist/components/ui/borrowing-capacity-line-chart.mjs +9 -9
  158. package/dist/components/ui/button.js +2 -2
  159. package/dist/components/ui/button.mjs +4 -4
  160. package/dist/components/ui/calendar.js +17 -3
  161. package/dist/components/ui/calendar.mjs +6 -5
  162. package/dist/components/ui/card.mjs +3 -3
  163. package/dist/components/ui/cash-balance-line-chart.js +158 -158
  164. package/dist/components/ui/cash-balance-line-chart.mjs +9 -9
  165. package/dist/components/ui/cashflow-bar-chart.js +2 -2
  166. package/dist/components/ui/cashflow-bar-chart.mjs +9 -9
  167. package/dist/components/ui/chat-widget-primitives.js +573 -0
  168. package/dist/components/ui/chat-widget-primitives.mjs +21 -0
  169. package/dist/components/ui/chat-widget.js +1268 -0
  170. package/dist/components/ui/chat-widget.mjs +29 -0
  171. package/dist/components/ui/checkbox.mjs +3 -3
  172. package/dist/components/ui/chip.js +2 -2
  173. package/dist/components/ui/chip.mjs +6 -6
  174. package/dist/components/ui/color-picker.js +2 -2
  175. package/dist/components/ui/color-picker.mjs +7 -7
  176. package/dist/components/ui/combobox.mjs +3 -3
  177. package/dist/components/ui/data-table.js +2 -2
  178. package/dist/components/ui/data-table.mjs +12 -12
  179. package/dist/components/ui/date-picker.js +22 -6
  180. package/dist/components/ui/date-picker.mjs +9 -8
  181. package/dist/components/ui/dialog.js +2 -2
  182. package/dist/components/ui/dialog.mjs +5 -5
  183. package/dist/components/ui/document-checklist-template.js +630 -0
  184. package/dist/components/ui/document-checklist-template.mjs +15 -0
  185. package/dist/components/ui/drawer.js +2 -2
  186. package/dist/components/ui/drawer.mjs +3 -3
  187. package/dist/components/ui/dropdown-menu.mjs +3 -3
  188. package/dist/components/ui/empty.mjs +3 -3
  189. package/dist/components/ui/expense-bar-chart.js +2 -2
  190. package/dist/components/ui/expense-bar-chart.mjs +9 -9
  191. package/dist/components/ui/field.mjs +5 -5
  192. package/dist/components/ui/financial-cards.js +431 -291
  193. package/dist/components/ui/financial-cards.mjs +10 -9
  194. package/dist/components/ui/financial-drawers.js +4 -4
  195. package/dist/components/ui/financial-drawers.mjs +8 -8
  196. package/dist/components/ui/financial-primitives.mjs +3 -3
  197. package/dist/components/ui/financial-sections.js +8 -9
  198. package/dist/components/ui/financial-sections.mjs +12 -12
  199. package/dist/components/ui/form-primitives.mjs +8 -8
  200. package/dist/components/ui/income-bar-chart.js +2 -2
  201. package/dist/components/ui/income-bar-chart.mjs +9 -9
  202. package/dist/components/ui/input-group.js +2 -2
  203. package/dist/components/ui/input-group.mjs +7 -7
  204. package/dist/components/ui/input-otp.mjs +3 -3
  205. package/dist/components/ui/input.mjs +3 -3
  206. package/dist/components/ui/kanban-column.js +19 -23
  207. package/dist/components/ui/kanban-column.mjs +14 -14
  208. package/dist/components/ui/label.mjs +3 -3
  209. package/dist/components/ui/onboarding-layout.js +476 -0
  210. package/dist/components/ui/onboarding-layout.mjs +11 -0
  211. package/dist/components/ui/opportunity-card.js +2 -2
  212. package/dist/components/ui/opportunity-card.mjs +12 -12
  213. package/dist/components/ui/opportunity-edit-modals.js +22 -6
  214. package/dist/components/ui/opportunity-edit-modals.mjs +21 -20
  215. package/dist/components/ui/opportunity-summary-tab.js +991 -674
  216. package/dist/components/ui/opportunity-summary-tab.mjs +26 -26
  217. package/dist/components/ui/page-header.mjs +3 -3
  218. package/dist/components/ui/page-top-bar.mjs +3 -3
  219. package/dist/components/ui/pagination.js +2 -2
  220. package/dist/components/ui/pagination.mjs +6 -6
  221. package/dist/components/ui/password-strength-tooltip.js +197 -0
  222. package/dist/components/ui/password-strength-tooltip.mjs +11 -0
  223. package/dist/components/ui/pipeline-alerts.mjs +3 -3
  224. package/dist/components/ui/pipeline-board.js +19 -23
  225. package/dist/components/ui/pipeline-board.mjs +18 -18
  226. package/dist/components/ui/pipeline-chart.js +12 -6
  227. package/dist/components/ui/pipeline-chart.mjs +4 -3
  228. package/dist/components/ui/pipeline-dialogs.js +28 -12
  229. package/dist/components/ui/pipeline-dialogs.mjs +14 -13
  230. package/dist/components/ui/pipeline-primitives.mjs +6 -6
  231. package/dist/components/ui/popover.mjs +3 -3
  232. package/dist/components/ui/progress.mjs +3 -3
  233. package/dist/components/ui/property-cashflow-doughnut-chart.js +2 -2
  234. package/dist/components/ui/property-cashflow-doughnut-chart.mjs +9 -9
  235. package/dist/components/ui/property-debt-equity-doughnut-chart.js +2 -2
  236. package/dist/components/ui/property-debt-equity-doughnut-chart.mjs +9 -9
  237. package/dist/components/ui/property-mobile-estimate-line-chart.js +2 -2
  238. package/dist/components/ui/property-mobile-estimate-line-chart.mjs +9 -9
  239. package/dist/components/ui/radio-group.mjs +3 -3
  240. package/dist/components/ui/select.mjs +3 -3
  241. package/dist/components/ui/separator.mjs +3 -3
  242. package/dist/components/ui/sheet.mjs +3 -3
  243. package/dist/components/ui/sidebar-nav.js +6 -5
  244. package/dist/components/ui/sidebar-nav.mjs +7 -7
  245. package/dist/components/ui/skeleton.mjs +3 -3
  246. package/dist/components/ui/slider.mjs +3 -3
  247. package/dist/components/ui/sonner.mjs +2 -2
  248. package/dist/components/ui/spinner.mjs +3 -3
  249. package/dist/components/ui/stage-timeline.mjs +10 -10
  250. package/dist/components/ui/stepper.mjs +3 -3
  251. package/dist/components/ui/switch.mjs +3 -3
  252. package/dist/components/ui/table.mjs +3 -3
  253. package/dist/components/ui/tabs.mjs +3 -3
  254. package/dist/components/ui/textarea.mjs +3 -3
  255. package/dist/components/ui/toggle-group.mjs +4 -4
  256. package/dist/components/ui/toggle.mjs +3 -3
  257. package/dist/components/ui/tooltip.mjs +3 -3
  258. package/dist/components/ui/transactions-expense-categories-doughnut-chart.js +2 -2
  259. package/dist/components/ui/transactions-expense-categories-doughnut-chart.mjs +9 -9
  260. package/dist/components/ui/transactions-income-expense-bar-chart.js +2 -2
  261. package/dist/components/ui/transactions-income-expense-bar-chart.mjs +9 -9
  262. package/dist/components/ui/transactions-liabilities-breakdown-doughnut-chart.js +2 -2
  263. package/dist/components/ui/transactions-liabilities-breakdown-doughnut-chart.mjs +9 -9
  264. package/dist/components/ui/two-fa-setup-form.js +612 -0
  265. package/dist/components/ui/two-fa-setup-form.mjs +16 -0
  266. package/dist/components/ui/upload-card.js +187 -0
  267. package/dist/components/ui/upload-card.mjs +10 -0
  268. package/dist/components/ui/video-background.js +118 -0
  269. package/dist/components/ui/video-background.mjs +8 -0
  270. package/dist/index.js +12674 -9311
  271. package/dist/index.mjs +341 -245
  272. package/dist/lib/colors.mjs +1 -1
  273. package/dist/lib/theme-provider.mjs +1 -1
  274. package/dist/lib/typography.mjs +2 -2
  275. package/dist/lib/utils.js +8 -2
  276. package/dist/lib/utils.mjs +6 -4
  277. package/dist/styles.css +1 -1
  278. package/package.json +61 -1
  279. package/src/components/index.tsx +126 -1
  280. package/src/components/ui/add-lead-modal.tsx +101 -142
  281. package/src/components/ui/ai-builder.tsx +560 -0
  282. package/src/components/ui/ai-conversations.tsx +1690 -0
  283. package/src/components/ui/appointment-availability-settings.tsx +152 -101
  284. package/src/components/ui/appointment-book-dialog.tsx +138 -24
  285. package/src/components/ui/appointment-calendar-view.tsx +2 -3
  286. package/src/components/ui/appointment-gmail-connect.tsx +23 -42
  287. package/src/components/ui/auth-logo.tsx +50 -0
  288. package/src/components/ui/auth-page-layout.tsx +59 -0
  289. package/src/components/ui/borrowing-capacity-line-chart.tsx +10 -8
  290. package/src/components/ui/button.tsx +2 -2
  291. package/src/components/ui/calendar.tsx +2 -1
  292. package/src/components/ui/cash-balance-line-chart.tsx +11 -20
  293. package/src/components/ui/chart-shared.tsx +10 -0
  294. package/src/components/ui/chat-widget-primitives.tsx +336 -0
  295. package/src/components/ui/chat-widget.tsx +822 -0
  296. package/src/components/ui/document-checklist-template.tsx +264 -0
  297. package/src/components/ui/drawer.tsx +2 -2
  298. package/src/components/ui/financial-cards.tsx +176 -78
  299. package/src/components/ui/financial-drawers.tsx +2 -2
  300. package/src/components/ui/financial-sections.tsx +1 -1
  301. package/src/components/ui/kanban-column.tsx +2 -5
  302. package/src/components/ui/onboarding-layout.tsx +109 -0
  303. package/src/components/ui/opportunity-summary-tab.tsx +469 -142
  304. package/src/components/ui/password-strength-tooltip.tsx +70 -0
  305. package/src/components/ui/pipeline-chart.tsx +2 -6
  306. package/src/components/ui/sidebar-nav.tsx +1 -11
  307. package/src/components/ui/two-fa-setup-form.tsx +229 -0
  308. package/src/components/ui/upload-card.tsx +98 -0
  309. package/src/components/ui/video-background.tsx +55 -0
  310. package/src/lib/format-date.ts +26 -0
  311. package/src/lib/utils.ts +11 -0
  312. package/src/styles/styles-css.ts +1 -1
  313. package/tsup.config.ts +13 -0
@@ -1,7 +1,24 @@
1
1
  import * as React from "react";
2
2
  import { useState } from "react";
3
- import { ChevronDown, ChevronUp, Mail, Pencil, Phone } from "lucide-react";
3
+ import {
4
+ AlertCircle,
5
+ CheckCircle2,
6
+ Download,
7
+ FileText,
8
+ Mail,
9
+ Pencil,
10
+ Phone,
11
+ } from "lucide-react";
12
+ import { Badge } from "./badge";
13
+ import {
14
+ Select,
15
+ SelectContent,
16
+ SelectItem,
17
+ SelectTrigger,
18
+ SelectValue,
19
+ } from "./select";
4
20
  import { Button } from "./button";
21
+ import { Checkbox } from "./checkbox";
5
22
  import { cn } from "@/lib/utils";
6
23
  import { formatCurrency } from "@/lib/format-currency";
7
24
  import { PROPERTY_ASSET_TYPES } from "@/lib/opportunity-constants";
@@ -56,6 +73,20 @@ import type {
56
73
  // Types
57
74
  // ---------------------------------------------------------------------------
58
75
 
76
+ export interface DocumentItem {
77
+ id: string;
78
+ name: string;
79
+ documentType: string;
80
+ /** Which applicant uploaded this document. */
81
+ uploadedBy: "main" | "co";
82
+ uploadedAt: string;
83
+ status: "verified" | "pending" | "rejected";
84
+ /** PDF URL — opens in browser when title is clicked. */
85
+ url?: string;
86
+ /** Document checklist category this satisfies (e.g. "Income Verification"). */
87
+ checklistItem?: string;
88
+ }
89
+
59
90
  export interface OpportunitySummaryTabProps {
60
91
  /** Whether this is a joint application (shows Co-Applicant tab). */
61
92
  isJoint?: boolean;
@@ -85,6 +116,16 @@ export interface OpportunitySummaryTabProps {
85
116
  onCoIncomeChange?: (data: IncomeFormData) => void;
86
117
  coExpenses?: ExpensesFormData;
87
118
  onCoExpensesChange?: (data: ExpensesFormData) => void;
119
+
120
+ // ── Documents ─────────────────────────────────────────────────────────────
121
+ documents?: DocumentItem[];
122
+ /** Called with the selected documents when advisor clicks Download. */
123
+ onDocumentsDownload?: (docs: DocumentItem[]) => void;
124
+ /** Called when advisor changes a document's verification status. */
125
+ onDocumentStatusChange?: (
126
+ docId: string,
127
+ status: DocumentItem["status"],
128
+ ) => void;
88
129
  }
89
130
 
90
131
  // ---------------------------------------------------------------------------
@@ -128,25 +169,15 @@ function lvrColorClass(pct: number): string {
128
169
  return "text-destructive";
129
170
  }
130
171
 
131
- /** Serviceability label + badge class based on net monthly surplus. */
172
+ /** Serviceability label + badge variant based on net monthly surplus. */
132
173
  function serviceabilityInfo(surplus: number): {
133
174
  label: string;
134
- badgeClass: string;
175
+ variant: "success" | "warning" | "destructive";
135
176
  } {
136
177
  if (surplus > 1500)
137
- return {
138
- label: "Likely Serviceable",
139
- badgeClass: "bg-emerald-50 text-emerald-700 border border-emerald-200",
140
- };
141
- if (surplus > 0)
142
- return {
143
- label: "Borderline",
144
- badgeClass: "bg-amber-50 text-amber-700 border border-amber-200",
145
- };
146
- return {
147
- label: "At Risk",
148
- badgeClass: "bg-red-50 text-destructive border border-red-200",
149
- };
178
+ return { label: "Likely Serviceable", variant: "success" };
179
+ if (surplus > 0) return { label: "Borderline", variant: "warning" };
180
+ return { label: "At Risk", variant: "destructive" };
150
181
  }
151
182
 
152
183
  /**
@@ -303,9 +334,7 @@ function HeroBand({
303
334
  {surplusDisplay}
304
335
  </span>
305
336
  </div>
306
- <div className={cn("px-2.5 py-1 text-xs font-medium", svc.badgeClass)}>
307
- {svc.label}
308
- </div>
337
+ <Badge variant={svc.variant}>{svc.label}</Badge>
309
338
  </div>
310
339
  </div>
311
340
  );
@@ -313,9 +342,6 @@ function HeroBand({
313
342
 
314
343
  // ---------------------------------------------------------------------------
315
344
  // ApplicantCardTab (internal)
316
- // Replaces ApplicantAccordionTab — uses bordered card pattern for consistency.
317
- // AboutCard / IncomeCard / ExpensesCard each carry their own border so we float
318
- // the section label + edit button ABOVE each card (no outer bordered wrapper).
319
345
  // ---------------------------------------------------------------------------
320
346
 
321
347
  interface ApplicantCardTabProps {
@@ -351,69 +377,226 @@ function ApplicantCardTab({
351
377
  return (
352
378
  <div className="flex flex-col gap-4 py-4">
353
379
  {/* ── About ── */}
354
- <div className="flex flex-col gap-2">
355
- <div className="flex items-center justify-between">
380
+ <div className="border border-border">
381
+ <div className="flex items-center justify-between px-4 py-2.5">
356
382
  <FinancialSectionLabel>About</FinancialSectionLabel>
357
383
  <SectionEditButton onClick={onEditAbout} title="Edit About" />
358
384
  </div>
359
- <AboutCard
360
- title={about.title}
361
- firstName={about.firstName}
362
- lastName={about.lastName}
363
- phone={about.phone}
364
- email={about.email}
365
- dob={about.dob}
366
- gender={about.gender}
367
- maritalStatus={about.maritalStatus}
368
- numDependants={about.numDependants}
369
- citizenStatus={about.citizenStatus}
370
- residentialAddress={about.residentialAddress}
371
- residentialStatus={about.residentialStatus}
372
- timeAtAddressYears={about.timeAtAddressYears}
373
- timeAtAddressMonths={about.timeAtAddressMonths}
374
- driversLicence={about.driversLicence}
375
- passport={about.passport}
376
- propertyInTrust={about.propertyInTrust}
377
- companyOwnership={about.companyOwnership}
378
- />
385
+ <div className="border-t border-border">
386
+ <AboutCard
387
+ borderless
388
+ title={about.title}
389
+ firstName={about.firstName}
390
+ lastName={about.lastName}
391
+ phone={about.phone}
392
+ email={about.email}
393
+ dob={about.dob}
394
+ gender={about.gender}
395
+ maritalStatus={about.maritalStatus}
396
+ numDependants={about.numDependants}
397
+ dependants={about.dependants}
398
+ citizenStatus={about.citizenStatus}
399
+ residentialAddress={about.residentialAddress}
400
+ residentialStatus={about.residentialStatus}
401
+ timeAtAddressYears={about.timeAtAddressYears}
402
+ timeAtAddressMonths={about.timeAtAddressMonths}
403
+ driversLicence={about.driversLicence}
404
+ passport={about.passport}
405
+ propertyInTrust={about.propertyInTrust}
406
+ companyOwnership={about.companyOwnership}
407
+ />
408
+ </div>
379
409
  </div>
380
410
 
381
411
  {/* ── Income ── */}
382
- <div className="flex flex-col gap-2">
383
- <div className="flex items-center justify-between">
412
+ <div className="border border-border">
413
+ <div className="flex items-center justify-between px-4 py-2.5">
384
414
  <FinancialSectionLabel>Income</FinancialSectionLabel>
385
415
  <SectionEditButton onClick={onEditIncome} title="Edit Income" />
386
416
  </div>
387
- <IncomeCard
388
- items={income.items.map((i) => ({
389
- incomeType: i.incomeType,
390
- jobTitle: i.jobTitle,
391
- companyName: i.companyName,
392
- companyAddress: i.companyAddress,
393
- startDate: i.startDate,
394
- stillInPosition: i.stillInPosition,
395
- endDate: i.endDate,
396
- companyType: i.companyType,
397
- amountLabel: `${formatCurrency(i.incomeAmount)} / ${i.frequency}`,
398
- }))}
399
- totalMonthly={totalMonthlyIncome}
400
- />
417
+ <div className="border-t border-border">
418
+ <IncomeCard
419
+ borderless
420
+ items={income.items.map((i) => ({
421
+ incomeType: i.incomeType,
422
+ jobTitle: i.jobTitle,
423
+ companyName: i.companyName,
424
+ companyAddress: i.companyAddress,
425
+ startDate: i.startDate,
426
+ stillInPosition: i.stillInPosition,
427
+ endDate: i.endDate,
428
+ companyType: i.companyType,
429
+ amountLabel: `${formatCurrency(i.incomeAmount)} / ${i.frequency}`,
430
+ }))}
431
+ totalMonthly={totalMonthlyIncome}
432
+ />
433
+ </div>
401
434
  </div>
402
435
 
403
436
  {/* ── Expenses ── */}
404
- <div className="flex flex-col gap-2">
405
- <div className="flex items-center justify-between">
437
+ <div className="border border-border">
438
+ <div className="flex items-center justify-between px-4 py-2.5">
406
439
  <FinancialSectionLabel>Expenses</FinancialSectionLabel>
407
440
  <SectionEditButton onClick={onEditExpenses} title="Edit Expenses" />
408
441
  </div>
409
- <ExpensesCard
410
- items={expenses.items.map((e) => ({
411
- expenseType: e.expenseType,
412
- amountLabel: `${formatCurrency(e.amount)} / ${e.frequency}`,
413
- }))}
414
- totalMonthly={totalMonthlyExpenses}
415
- />
442
+ <div className="border-t border-border">
443
+ <ExpensesCard
444
+ borderless
445
+ items={expenses.items.map((e) => ({
446
+ expenseType: e.expenseType,
447
+ amountLabel: `${formatCurrency(e.amount)} / ${e.frequency}`,
448
+ }))}
449
+ totalMonthly={totalMonthlyExpenses}
450
+ />
451
+ </div>
452
+ </div>
453
+ </div>
454
+ );
455
+ }
456
+
457
+ // ---------------------------------------------------------------------------
458
+ // DocRow — single document row (flat list or grouped)
459
+ // ---------------------------------------------------------------------------
460
+
461
+ const STATUS_OPTIONS = ["verified", "pending", "rejected"] as const;
462
+
463
+ const STATUS_CONFIG: Record<
464
+ DocumentItem["status"],
465
+ {
466
+ Icon: React.ComponentType<{ className?: string }>;
467
+ label: string;
468
+ iconCls: string;
469
+ triggerCls: string;
470
+ }
471
+ > = {
472
+ verified: {
473
+ Icon: CheckCircle2,
474
+ label: "Verified",
475
+ iconCls: "text-success",
476
+ triggerCls: "border-success text-success",
477
+ },
478
+ pending: {
479
+ Icon: AlertCircle,
480
+ label: "Pending",
481
+ iconCls: "text-warning",
482
+ triggerCls: "border-warning text-warning",
483
+ },
484
+ rejected: {
485
+ Icon: AlertCircle,
486
+ label: "Rejected",
487
+ iconCls: "text-destructive",
488
+ triggerCls: "border-destructive text-destructive",
489
+ },
490
+ };
491
+
492
+ interface DocRowProps {
493
+ doc: DocumentItem;
494
+ status: DocumentItem["status"];
495
+ isSelected: boolean;
496
+ uploaderName: string;
497
+ onRowClick: () => void;
498
+ onStatusChange: (status: DocumentItem["status"]) => void;
499
+ }
500
+
501
+ function DocRow({
502
+ doc,
503
+ status,
504
+ isSelected,
505
+ uploaderName,
506
+ onRowClick,
507
+ onStatusChange,
508
+ }: DocRowProps) {
509
+ const {
510
+ Icon: StatusIcon,
511
+ label: statusLabel,
512
+ iconCls,
513
+ triggerCls,
514
+ } = STATUS_CONFIG[status];
515
+
516
+ return (
517
+ <div
518
+ onClick={onRowClick}
519
+ className={cn(
520
+ "flex min-h-[44px] cursor-pointer items-center gap-3 px-4 transition-colors",
521
+ isSelected ? "bg-primary/5" : "hover:bg-muted/40",
522
+ )}
523
+ >
524
+ <span className="shrink-0" onClick={(e) => e.stopPropagation()}>
525
+ <Checkbox checked={isSelected} onCheckedChange={() => onRowClick()} />
526
+ </span>
527
+ <FileText className="size-4 shrink-0 text-muted-foreground" />
528
+
529
+ {/* Name + subtitle */}
530
+ <div className="flex min-w-0 flex-1 flex-col">
531
+ {doc.url ? (
532
+ <a
533
+ href={doc.url}
534
+ target="_blank"
535
+ rel="noreferrer"
536
+ onClick={(e) => e.stopPropagation()}
537
+ className="w-fit text-label-medium text-foreground underline-offset-2 hover:underline"
538
+ >
539
+ {doc.name}
540
+ </a>
541
+ ) : (
542
+ <span className="text-label-medium text-foreground">{doc.name}</span>
543
+ )}
544
+ <span className="text-xs text-muted-foreground">
545
+ {doc.documentType}
546
+ {doc.checklistItem ? ` · ${doc.checklistItem}` : ""}
547
+ </span>
416
548
  </div>
549
+
550
+ {/* Uploader */}
551
+ <Badge variant="secondary" className="shrink-0">
552
+ {uploaderName}
553
+ </Badge>
554
+
555
+ {/* Status — select */}
556
+ <span className="shrink-0" onClick={(e) => e.stopPropagation()}>
557
+ <Select
558
+ value={status}
559
+ onValueChange={(v) => onStatusChange(v as DocumentItem["status"])}
560
+ >
561
+ <SelectTrigger
562
+ size="sm"
563
+ className={cn("h-7 w-[116px] gap-1.5 text-xs", triggerCls)}
564
+ >
565
+ <StatusIcon className={cn("size-3.5", iconCls)} />
566
+ {statusLabel}
567
+ </SelectTrigger>
568
+ <SelectContent>
569
+ {STATUS_OPTIONS.map((v) => {
570
+ const { Icon, label, iconCls } = STATUS_CONFIG[v];
571
+ return (
572
+ <SelectItem key={v} value={v}>
573
+ <span className="flex items-center gap-1.5">
574
+ <Icon className={cn("size-3.5", iconCls)} />
575
+ {label}
576
+ </span>
577
+ </SelectItem>
578
+ );
579
+ })}
580
+ </SelectContent>
581
+ </Select>
582
+ </span>
583
+
584
+ {/* Upload date */}
585
+ <span className="shrink-0 text-xs text-muted-foreground">
586
+ {doc.uploadedAt}
587
+ </span>
588
+
589
+ {/* Per-row download */}
590
+ {doc.url && (
591
+ <a
592
+ href={doc.url}
593
+ download
594
+ onClick={(e) => e.stopPropagation()}
595
+ className="shrink-0 text-muted-foreground transition-colors hover:text-foreground"
596
+ >
597
+ <Download className="size-4" />
598
+ </a>
599
+ )}
417
600
  </div>
418
601
  );
419
602
  }
@@ -442,6 +625,9 @@ export function OpportunitySummaryTab({
442
625
  onCoIncomeChange,
443
626
  coExpenses,
444
627
  onCoExpensesChange,
628
+ documents = [],
629
+ onDocumentsDownload,
630
+ onDocumentStatusChange,
445
631
  }: OpportunitySummaryTabProps) {
446
632
  // ── Portal container — scopes edit modal overlays inside the drawer ────────
447
633
  const [portalEl, setPortalEl] = useState<HTMLDivElement | null>(null);
@@ -451,8 +637,69 @@ export function OpportunitySummaryTab({
451
637
  "joint",
452
638
  );
453
639
 
454
- // ── Loan Scenario collapse ─────────────────────────────────────────────────
455
- const [loanScenarioExpanded, setLoanScenarioExpanded] = useState(true);
640
+ // ── Document selection for download ──────────────────────────────────────
641
+ const [selectedDocIds, setSelectedDocIds] = useState<Set<string>>(new Set());
642
+
643
+ function toggleDocSelection(id: string) {
644
+ setSelectedDocIds((prev) => {
645
+ const next = new Set(prev);
646
+ if (next.has(id)) next.delete(id);
647
+ else next.add(id);
648
+ return next;
649
+ });
650
+ }
651
+
652
+ // ── Document status overrides (local optimistic state) ────────────────────
653
+ const [docStatusOverrides, setDocStatusOverrides] = useState<
654
+ Record<string, DocumentItem["status"]>
655
+ >({});
656
+
657
+ function handleDocStatusChange(
658
+ docId: string,
659
+ status: DocumentItem["status"],
660
+ ) {
661
+ setDocStatusOverrides((prev) => ({ ...prev, [docId]: status }));
662
+ onDocumentStatusChange?.(docId, status);
663
+ }
664
+
665
+ function effectiveStatus(doc: DocumentItem): DocumentItem["status"] {
666
+ return docStatusOverrides[doc.id] ?? doc.status;
667
+ }
668
+
669
+ // ── Checklist progress — derived from docs grouped by checklistItem ────────
670
+ const checklistProgress = React.useMemo(() => {
671
+ const map = new Map<
672
+ string,
673
+ { count: number; hasVerified: boolean; hasPending: boolean }
674
+ >();
675
+ for (const doc of documents) {
676
+ if (!doc.checklistItem) continue;
677
+ const st = docStatusOverrides[doc.id] ?? doc.status;
678
+ const entry = map.get(doc.checklistItem) ?? {
679
+ count: 0,
680
+ hasVerified: false,
681
+ hasPending: false,
682
+ };
683
+ entry.count += 1;
684
+ if (st === "verified") entry.hasVerified = true;
685
+ if (st === "pending") entry.hasPending = true;
686
+ map.set(doc.checklistItem, entry);
687
+ }
688
+ return Array.from(map.entries()).map(([name, data]) => ({ name, ...data }));
689
+ }, [documents, docStatusOverrides]);
690
+
691
+ // ── Group docs by checklistItem; null when flat list is preferred ────────
692
+ const groupedDocs = React.useMemo(() => {
693
+ if (documents.length <= 5 || !documents.some((d) => d.checklistItem))
694
+ return null;
695
+ const groups: Record<string, DocumentItem[]> = {};
696
+ for (const doc of documents) {
697
+ const key = doc.checklistItem ?? "Other";
698
+ if (!groups[key]) groups[key] = [];
699
+ groups[key].push(doc);
700
+ }
701
+ return groups;
702
+ }, [documents]);
456
703
 
457
704
  // ── Modal open state (internal) ───────────────────────────────────────────
458
705
  const [editLoanOpen, setEditLoanOpen] = useState(false);
@@ -520,76 +767,58 @@ export function OpportunitySummaryTab({
520
767
  netSurplus={netSurplus}
521
768
  />
522
769
 
523
- {/* ── Loan Scenario — collapsible bordered card ── */}
770
+ {/* ── Loan Scenario ── */}
524
771
  <div className="mb-4 border border-border">
525
772
  <div className="flex items-center justify-between px-4 py-2.5">
526
- {/* Left: toggle area (label + chevron) */}
527
- <Button
528
- type="button"
529
- variant="ghost"
530
- className="h-auto flex-1 justify-start gap-2 px-0 text-left"
531
- onClick={() => setLoanScenarioExpanded((v) => !v)}
532
- aria-expanded={loanScenarioExpanded}
533
- >
534
- <FinancialSectionLabel>Loan Scenario</FinancialSectionLabel>
535
- {loanScenarioExpanded ? (
536
- <ChevronUp className="size-3.5 text-muted-foreground" />
537
- ) : (
538
- <ChevronDown className="size-3.5 text-muted-foreground" />
539
- )}
540
- </Button>
541
- {/* Right: edit button */}
773
+ <FinancialSectionLabel>Loan Scenario</FinancialSectionLabel>
542
774
  <SectionEditButton
543
775
  onClick={() => setEditLoanOpen(true)}
544
776
  title="Edit Loan Scenario"
545
777
  />
546
778
  </div>
547
-
548
- {loanScenarioExpanded && (
549
- <div className="grid grid-cols-4 gap-x-6 gap-y-4 border-t border-border p-4">
550
- <FinancialDetailField label="Lending Type" value="Home Loan" />
551
- <FinancialDetailField
552
- label="Purpose of Loan"
553
- value={loanScenario.loanPurpose}
554
- />
555
- <FinancialDetailField
556
- label="Loan Amount"
557
- value={formatCurrency(loanScenario.loanAmount)}
558
- />
559
- <FinancialDetailField
560
- label="Property Estimate"
561
- value={formatCurrency(loanScenario.propertyEstimate)}
562
- />
563
- <FinancialDetailField
564
- label="Cash / Deposit"
565
- value={formatCurrency(loanScenario.cashEquity)}
566
- />
567
- <FinancialDetailField label="Property Address" value="—" />
568
- <FinancialDetailField
569
- label="Duration"
570
- value={`${loanScenario.loanDuration} years`}
571
- />
572
- <FinancialDetailField
573
- label="Important Features"
574
- value={
575
- [
576
- loanScenario.featureVariableRate && "Variable Rate",
577
- loanScenario.featureFixedRate && "Fixed Rate",
578
- loanScenario.featureRedrawFacility && "Redraw Facility",
579
- loanScenario.feature100Offset && "100% Offset",
580
- loanScenario.featureSplitLoan && "Split Loan",
581
- loanScenario.featureInterestOnly && "Interest Only",
582
- ]
583
- .filter(Boolean)
584
- .join(", ") || "—"
585
- }
586
- />
587
- <FinancialDetailField
588
- label="Top Priorities"
589
- value={loanScenario.priorities.join(", ") || "—"}
590
- />
591
- </div>
592
- )}
779
+ <div className="grid grid-cols-4 gap-x-6 gap-y-4 border-t border-border p-4">
780
+ <FinancialDetailField label="Lending Type" value="Home Loan" />
781
+ <FinancialDetailField
782
+ label="Purpose of Loan"
783
+ value={loanScenario.loanPurpose}
784
+ />
785
+ <FinancialDetailField
786
+ label="Loan Amount"
787
+ value={formatCurrency(loanScenario.loanAmount)}
788
+ />
789
+ <FinancialDetailField
790
+ label="Property Estimate"
791
+ value={formatCurrency(loanScenario.propertyEstimate)}
792
+ />
793
+ <FinancialDetailField
794
+ label="Cash / Deposit"
795
+ value={formatCurrency(loanScenario.cashEquity)}
796
+ />
797
+ <FinancialDetailField label="Property Address" value="—" />
798
+ <FinancialDetailField
799
+ label="Duration"
800
+ value={`${loanScenario.loanDuration} years`}
801
+ />
802
+ <FinancialDetailField
803
+ label="Important Features"
804
+ value={
805
+ [
806
+ loanScenario.featureVariableRate && "Variable Rate",
807
+ loanScenario.featureFixedRate && "Fixed Rate",
808
+ loanScenario.featureRedrawFacility && "Redraw Facility",
809
+ loanScenario.feature100Offset && "100% Offset",
810
+ loanScenario.featureSplitLoan && "Split Loan",
811
+ loanScenario.featureInterestOnly && "Interest Only",
812
+ ]
813
+ .filter(Boolean)
814
+ .join(", ") || "—"
815
+ }
816
+ />
817
+ <FinancialDetailField
818
+ label="Top Priorities"
819
+ value={loanScenario.priorities.join(", ") || "—"}
820
+ />
821
+ </div>
593
822
  </div>
594
823
 
595
824
  {/* ── Applicant sub-tabs — filled variant, full width ── */}
@@ -671,16 +900,114 @@ export function OpportunitySummaryTab({
671
900
  </div>
672
901
  </div>
673
902
 
674
- {/* Documents — placed early so brokers can immediately check
675
- what's been uploaded vs what's still missing */}
903
+ {/* Documents */}
676
904
  <div className="border border-border">
677
- <div className="px-4 py-2.5">
905
+ {/* Section header */}
906
+ <div className="flex min-h-[40px] items-center justify-between px-4">
678
907
  <FinancialSectionLabel>Documents</FinancialSectionLabel>
908
+ <div className="flex items-center gap-3">
909
+ {selectedDocIds.size > 0 && (
910
+ <Button
911
+ size="sm"
912
+ variant="outline-secondary"
913
+ className="h-7 gap-1.5 text-xs"
914
+ onClick={() => {
915
+ const selected = documents.filter((d) =>
916
+ selectedDocIds.has(d.id),
917
+ );
918
+ onDocumentsDownload?.(selected);
919
+ }}
920
+ >
921
+ <Download className="size-3.5" />
922
+ Download ({selectedDocIds.size})
923
+ </Button>
924
+ )}
925
+ {documents.length > 0 && (
926
+ <span className="text-xs text-muted-foreground">
927
+ {documents.length} file
928
+ {documents.length !== 1 ? "s" : ""}
929
+ </span>
930
+ )}
931
+ </div>
679
932
  </div>
680
- <div className="p-4">
681
- <p className="text-body-small text-muted-foreground">
682
- No documents uploaded yet.
683
- </p>
933
+
934
+ {/* Checklist progress — derived from docs grouped by checklistItem */}
935
+ {checklistProgress.length > 0 && (
936
+ <div className="flex flex-wrap gap-x-4 gap-y-1.5 border-t border-border px-4 py-3">
937
+ {checklistProgress.map((cat) => (
938
+ <span
939
+ key={cat.name}
940
+ className={cn(
941
+ "flex items-center gap-1 text-xs",
942
+ cat.hasVerified ? "text-success" : "text-warning",
943
+ )}
944
+ >
945
+ {cat.hasVerified ? (
946
+ <CheckCircle2 className="size-3.5 shrink-0" />
947
+ ) : (
948
+ <AlertCircle className="size-3.5 shrink-0" />
949
+ )}
950
+ {cat.name}
951
+ <span className="text-muted-foreground">({cat.count})</span>
952
+ </span>
953
+ ))}
954
+ </div>
955
+ )}
956
+
957
+ {/* Document list */}
958
+ <div className="border-t border-border">
959
+ {documents.length === 0 ? (
960
+ <p className="px-4 py-4 text-body-small text-muted-foreground">
961
+ No documents uploaded yet.
962
+ </p>
963
+ ) : groupedDocs ? (
964
+ // Grouped by checklistItem
965
+ Object.entries(groupedDocs).map(([category, docs]) => (
966
+ <div key={category}>
967
+ <div className="bg-muted/30 px-4 py-1.5 text-xs font-medium text-muted-foreground">
968
+ {category}
969
+ </div>
970
+ <div className="flex flex-col divide-y divide-border">
971
+ {docs.map((doc) => (
972
+ <DocRow
973
+ key={doc.id}
974
+ doc={doc}
975
+ status={effectiveStatus(doc)}
976
+ isSelected={selectedDocIds.has(doc.id)}
977
+ uploaderName={
978
+ doc.uploadedBy === "main"
979
+ ? mainAbout.firstName || "Main"
980
+ : coAbout?.firstName || "Co"
981
+ }
982
+ onRowClick={() => toggleDocSelection(doc.id)}
983
+ onStatusChange={(s) =>
984
+ handleDocStatusChange(doc.id, s)
985
+ }
986
+ />
987
+ ))}
988
+ </div>
989
+ </div>
990
+ ))
991
+ ) : (
992
+ // Flat list
993
+ <div className="flex flex-col divide-y divide-border">
994
+ {documents.map((doc) => (
995
+ <DocRow
996
+ key={doc.id}
997
+ doc={doc}
998
+ status={effectiveStatus(doc)}
999
+ isSelected={selectedDocIds.has(doc.id)}
1000
+ uploaderName={
1001
+ doc.uploadedBy === "main"
1002
+ ? mainAbout.firstName || "Main"
1003
+ : coAbout?.firstName || "Co"
1004
+ }
1005
+ onRowClick={() => toggleDocSelection(doc.id)}
1006
+ onStatusChange={(s) => handleDocStatusChange(doc.id, s)}
1007
+ />
1008
+ ))}
1009
+ </div>
1010
+ )}
684
1011
  </div>
685
1012
  </div>
686
1013
 
@@ -725,7 +1052,7 @@ export function OpportunitySummaryTab({
725
1052
  title="Edit Debts"
726
1053
  />
727
1054
  </div>
728
- <div className="grid grid-cols-2 gap-3 p-4">
1055
+ <div className="grid grid-cols-2 gap-3 border-t border-border p-4">
729
1056
  {debts.map((debt) => (
730
1057
  <DebtCard key={debt.id} {...debtToCard(debt)} />
731
1058
  ))}