@wealthx/shadcn 1.4.1 → 1.5.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 (430) hide show
  1. package/.turbo/turbo-build.log +356 -176
  2. package/CHANGELOG.md +12 -0
  3. package/dist/chunk-2A53WPEC.mjs +182 -0
  4. package/dist/{chunk-SIVYAI3M.mjs → chunk-2LLFNGJZ.mjs} +15 -15
  5. package/dist/chunk-2QNOPXMQ.mjs +360 -0
  6. package/dist/{chunk-K5QV4TT6.mjs → chunk-2WCIORP7.mjs} +29 -4
  7. package/dist/{chunk-5NF6T2RS.mjs → chunk-3AREQTZU.mjs} +8 -8
  8. package/dist/{chunk-2EM2FRU6.mjs → chunk-3WGFIFP6.mjs} +5 -5
  9. package/dist/{chunk-K4KOD3KR.mjs → chunk-42NEC57Y.mjs} +44 -25
  10. package/dist/{chunk-FL6DZFJK.mjs → chunk-46Q4335I.mjs} +121 -39
  11. package/dist/chunk-4DO3WM7V.mjs +48 -0
  12. package/dist/chunk-5FHBC6DY.mjs +68 -0
  13. package/dist/chunk-5SAYZ4CI.mjs +40 -0
  14. package/dist/chunk-5WMFKQZ6.mjs +180 -0
  15. package/dist/chunk-623YVI5O.mjs +43 -0
  16. package/dist/{chunk-SFH2NJEJ.mjs → chunk-6OSULDEO.mjs} +3 -3
  17. package/dist/{chunk-UALR6JGV.mjs → chunk-6SR4K5T5.mjs} +1 -1
  18. package/dist/{chunk-D2NSIIXG.mjs → chunk-7KT5HPYM.mjs} +11 -11
  19. package/dist/chunk-A6ER36CW.mjs +456 -0
  20. package/dist/{chunk-QX7IFQSF.mjs → chunk-AHKHVBWR.mjs} +4 -4
  21. package/dist/chunk-AHSCWXYJ.mjs +113 -0
  22. package/dist/{chunk-7GWRPXHD.mjs → chunk-AL6GOL2Y.mjs} +1 -1
  23. package/dist/{chunk-OIKBW2QD.mjs → chunk-AUEUTZIC.mjs} +13 -13
  24. package/dist/{chunk-FYPSTTEJ.mjs → chunk-B7DD3ODQ.mjs} +1 -1
  25. package/dist/{chunk-TLAWKTSA.mjs → chunk-BD3DWDT4.mjs} +3 -3
  26. package/dist/{chunk-S2FKV4M5.mjs → chunk-BDESHD25.mjs} +4 -4
  27. package/dist/{chunk-OKTJFDPN.mjs → chunk-BFB3UH7V.mjs} +2 -2
  28. package/dist/{chunk-DGNHGNYH.mjs → chunk-C6SWS7OW.mjs} +1 -1
  29. package/dist/chunk-CDVG7SFT.mjs +271 -0
  30. package/dist/chunk-CUSHAIUL.mjs +109 -0
  31. package/dist/{chunk-QXKGOMUX.mjs → chunk-CW32WTZU.mjs} +4 -4
  32. package/dist/{chunk-SET2ANTY.mjs → chunk-D447W45Z.mjs} +3 -3
  33. package/dist/{chunk-RCAOCHWA.mjs → chunk-DFL5CV75.mjs} +18 -18
  34. package/dist/chunk-DYSVJ473.mjs +162 -0
  35. package/dist/chunk-E3PQDBYI.mjs +288 -0
  36. package/dist/chunk-EMYBNPIA.mjs +83 -0
  37. package/dist/chunk-EUYPMDQG.mjs +348 -0
  38. package/dist/{chunk-XYPW2XA5.mjs → chunk-EW72FINW.mjs} +11 -11
  39. package/dist/chunk-F24U4QQQ.mjs +234 -0
  40. package/dist/{chunk-VB5M6OZQ.mjs → chunk-F4O2YPXJ.mjs} +1 -1
  41. package/dist/chunk-FFXTQTB4.mjs +84 -0
  42. package/dist/{chunk-ZOWL2L5J.mjs → chunk-FYUSF5KO.mjs} +5 -1
  43. package/dist/{chunk-FTPBQVQ6.mjs → chunk-GNER6MCO.mjs} +1 -1
  44. package/dist/{chunk-2D3HQPFN.mjs → chunk-HF4FUBCY.mjs} +5 -5
  45. package/dist/{chunk-RSUIPKGX.mjs → chunk-HNDTLT5X.mjs} +1 -1
  46. package/dist/{chunk-N6Q5IPKT.mjs → chunk-HO6S3ECM.mjs} +46 -18
  47. package/dist/{chunk-L4NSRQ3T.mjs → chunk-HROG643K.mjs} +1 -1
  48. package/dist/chunk-I2EKKSEF.mjs +148 -0
  49. package/dist/{chunk-QTRSCVQ3.mjs → chunk-I3UDLWQ7.mjs} +1 -1
  50. package/dist/{chunk-AE7MASLF.mjs → chunk-IQGKOT7A.mjs} +9 -6
  51. package/dist/chunk-IXR4BQSQ.mjs +290 -0
  52. package/dist/{chunk-4MM7LHM5.mjs → chunk-J5NW5NCT.mjs} +1 -1
  53. package/dist/{chunk-OLKMCXAR.mjs → chunk-JTG5R5YV.mjs} +24 -24
  54. package/dist/chunk-JUBUN65Q.mjs +106 -0
  55. package/dist/chunk-K7TWMLLW.mjs +520 -0
  56. package/dist/{chunk-BOW7U26Y.mjs → chunk-K7WSRWOU.mjs} +4 -4
  57. package/dist/{chunk-NTYQWVLI.mjs → chunk-KAD26MCC.mjs} +1 -1
  58. package/dist/{chunk-KCWNDYPZ.mjs → chunk-KB7MZMED.mjs} +4 -4
  59. package/dist/chunk-KCKYGQVQ.mjs +61 -0
  60. package/dist/{chunk-VY5NEUP7.mjs → chunk-KLJLDNCA.mjs} +1 -1
  61. package/dist/chunk-LLAGF6BA.mjs +49 -0
  62. package/dist/{chunk-G27TSQLQ.mjs → chunk-M4LTX3MH.mjs} +1 -1
  63. package/dist/{chunk-YIZHS72Z.mjs → chunk-MHHA7QGO.mjs} +94 -54
  64. package/dist/{chunk-P2N2PEFY.mjs → chunk-NCUH54IZ.mjs} +4 -4
  65. package/dist/{chunk-PNRUH7JY.mjs → chunk-OECGKCVF.mjs} +5 -5
  66. package/dist/{chunk-YE67AALL.mjs → chunk-OL65UQHQ.mjs} +10 -10
  67. package/dist/{chunk-LQULK2E3.mjs → chunk-OYBIUEGE.mjs} +1 -1
  68. package/dist/{chunk-LR6LHDP3.mjs → chunk-PGR53HMH.mjs} +7 -7
  69. package/dist/chunk-PUJ42INK.mjs +141 -0
  70. package/dist/{chunk-M4VYX2PV.mjs → chunk-PV3Y7QGK.mjs} +2 -2
  71. package/dist/{chunk-UJZ4UHWI.mjs → chunk-PV7PNA6K.mjs} +4 -4
  72. package/dist/{chunk-6HIOM2HL.mjs → chunk-Q35PNFJ7.mjs} +1 -1
  73. package/dist/{chunk-JZY6TNIS.mjs → chunk-Q5SGEIJV.mjs} +27 -27
  74. package/dist/{chunk-ZFCDYW6N.mjs → chunk-QAX6HCUH.mjs} +1 -1
  75. package/dist/chunk-QHJDGB54.mjs +135 -0
  76. package/dist/chunk-QQSOZQOC.mjs +27 -0
  77. package/dist/chunk-RUX3OLVZ.mjs +59 -0
  78. package/dist/{chunk-QOJ2DQD6.mjs → chunk-S4CTM3UE.mjs} +5 -0
  79. package/dist/{chunk-ZEDMKQK2.mjs → chunk-TAX3KL66.mjs} +1 -1
  80. package/dist/chunk-TC43SMIN.mjs +133 -0
  81. package/dist/chunk-TGVXRD53.mjs +174 -0
  82. package/dist/{chunk-K5VHK7CM.mjs → chunk-TLYSVRSK.mjs} +12 -12
  83. package/dist/{chunk-YCWLFG27.mjs → chunk-TOIVHWNC.mjs} +1 -1
  84. package/dist/chunk-TXUBGKB7.mjs +160 -0
  85. package/dist/chunk-UEREFDAE.mjs +75 -0
  86. package/dist/chunk-UTCQN6XU.mjs +123 -0
  87. package/dist/{chunk-37AE3OM5.mjs → chunk-V4CUTCHS.mjs} +4 -4
  88. package/dist/{chunk-THVO2N47.mjs → chunk-VFH632TB.mjs} +9 -9
  89. package/dist/{chunk-3ERBUVHC.mjs → chunk-VJ3GC7W3.mjs} +95 -49
  90. package/dist/{chunk-V6XGXYCJ.mjs → chunk-VLELWBEW.mjs} +4 -4
  91. package/dist/{chunk-FEZKMUCF.mjs → chunk-WDTXHLYM.mjs} +1 -1
  92. package/dist/chunk-WUA546RX.mjs +129 -0
  93. package/dist/chunk-XHGISOX5.mjs +257 -0
  94. package/dist/chunk-XIY5DJXI.mjs +168 -0
  95. package/dist/{chunk-TOWTPLRC.mjs → chunk-XN37434W.mjs} +8 -8
  96. package/dist/{chunk-KLTACJ2G.mjs → chunk-XTWAJWCQ.mjs} +1 -1
  97. package/dist/chunk-Y24TXIFJ.mjs +518 -0
  98. package/dist/{chunk-DMXYRCHM.mjs → chunk-Y6UM3VTN.mjs} +4 -4
  99. package/dist/components/ui/about-you-form.js +1120 -0
  100. package/dist/components/ui/about-you-form.mjs +323 -0
  101. package/dist/components/ui/account-list-carousel.js +304 -0
  102. package/dist/components/ui/account-list-carousel.mjs +11 -0
  103. package/dist/components/ui/add-column-modal.js +1 -1
  104. package/dist/components/ui/add-column-modal.mjs +6 -6
  105. package/dist/components/ui/add-lead-modal.js +2 -2
  106. package/dist/components/ui/add-lead-modal.mjs +6 -6
  107. package/dist/components/ui/advisor-card.mjs +2 -2
  108. package/dist/components/ui/agent-evaluation-toast.js +299 -0
  109. package/dist/components/ui/agent-evaluation-toast.mjs +12 -0
  110. package/dist/components/ui/ai-assistant-drawer.mjs +5 -5
  111. package/dist/components/ui/ai-builder.js +3 -3
  112. package/dist/components/ui/ai-builder.mjs +5 -5
  113. package/dist/components/ui/ai-conversations.js +2 -2
  114. package/dist/components/ui/ai-conversations.mjs +11 -11
  115. package/dist/components/ui/alert-dialog.mjs +3 -3
  116. package/dist/components/ui/applicant-document-checklist.js +346 -0
  117. package/dist/components/ui/applicant-document-checklist.mjs +10 -0
  118. package/dist/components/ui/applicant-expenses-section.js +455 -0
  119. package/dist/components/ui/applicant-expenses-section.mjs +220 -0
  120. package/dist/components/ui/applicant-navigation-bar.js +309 -0
  121. package/dist/components/ui/applicant-navigation-bar.mjs +87 -0
  122. package/dist/components/ui/applicant-switcher.js +268 -0
  123. package/dist/components/ui/applicant-switcher.mjs +46 -0
  124. package/dist/components/ui/application-mobile-layout.js +88 -0
  125. package/dist/components/ui/application-mobile-layout.mjs +8 -0
  126. package/dist/components/ui/appointment-action-dialogs.js +1 -1
  127. package/dist/components/ui/appointment-action-dialogs.mjs +10 -10
  128. package/dist/components/ui/appointment-availability-settings.js +78 -31
  129. package/dist/components/ui/appointment-availability-settings.mjs +12 -10
  130. package/dist/components/ui/appointment-book-dialog.js +137 -58
  131. package/dist/components/ui/appointment-book-dialog.mjs +14 -14
  132. package/dist/components/ui/appointment-calendar-view.js +1 -1
  133. package/dist/components/ui/appointment-calendar-view.mjs +4 -4
  134. package/dist/components/ui/appointment-detail-sheet.js +38 -11
  135. package/dist/components/ui/appointment-detail-sheet.mjs +13 -13
  136. package/dist/components/ui/appointment-gmail-connect.mjs +2 -2
  137. package/dist/components/ui/appointment-time-slot-picker.mjs +2 -2
  138. package/dist/components/ui/appointment-upcoming-card.js +1 -1
  139. package/dist/components/ui/appointment-upcoming-card.mjs +10 -10
  140. package/dist/components/ui/asset-accordion.js +506 -0
  141. package/dist/components/ui/asset-accordion.mjs +202 -0
  142. package/dist/components/ui/assets-liabilities-side-card.js +328 -0
  143. package/dist/components/ui/assets-liabilities-side-card.mjs +127 -0
  144. package/dist/components/ui/auth-page-layout.js +3 -3
  145. package/dist/components/ui/auth-page-layout.mjs +1 -1
  146. package/dist/components/ui/backoffice-alert-history-chart.mjs +4 -4
  147. package/dist/components/ui/backoffice-alert-matching-chart.js +786 -0
  148. package/dist/components/ui/backoffice-alert-matching-chart.mjs +19 -0
  149. package/dist/components/ui/backoffice-alerts-chart.mjs +4 -4
  150. package/dist/components/ui/backoffice-connections-chart.mjs +4 -4
  151. package/dist/components/ui/backoffice-contact-history-chart.mjs +4 -4
  152. package/dist/components/ui/backoffice-contact-matching-chart.js +803 -0
  153. package/dist/components/ui/backoffice-contact-matching-chart.mjs +19 -0
  154. package/dist/components/ui/backoffice-signup-steps.js +1673 -0
  155. package/dist/components/ui/backoffice-signup-steps.mjs +36 -0
  156. package/dist/components/ui/bank-statement-document-table.js +467 -0
  157. package/dist/components/ui/bank-statement-document-table.mjs +12 -0
  158. package/dist/components/ui/bank-statement-generate-dialog.js +1517 -0
  159. package/dist/components/ui/bank-statement-generate-dialog.mjs +27 -0
  160. package/dist/components/ui/bank-statement-pdf-viewer.js +525 -0
  161. package/dist/components/ui/bank-statement-pdf-viewer.mjs +14 -0
  162. package/dist/components/ui/banking-accounts-connect.js +336 -0
  163. package/dist/components/ui/banking-accounts-connect.mjs +114 -0
  164. package/dist/components/ui/borrowing-capacity-atoms.js +382 -0
  165. package/dist/components/ui/borrowing-capacity-atoms.mjs +17 -0
  166. package/dist/components/ui/borrowing-capacity-card.js +835 -0
  167. package/dist/components/ui/borrowing-capacity-card.mjs +89 -0
  168. package/dist/components/ui/borrowing-capacity-line-chart.mjs +4 -4
  169. package/dist/components/ui/broker-info-panel.js +281 -0
  170. package/dist/components/ui/broker-info-panel.mjs +59 -0
  171. package/dist/components/ui/calculator-input-item.js +101 -0
  172. package/dist/components/ui/calculator-input-item.mjs +8 -0
  173. package/dist/components/ui/calculator-section.js +743 -0
  174. package/dist/components/ui/calculator-section.mjs +220 -0
  175. package/dist/components/ui/calendar.mjs +2 -2
  176. package/dist/components/ui/cash-balance-line-chart.mjs +5 -5
  177. package/dist/components/ui/cashflow-bar-chart.mjs +4 -4
  178. package/dist/components/ui/category-edit-dialog.js +737 -0
  179. package/dist/components/ui/category-edit-dialog.mjs +16 -0
  180. package/dist/components/ui/chat-widget.mjs +3 -3
  181. package/dist/components/ui/color-picker.mjs +4 -4
  182. package/dist/components/ui/connect-bank-step.js +511 -0
  183. package/dist/components/ui/connect-bank-step.mjs +287 -0
  184. package/dist/components/ui/contact-alert-dialog.js +1405 -0
  185. package/dist/components/ui/contact-alert-dialog.mjs +27 -0
  186. package/dist/components/ui/create-contact-modal.js +1028 -0
  187. package/dist/components/ui/create-contact-modal.mjs +21 -0
  188. package/dist/components/ui/csv-import-modal.js +583 -0
  189. package/dist/components/ui/csv-import-modal.mjs +14 -0
  190. package/dist/components/ui/currency-input.js +439 -0
  191. package/dist/components/ui/currency-input.mjs +13 -0
  192. package/dist/components/ui/dashboard-expense-categories.js +355 -0
  193. package/dist/components/ui/dashboard-expense-categories.mjs +186 -0
  194. package/dist/components/ui/dashboard-transactions-table.js +1083 -0
  195. package/dist/components/ui/dashboard-transactions-table.mjs +177 -0
  196. package/dist/components/ui/data-table.mjs +6 -6
  197. package/dist/components/ui/date-picker.mjs +6 -6
  198. package/dist/components/ui/debt-accordion.js +523 -0
  199. package/dist/components/ui/debt-accordion.mjs +219 -0
  200. package/dist/components/ui/delete-contact-component.js +479 -0
  201. package/dist/components/ui/delete-contact-component.mjs +14 -0
  202. package/dist/components/ui/dialog.js +1 -1
  203. package/dist/components/ui/dialog.mjs +3 -3
  204. package/dist/components/ui/document-checklist-template.mjs +4 -4
  205. package/dist/components/ui/drawer.mjs +3 -3
  206. package/dist/components/ui/dropdown-menu.mjs +3 -3
  207. package/dist/components/ui/dynamic-tabs.js +274 -0
  208. package/dist/components/ui/dynamic-tabs.mjs +116 -0
  209. package/dist/components/ui/editable-money-item.js +306 -0
  210. package/dist/components/ui/editable-money-item.mjs +12 -0
  211. package/dist/components/ui/expense-bar-chart.mjs +4 -4
  212. package/dist/components/ui/expense-detail-item.js +506 -0
  213. package/dist/components/ui/expense-detail-item.mjs +15 -0
  214. package/dist/components/ui/expense-work-details.js +1259 -0
  215. package/dist/components/ui/expense-work-details.mjs +175 -0
  216. package/dist/components/ui/field.mjs +2 -2
  217. package/dist/components/ui/file-preview-dialog.js +704 -0
  218. package/dist/components/ui/file-preview-dialog.mjs +17 -0
  219. package/dist/components/ui/financial-cards.mjs +2 -2
  220. package/dist/components/ui/financial-drawers.js +1 -1
  221. package/dist/components/ui/financial-drawers.mjs +5 -5
  222. package/dist/components/ui/financial-sections.mjs +4 -4
  223. package/dist/components/ui/form-primitives.mjs +2 -2
  224. package/dist/components/ui/frontend-signup-steps.js +1239 -0
  225. package/dist/components/ui/frontend-signup-steps.mjs +38 -0
  226. package/dist/components/ui/income-bar-chart.mjs +4 -4
  227. package/dist/components/ui/income-sources-card.js +269 -0
  228. package/dist/components/ui/income-sources-card.mjs +100 -0
  229. package/dist/components/ui/income-summary-component.js +361 -0
  230. package/dist/components/ui/income-summary-component.mjs +84 -0
  231. package/dist/components/ui/income-work-details.js +1663 -0
  232. package/dist/components/ui/income-work-details.mjs +28 -0
  233. package/dist/components/ui/incoming-outgoings-card.js +218 -0
  234. package/dist/components/ui/incoming-outgoings-card.mjs +82 -0
  235. package/dist/components/ui/interest-rate-input.js +442 -0
  236. package/dist/components/ui/interest-rate-input.mjs +90 -0
  237. package/dist/components/ui/interest-rate-section.js +337 -0
  238. package/dist/components/ui/interest-rate-section.mjs +84 -0
  239. package/dist/components/ui/interest-rate-used.js +202 -0
  240. package/dist/components/ui/interest-rate-used.mjs +62 -0
  241. package/dist/components/ui/kanban-column.js +338 -160
  242. package/dist/components/ui/kanban-column.mjs +13 -11
  243. package/dist/components/ui/loan-applicant-information.js +336 -0
  244. package/dist/components/ui/loan-applicant-information.mjs +59 -0
  245. package/dist/components/ui/loan-applicant-invite.js +319 -0
  246. package/dist/components/ui/loan-applicant-invite.mjs +68 -0
  247. package/dist/components/ui/loan-application-badge.js +236 -0
  248. package/dist/components/ui/loan-application-badge.mjs +10 -0
  249. package/dist/components/ui/loan-application-cards.js +356 -0
  250. package/dist/components/ui/loan-application-cards.mjs +110 -0
  251. package/dist/components/ui/loan-entry-shell.js +104 -0
  252. package/dist/components/ui/loan-entry-shell.mjs +8 -0
  253. package/dist/components/ui/loan-financials.js +410 -0
  254. package/dist/components/ui/loan-financials.mjs +76 -0
  255. package/dist/components/ui/loan-option-card.js +102 -0
  256. package/dist/components/ui/loan-option-card.mjs +41 -0
  257. package/dist/components/ui/loan-option-group.js +288 -0
  258. package/dist/components/ui/loan-option-group.mjs +10 -0
  259. package/dist/components/ui/loan-steps.js +1121 -0
  260. package/dist/components/ui/loan-steps.mjs +509 -0
  261. package/dist/components/ui/loan-wizard-shell.js +452 -0
  262. package/dist/components/ui/loan-wizard-shell.mjs +11 -0
  263. package/dist/components/ui/money-input-with-slider.js +210 -0
  264. package/dist/components/ui/money-input-with-slider.mjs +10 -0
  265. package/dist/components/ui/money-item-with-color-indicator.js +314 -0
  266. package/dist/components/ui/money-item-with-color-indicator.mjs +20 -0
  267. package/dist/components/ui/opportunity-card.js +235 -97
  268. package/dist/components/ui/opportunity-card.mjs +11 -9
  269. package/dist/components/ui/opportunity-edit-modals.js +1 -1
  270. package/dist/components/ui/opportunity-edit-modals.mjs +15 -15
  271. package/dist/components/ui/opportunity-summary-tab.js +1 -1
  272. package/dist/components/ui/opportunity-summary-tab.mjs +19 -19
  273. package/dist/components/ui/pagination.mjs +4 -4
  274. package/dist/components/ui/password-strength-tooltip.mjs +4 -4
  275. package/dist/components/ui/pipeline-board.js +358 -176
  276. package/dist/components/ui/pipeline-board.mjs +16 -14
  277. package/dist/components/ui/pipeline-chart.mjs +3 -3
  278. package/dist/components/ui/pipeline-dialogs.js +1 -1
  279. package/dist/components/ui/pipeline-dialogs.mjs +9 -9
  280. package/dist/components/ui/pipeline-primitives.js +75 -8
  281. package/dist/components/ui/pipeline-primitives.mjs +3 -2
  282. package/dist/components/ui/popover.mjs +3 -3
  283. package/dist/components/ui/property-asset-card.js +428 -0
  284. package/dist/components/ui/property-asset-card.mjs +156 -0
  285. package/dist/components/ui/property-cashflow-doughnut-chart.mjs +4 -4
  286. package/dist/components/ui/property-debt-equity-doughnut-chart.mjs +4 -4
  287. package/dist/components/ui/property-list-carousel.js +295 -0
  288. package/dist/components/ui/property-list-carousel.mjs +11 -0
  289. package/dist/components/ui/property-mobile-estimate-line-chart.mjs +4 -4
  290. package/dist/components/ui/property-report-dialog.js +1148 -0
  291. package/dist/components/ui/property-report-dialog.mjs +25 -0
  292. package/dist/components/ui/resource-center.js +748 -0
  293. package/dist/components/ui/resource-center.mjs +24 -0
  294. package/dist/components/ui/review-alerts-dialog.js +569 -0
  295. package/dist/components/ui/review-alerts-dialog.mjs +15 -0
  296. package/dist/components/ui/savings-goal-modal.js +1148 -0
  297. package/dist/components/ui/savings-goal-modal.mjs +160 -0
  298. package/dist/components/ui/scenario-drawer.js +791 -0
  299. package/dist/components/ui/scenario-drawer.mjs +294 -0
  300. package/dist/components/ui/scenario-item.js +256 -0
  301. package/dist/components/ui/scenario-item.mjs +11 -0
  302. package/dist/components/ui/scenario-list.js +507 -0
  303. package/dist/components/ui/scenario-list.mjs +100 -0
  304. package/dist/components/ui/select.mjs +3 -3
  305. package/dist/components/ui/share-details-dialog.js +636 -0
  306. package/dist/components/ui/share-details-dialog.mjs +19 -0
  307. package/dist/components/ui/sheet.mjs +3 -3
  308. package/dist/components/ui/sidebar-nav.mjs +5 -5
  309. package/dist/components/ui/signup-form-primitives.js +770 -0
  310. package/dist/components/ui/signup-form-primitives.mjs +25 -0
  311. package/dist/components/ui/signup-shell.js +338 -0
  312. package/dist/components/ui/signup-shell.mjs +13 -0
  313. package/dist/components/ui/stage-timeline.js +103 -33
  314. package/dist/components/ui/stage-timeline.mjs +5 -4
  315. package/dist/components/ui/submission-confirmation-card.js +284 -0
  316. package/dist/components/ui/submission-confirmation-card.mjs +62 -0
  317. package/dist/components/ui/tooltip.mjs +3 -3
  318. package/dist/components/ui/top-three-product.js +374 -0
  319. package/dist/components/ui/top-three-product.mjs +129 -0
  320. package/dist/components/ui/transactions-expense-categories-doughnut-chart.mjs +4 -4
  321. package/dist/components/ui/transactions-income-expense-bar-chart.mjs +5 -5
  322. package/dist/components/ui/transactions-liabilities-breakdown-doughnut-chart.mjs +4 -4
  323. package/dist/components/ui/transactions-summary-block.js +95 -0
  324. package/dist/components/ui/transactions-summary-block.mjs +34 -0
  325. package/dist/components/ui/two-fa-setup-form.mjs +3 -3
  326. package/dist/index.js +9430 -4573
  327. package/dist/index.mjs +404 -251
  328. package/dist/lib/colors.js +6 -0
  329. package/dist/lib/colors.mjs +3 -1
  330. package/dist/lib/theme-provider.mjs +2 -2
  331. package/dist/styles.css +1 -1
  332. package/package.json +367 -3
  333. package/src/components/index.tsx +223 -0
  334. package/src/components/ui/about-you-form.tsx +397 -0
  335. package/src/components/ui/account-list-carousel.tsx +87 -0
  336. package/src/components/ui/add-lead-modal.tsx +1 -1
  337. package/src/components/ui/agent-evaluation-toast.tsx +191 -0
  338. package/src/components/ui/ai-builder.tsx +3 -5
  339. package/src/components/ui/ai-conversations.tsx +1 -1
  340. package/src/components/ui/applicant-document-checklist.tsx +175 -0
  341. package/src/components/ui/applicant-expenses-section.tsx +260 -0
  342. package/src/components/ui/applicant-navigation-bar.tsx +104 -0
  343. package/src/components/ui/applicant-switcher.tsx +54 -0
  344. package/src/components/ui/application-mobile-layout.tsx +34 -0
  345. package/src/components/ui/appointment-availability-settings.tsx +115 -23
  346. package/src/components/ui/appointment-book-dialog.tsx +406 -259
  347. package/src/components/ui/appointment-calendar-view.tsx +4 -1
  348. package/src/components/ui/appointment-detail-sheet.tsx +59 -24
  349. package/src/components/ui/appointment-time-slot-picker.tsx +5 -1
  350. package/src/components/ui/asset-accordion.tsx +241 -0
  351. package/src/components/ui/assets-liabilities-side-card.tsx +157 -0
  352. package/src/components/ui/auth-page-layout.tsx +3 -3
  353. package/src/components/ui/backoffice-alert-matching-chart.tsx +320 -0
  354. package/src/components/ui/backoffice-contact-matching-chart.tsx +341 -0
  355. package/src/components/ui/backoffice-signup-steps.tsx +615 -0
  356. package/src/components/ui/bank-statement-document-table.tsx +222 -0
  357. package/src/components/ui/bank-statement-generate-dialog.tsx +435 -0
  358. package/src/components/ui/bank-statement-pdf-viewer.tsx +241 -0
  359. package/src/components/ui/banking-accounts-connect.tsx +187 -0
  360. package/src/components/ui/borrowing-capacity-atoms.tsx +228 -0
  361. package/src/components/ui/borrowing-capacity-card.tsx +155 -0
  362. package/src/components/ui/broker-info-panel.tsx +94 -0
  363. package/src/components/ui/calculator-input-item.tsx +49 -0
  364. package/src/components/ui/calculator-section.tsx +275 -0
  365. package/src/components/ui/category-edit-dialog.tsx +300 -0
  366. package/src/components/ui/connect-bank-step.tsx +379 -0
  367. package/src/components/ui/contact-alert-dialog.tsx +710 -0
  368. package/src/components/ui/create-contact-modal.tsx +237 -0
  369. package/src/components/ui/csv-import-modal.tsx +153 -0
  370. package/src/components/ui/currency-input.tsx +104 -0
  371. package/src/components/ui/dashboard-expense-categories.tsx +262 -0
  372. package/src/components/ui/dashboard-transactions-table.tsx +241 -0
  373. package/src/components/ui/debt-accordion.tsx +254 -0
  374. package/src/components/ui/delete-contact-component.tsx +87 -0
  375. package/src/components/ui/dialog.tsx +2 -2
  376. package/src/components/ui/dynamic-tabs.tsx +149 -0
  377. package/src/components/ui/editable-money-item.tsx +176 -0
  378. package/src/components/ui/expense-detail-item.tsx +216 -0
  379. package/src/components/ui/expense-work-details.tsx +229 -0
  380. package/src/components/ui/file-preview-dialog.tsx +292 -0
  381. package/src/components/ui/financial-drawers.tsx +1 -1
  382. package/src/components/ui/frontend-signup-steps.tsx +548 -0
  383. package/src/components/ui/income-sources-card.tsx +143 -0
  384. package/src/components/ui/income-summary-component.tsx +120 -0
  385. package/src/components/ui/income-work-details.tsx +429 -0
  386. package/src/components/ui/incoming-outgoings-card.tsx +111 -0
  387. package/src/components/ui/interest-rate-input.tsx +111 -0
  388. package/src/components/ui/interest-rate-section.tsx +158 -0
  389. package/src/components/ui/interest-rate-used.tsx +82 -0
  390. package/src/components/ui/kanban-column.tsx +53 -2
  391. package/src/components/ui/loan-applicant-information.tsx +64 -0
  392. package/src/components/ui/loan-applicant-invite.tsx +67 -0
  393. package/src/components/ui/loan-application-badge.tsx +70 -0
  394. package/src/components/ui/loan-application-cards.tsx +152 -0
  395. package/src/components/ui/loan-entry-shell.tsx +86 -0
  396. package/src/components/ui/loan-financials.tsx +77 -0
  397. package/src/components/ui/loan-option-card.tsx +62 -0
  398. package/src/components/ui/loan-option-group.tsx +106 -0
  399. package/src/components/ui/loan-steps.tsx +630 -0
  400. package/src/components/ui/loan-wizard-shell.tsx +235 -0
  401. package/src/components/ui/money-input-with-slider.tsx +77 -0
  402. package/src/components/ui/money-item-with-color-indicator.tsx +30 -0
  403. package/src/components/ui/opportunity-card.tsx +46 -18
  404. package/src/components/ui/pipeline-board.tsx +13 -0
  405. package/src/components/ui/pipeline-primitives.tsx +28 -0
  406. package/src/components/ui/property-asset-card.tsx +221 -0
  407. package/src/components/ui/property-list-carousel.tsx +73 -0
  408. package/src/components/ui/property-report-dialog.tsx +355 -0
  409. package/src/components/ui/resource-center.tsx +539 -0
  410. package/src/components/ui/review-alerts-dialog.tsx +163 -0
  411. package/src/components/ui/savings-goal-modal.tsx +169 -0
  412. package/src/components/ui/scenario-drawer.tsx +358 -0
  413. package/src/components/ui/scenario-item.tsx +141 -0
  414. package/src/components/ui/scenario-list.tsx +150 -0
  415. package/src/components/ui/share-details-dialog.tsx +238 -0
  416. package/src/components/ui/signup-form-primitives.tsx +212 -0
  417. package/src/components/ui/signup-shell.tsx +180 -0
  418. package/src/components/ui/stage-timeline.tsx +11 -0
  419. package/src/components/ui/submission-confirmation-card.tsx +68 -0
  420. package/src/components/ui/top-three-product.tsx +207 -0
  421. package/src/components/ui/transactions-summary-block.tsx +59 -0
  422. package/src/lib/colors.ts +12 -0
  423. package/src/lib/format-date.ts +2 -2
  424. package/src/styles/styles-css.ts +1 -1
  425. package/tsup.config.ts +77 -1
  426. package/dist/{chunk-5VOTTIXF.mjs → chunk-FRT3S72S.mjs} +3 -3
  427. package/dist/{chunk-7BTFGCFC.mjs → chunk-MUV4EGDW.mjs} +3 -3
  428. package/dist/{chunk-57ZXILTS.mjs → chunk-MXP2RX2V.mjs} +3 -3
  429. package/dist/{chunk-ZKWXDQDG.mjs → chunk-VCDGLN25.mjs} +3 -3
  430. package/dist/{chunk-FLL633WS.mjs → chunk-ZXEUBBHJ.mjs} +3 -3
@@ -0,0 +1,254 @@
1
+ import * as React from "react";
2
+ import { cn } from "@/lib/utils";
3
+ import {
4
+ Accordion,
5
+ AccordionContent,
6
+ AccordionItem,
7
+ AccordionTrigger,
8
+ } from "@/components/ui/accordion";
9
+ import { Button } from "@/components/ui/button";
10
+ import { formatCurrency } from "@/lib/format-currency";
11
+
12
+ export type DebtItem = {
13
+ id: string;
14
+ description: string;
15
+ currentOwing: number;
16
+ monthlyRepayment?: number;
17
+ interestRate?: number;
18
+ subLabel?: string;
19
+ };
20
+
21
+ export type DebtGroup = {
22
+ debtType: string;
23
+ label: string;
24
+ items: DebtItem[];
25
+ };
26
+
27
+ export type DebtAccordionProps = {
28
+ groups: DebtGroup[];
29
+ openGroups?: string[];
30
+ onToggleGroup?: (debtType: string) => void;
31
+ onAddItem?: (debtType: string) => void;
32
+ onEditItem?: (debtType: string, itemId: string) => void;
33
+ onDeleteItem?: (debtType: string, itemId: string) => void;
34
+ onPrev?: () => void;
35
+ onNext?: () => void;
36
+ nextLabel?: string;
37
+ /** Accessible label for the accordion landmark region. */
38
+ "aria-label"?: string;
39
+ className?: string;
40
+ };
41
+
42
+ export function DebtAccordion({
43
+ groups,
44
+ openGroups,
45
+ onToggleGroup,
46
+ onAddItem,
47
+ onEditItem,
48
+ onDeleteItem,
49
+ onPrev,
50
+ onNext,
51
+ nextLabel = "Next",
52
+ "aria-label": ariaLabel = "Debt list",
53
+ className,
54
+ }: DebtAccordionProps) {
55
+ const totalOwing = groups
56
+ .flatMap((g) => g.items)
57
+ .reduce((sum, item) => sum + item.currentOwing, 0);
58
+
59
+ return (
60
+ <div className={cn("flex flex-col gap-4 font-sans", className)}>
61
+ <div className="flex items-center justify-between pb-2">
62
+ <h3 className="text-sm font-semibold text-foreground">Debts</h3>
63
+ <span className="text-sm font-medium text-foreground">
64
+ Total owing: {formatCurrency(totalOwing)}
65
+ </span>
66
+ </div>
67
+
68
+ <Accordion
69
+ type="multiple"
70
+ aria-label={ariaLabel}
71
+ value={openGroups}
72
+ onValueChange={(vals) => {
73
+ const prev = new Set(openGroups ?? []);
74
+ const next = new Set(vals as string[]);
75
+ for (const v of next) {
76
+ if (!prev.has(v)) onToggleGroup?.(v);
77
+ }
78
+ for (const v of prev) {
79
+ if (!next.has(v)) onToggleGroup?.(v);
80
+ }
81
+ }}
82
+ >
83
+ {groups.map((group) => (
84
+ <AccordionItem key={group.debtType} value={group.debtType}>
85
+ <AccordionTrigger>
86
+ <div className="flex w-full items-center justify-between pr-2">
87
+ <span>{group.label}</span>
88
+ <span className="text-xs text-muted-foreground">
89
+ {group.items.length}{" "}
90
+ {group.items.length === 1 ? "item" : "items"} ·{" "}
91
+ {formatCurrency(
92
+ group.items.reduce((s, i) => s + i.currentOwing, 0),
93
+ )}{" "}
94
+ owing
95
+ </span>
96
+ </div>
97
+ </AccordionTrigger>
98
+ <AccordionContent>
99
+ <div className="flex flex-col gap-2 pb-2">
100
+ {group.items.length === 0 ? (
101
+ <p className="text-xs text-muted-foreground">
102
+ No {group.label.toLowerCase()} added yet.
103
+ </p>
104
+ ) : (
105
+ group.items.map((item) => (
106
+ <DebtItemRow
107
+ key={item.id}
108
+ item={item}
109
+ onEdit={() => onEditItem?.(group.debtType, item.id)}
110
+ onDelete={() => onDeleteItem?.(group.debtType, item.id)}
111
+ />
112
+ ))
113
+ )}
114
+ <button
115
+ type="button"
116
+ onClick={() => onAddItem?.(group.debtType)}
117
+ className="mt-1 flex items-center gap-1 text-xs text-primary hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
118
+ >
119
+ <PlusIcon className="size-3" />
120
+ Add {group.label}
121
+ </button>
122
+ </div>
123
+ </AccordionContent>
124
+ </AccordionItem>
125
+ ))}
126
+ </Accordion>
127
+
128
+ <div className="flex items-center justify-between pt-2">
129
+ {onPrev ? (
130
+ <Button variant="outline" onClick={onPrev}>
131
+ <ChevronLeftIcon className="mr-1 size-4" />
132
+ Previous
133
+ </Button>
134
+ ) : (
135
+ <span />
136
+ )}
137
+ {onNext && (
138
+ <Button onClick={onNext}>
139
+ {nextLabel}
140
+ <ChevronRightIcon className="ml-1 size-4" />
141
+ </Button>
142
+ )}
143
+ </div>
144
+ </div>
145
+ );
146
+ }
147
+
148
+ function DebtItemRow({
149
+ item,
150
+ onEdit,
151
+ onDelete,
152
+ }: {
153
+ item: DebtItem;
154
+ onEdit?: () => void;
155
+ onDelete?: () => void;
156
+ }) {
157
+ return (
158
+ <div className="flex items-start justify-between border border-border p-3">
159
+ <div className="flex flex-col gap-0.5">
160
+ <span className="text-sm font-medium text-foreground">
161
+ {item.description}
162
+ </span>
163
+ {item.subLabel && (
164
+ <span className="text-xs text-muted-foreground">{item.subLabel}</span>
165
+ )}
166
+ <div className="flex flex-wrap gap-3">
167
+ <span className="text-sm text-foreground">
168
+ Owing: {formatCurrency(item.currentOwing)}
169
+ </span>
170
+ {item.monthlyRepayment != null && (
171
+ <span className="text-xs text-muted-foreground">
172
+ Repayment: {formatCurrency(item.monthlyRepayment)}/mo
173
+ </span>
174
+ )}
175
+ {item.interestRate != null && (
176
+ <span className="text-xs text-muted-foreground">
177
+ Rate: {item.interestRate}%
178
+ </span>
179
+ )}
180
+ </div>
181
+ </div>
182
+ <div className="flex shrink-0 items-center gap-2">
183
+ <button
184
+ type="button"
185
+ onClick={onEdit}
186
+ className="text-xs text-primary hover:underline focus-visible:outline-none"
187
+ >
188
+ Edit
189
+ </button>
190
+ <button
191
+ type="button"
192
+ onClick={onDelete}
193
+ className="text-xs text-destructive hover:underline focus-visible:outline-none"
194
+ >
195
+ Delete
196
+ </button>
197
+ </div>
198
+ </div>
199
+ );
200
+ }
201
+
202
+ function PlusIcon({ className }: { className?: string }) {
203
+ return (
204
+ <svg
205
+ xmlns="http://www.w3.org/2000/svg"
206
+ viewBox="0 0 24 24"
207
+ fill="none"
208
+ stroke="currentColor"
209
+ strokeWidth={2}
210
+ strokeLinecap="round"
211
+ strokeLinejoin="round"
212
+ className={className}
213
+ aria-hidden="true"
214
+ >
215
+ <path d="M12 5v14M5 12h14" />
216
+ </svg>
217
+ );
218
+ }
219
+
220
+ function ChevronLeftIcon({ className }: { className?: string }) {
221
+ return (
222
+ <svg
223
+ xmlns="http://www.w3.org/2000/svg"
224
+ viewBox="0 0 24 24"
225
+ fill="none"
226
+ stroke="currentColor"
227
+ strokeWidth={2}
228
+ strokeLinecap="round"
229
+ strokeLinejoin="round"
230
+ className={className}
231
+ aria-hidden="true"
232
+ >
233
+ <path d="m15 18-6-6 6-6" />
234
+ </svg>
235
+ );
236
+ }
237
+
238
+ function ChevronRightIcon({ className }: { className?: string }) {
239
+ return (
240
+ <svg
241
+ xmlns="http://www.w3.org/2000/svg"
242
+ viewBox="0 0 24 24"
243
+ fill="none"
244
+ stroke="currentColor"
245
+ strokeWidth={2}
246
+ strokeLinecap="round"
247
+ strokeLinejoin="round"
248
+ className={className}
249
+ aria-hidden="true"
250
+ >
251
+ <path d="m9 18 6-6-6-6" />
252
+ </svg>
253
+ );
254
+ }
@@ -0,0 +1,87 @@
1
+ import * as React from "react";
2
+ import {
3
+ AlertDialog,
4
+ AlertDialogAction,
5
+ AlertDialogCancel,
6
+ AlertDialogContent,
7
+ AlertDialogDescription,
8
+ AlertDialogFooter,
9
+ AlertDialogHeader,
10
+ AlertDialogTitle,
11
+ } from "@/components/ui/alert-dialog";
12
+ import { Spinner } from "@/components/ui/spinner";
13
+
14
+ // ---------------------------------------------------------------------------
15
+ // Types
16
+ // ---------------------------------------------------------------------------
17
+
18
+ export interface DeleteContactComponentProps {
19
+ open: boolean;
20
+ onOpenChange: (open: boolean) => void;
21
+ /** "single" — confirm deleting one contact by name. */
22
+ mode: "single" | "bulk";
23
+ /** Required when mode="single". */
24
+ contactName?: string;
25
+ /** Required when mode="bulk". Number of contacts to delete. */
26
+ count?: number;
27
+ onConfirm: () => void;
28
+ isLoading?: boolean;
29
+ }
30
+
31
+ // ---------------------------------------------------------------------------
32
+ // Component
33
+ // ---------------------------------------------------------------------------
34
+
35
+ export function DeleteContactComponent({
36
+ open,
37
+ onOpenChange,
38
+ mode,
39
+ contactName,
40
+ count = 0,
41
+ onConfirm,
42
+ isLoading = false,
43
+ }: DeleteContactComponentProps) {
44
+ const isSingle = mode === "single";
45
+
46
+ const title = isSingle
47
+ ? `Delete ${contactName ?? "this contact"}?`
48
+ : `Delete ${count} contact${count !== 1 ? "s" : ""}?`;
49
+
50
+ const description = isSingle
51
+ ? `This will permanently remove ${contactName ?? "this contact"} and all their associated data. This action cannot be undone.`
52
+ : `This will permanently remove ${count} selected contact${count !== 1 ? "s" : ""} and all their associated data. This action cannot be undone.`;
53
+
54
+ return (
55
+ <AlertDialog
56
+ open={open}
57
+ onOpenChange={isLoading ? undefined : onOpenChange}
58
+ >
59
+ <AlertDialogContent>
60
+ <AlertDialogHeader>
61
+ <AlertDialogTitle className="text-destructive">
62
+ {title}
63
+ </AlertDialogTitle>
64
+ <AlertDialogDescription>{description}</AlertDialogDescription>
65
+ </AlertDialogHeader>
66
+
67
+ <AlertDialogFooter>
68
+ <AlertDialogCancel disabled={isLoading}>Cancel</AlertDialogCancel>
69
+ <AlertDialogAction
70
+ onClick={onConfirm}
71
+ disabled={isLoading}
72
+ className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
73
+ >
74
+ {isLoading ? (
75
+ <>
76
+ <Spinner className="size-3.5" />
77
+ Deleting…
78
+ </>
79
+ ) : (
80
+ "Delete"
81
+ )}
82
+ </AlertDialogAction>
83
+ </AlertDialogFooter>
84
+ </AlertDialogContent>
85
+ </AlertDialog>
86
+ );
87
+ }
@@ -100,7 +100,7 @@ export type DialogContentProps = React.ComponentProps<
100
100
  * instead of appending to document.body.
101
101
  */
102
102
  container?: HTMLElement | null;
103
- /** Vertical alignment of the dialog. "top" anchors to top-4; "center" (default) vertically centers. */
103
+ /** Vertical alignment of the dialog. "top" (default) anchors to top-4; "center" vertically centers. */
104
104
  align?: "center" | "top";
105
105
  /**
106
106
  * Width behaviour of the dialog panel.
@@ -125,7 +125,7 @@ function DialogContent({
125
125
  showCloseButton = true,
126
126
  style,
127
127
  container,
128
- align = "center",
128
+ align = "top",
129
129
  size = "lg",
130
130
  minWidth,
131
131
  ...props
@@ -0,0 +1,149 @@
1
+ import * as React from "react";
2
+ import { useState } from "react";
3
+ import { cn } from "@/lib/utils";
4
+ import {
5
+ DropdownMenu,
6
+ DropdownMenuCheckboxItem,
7
+ DropdownMenuContent,
8
+ DropdownMenuTrigger,
9
+ DropdownMenuPortal,
10
+ } from "@/components/ui/dropdown-menu";
11
+ import { Button } from "@/components/ui/button";
12
+
13
+ export type DynamicTabItem = {
14
+ value: string;
15
+ label: string;
16
+ };
17
+
18
+ export type DynamicTabsProps = {
19
+ /** Full list of available tabs (selected + unselected). */
20
+ allTabs: DynamicTabItem[];
21
+ /** Values of the tabs currently shown in the bar. */
22
+ selectedTabs: string[];
23
+ /** Currently active tab value. */
24
+ activeTab: string;
25
+ onTabChange: (value: string) => void;
26
+ /** Called when the user adds/removes tabs via the overflow dropdown. */
27
+ onTabsChange?: (next: string[]) => void;
28
+ /** Label for the overflow toggle button. */
29
+ expandLabel?: string;
30
+ className?: string;
31
+ };
32
+
33
+ export function DynamicTabs({
34
+ allTabs,
35
+ selectedTabs,
36
+ activeTab,
37
+ onTabChange,
38
+ onTabsChange,
39
+ expandLabel = "More",
40
+ className,
41
+ }: DynamicTabsProps) {
42
+ // Staged selections inside the dropdown (committed on close).
43
+ const [staged, setStaged] = useState<Set<string>>(new Set());
44
+ const [open, setOpen] = useState(false);
45
+
46
+ const selectedSet = new Set(selectedTabs);
47
+
48
+ const handleOpenChange = (next: boolean) => {
49
+ if (!next && staged.size > 0) {
50
+ // Commit: append new tabs preserving original order
51
+ const ordered = allTabs
52
+ .filter((t) => staged.has(t.value))
53
+ .map((t) => t.value);
54
+ onTabsChange?.([...selectedTabs, ...ordered]);
55
+ setStaged(new Set());
56
+ }
57
+ setOpen(next);
58
+ };
59
+
60
+ const handleToggle = (value: string, checked: boolean) => {
61
+ if (selectedSet.has(value)) return; // already shown — no-op
62
+ setStaged((prev) => {
63
+ const next = new Set(prev);
64
+ checked ? next.add(value) : next.delete(value);
65
+ return next;
66
+ });
67
+ };
68
+
69
+ const hasOverflow = allTabs.some((t) => !selectedSet.has(t.value));
70
+
71
+ return (
72
+ // Outer wrapper is NOT role="tablist" — the dropdown must live outside it
73
+ <div
74
+ className={cn(
75
+ "flex items-end gap-0 border-b border-border font-sans",
76
+ className,
77
+ )}
78
+ >
79
+ {/* Tab buttons only — valid children of tablist */}
80
+ <div role="tablist" className="flex min-w-0 flex-1 items-end gap-0">
81
+ {selectedTabs.map((value) => {
82
+ const tab = allTabs.find((t) => t.value === value);
83
+ if (!tab) return null;
84
+ const isActive = value === activeTab;
85
+ return (
86
+ <button
87
+ key={value}
88
+ role="tab"
89
+ aria-selected={isActive}
90
+ onClick={() => onTabChange(value)}
91
+ className={cn(
92
+ "relative shrink-0 cursor-pointer px-4 py-2.5 text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
93
+ isActive
94
+ ? "border-b-2 border-primary text-foreground"
95
+ : "border-b-2 border-transparent text-muted-foreground hover:text-foreground",
96
+ )}
97
+ >
98
+ {tab.label}
99
+ </button>
100
+ );
101
+ })}
102
+ </div>
103
+
104
+ {/* Overflow dropdown lives outside the tablist to satisfy aria-required-children */}
105
+ {hasOverflow && (
106
+ <DropdownMenu open={open} onOpenChange={handleOpenChange}>
107
+ <DropdownMenuTrigger className="mb-px shrink-0 inline-flex items-center gap-1 px-3 py-2 text-sm font-medium text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring">
108
+ {expandLabel}
109
+ <ChevronDownIcon className="size-3" />
110
+ </DropdownMenuTrigger>
111
+ <DropdownMenuPortal>
112
+ <DropdownMenuContent align="end" className="min-w-48">
113
+ {allTabs.map((tab) => (
114
+ <DropdownMenuCheckboxItem
115
+ key={tab.value}
116
+ checked={selectedSet.has(tab.value) || staged.has(tab.value)}
117
+ disabled={selectedSet.has(tab.value)}
118
+ onCheckedChange={(checked) =>
119
+ handleToggle(tab.value, checked)
120
+ }
121
+ >
122
+ {tab.label}
123
+ </DropdownMenuCheckboxItem>
124
+ ))}
125
+ </DropdownMenuContent>
126
+ </DropdownMenuPortal>
127
+ </DropdownMenu>
128
+ )}
129
+ </div>
130
+ );
131
+ }
132
+
133
+ function ChevronDownIcon({ className }: { className?: string }) {
134
+ return (
135
+ <svg
136
+ xmlns="http://www.w3.org/2000/svg"
137
+ viewBox="0 0 24 24"
138
+ fill="none"
139
+ stroke="currentColor"
140
+ strokeWidth={2}
141
+ strokeLinecap="round"
142
+ strokeLinejoin="round"
143
+ className={className}
144
+ aria-hidden="true"
145
+ >
146
+ <path d="m6 9 6 6 6-6" />
147
+ </svg>
148
+ );
149
+ }
@@ -0,0 +1,176 @@
1
+ import React, { useRef, useState } from "react";
2
+ import { Info, Pencil } from "lucide-react";
3
+ import { cn } from "@/lib/utils";
4
+ import { colorMixSwatch } from "@/lib/colors";
5
+ import { Input } from "./input";
6
+ import {
7
+ Tooltip,
8
+ TooltipContent,
9
+ TooltipProvider,
10
+ TooltipTrigger,
11
+ } from "./tooltip";
12
+
13
+ /**
14
+ * A calculator-section row showing a color swatch, label, optional info
15
+ * tooltip, a money value, and an optional pencil edit trigger.
16
+ *
17
+ * Two modes:
18
+ *
19
+ * **Display + external callback** (`onEdit` only):
20
+ * Shows a formatted string and calls `onEdit` when the pencil is clicked.
21
+ * The parent handles the edit flow (e.g. opens a drawer).
22
+ *
23
+ * **Inline edit** (`numericValue` + `onValueChange`):
24
+ * Shows a formatted value in display state. Clicking the pencil (or the
25
+ * value itself) switches the row to an `<Input>` field. Committing via
26
+ * blur or Enter saves the value and returns to display state.
27
+ * `value` is used as the display-mode formatted string; `numericValue` /
28
+ * `onValueChange` drive the input.
29
+ *
30
+ * The pencil slot is always reserved (invisible when no edit prop) so value
31
+ * text stays column-aligned in mixed read-only / editable row groups.
32
+ */
33
+
34
+ export interface EditableMoneyItemProps {
35
+ /** Row label (e.g. "Current Expenses") */
36
+ label: string;
37
+ /** Pre-formatted display string (e.g. "$5,000") */
38
+ value: string;
39
+ /** CSS color used for the swatch border and tinted fill */
40
+ color: string;
41
+ /** Fill opacity for the swatch background (default: 0.4) */
42
+ fillOpacity?: number;
43
+ /** Optional tooltip text shown on the info icon */
44
+ tooltip?: string;
45
+
46
+ // ── External callback mode ──────────────────────────────────────────────────
47
+ /** Called when the pencil is clicked (use without numericValue / onValueChange) */
48
+ onEdit?: () => void;
49
+
50
+ // ── Inline edit mode ────────────────────────────────────────────────────────
51
+ /** Raw numeric value — enables inline edit when paired with onValueChange */
52
+ numericValue?: number;
53
+ /** Called with the new number when the inline input is committed */
54
+ onValueChange?: (value: number) => void;
55
+
56
+ className?: string;
57
+ }
58
+
59
+ export function EditableMoneyItem({
60
+ label,
61
+ value,
62
+ color,
63
+ fillOpacity = 0.4,
64
+ tooltip,
65
+ onEdit,
66
+ numericValue,
67
+ onValueChange,
68
+ className,
69
+ }: EditableMoneyItemProps) {
70
+ const [editing, setEditing] = useState(false);
71
+ const [draft, setDraft] = useState("");
72
+ const inputRef = useRef<HTMLInputElement>(null);
73
+
74
+ const inlineMode = numericValue !== undefined && onValueChange !== undefined;
75
+ const hasEditAction = inlineMode || Boolean(onEdit);
76
+
77
+ const startEditing = () => {
78
+ if (!inlineMode) {
79
+ onEdit?.();
80
+ return;
81
+ }
82
+ setDraft(String(numericValue ?? 0));
83
+ setEditing(true);
84
+ // Focus after React re-renders
85
+ setTimeout(() => inputRef.current?.select(), 0);
86
+ };
87
+
88
+ const commitEdit = () => {
89
+ const parsed = Number(draft.replace(/[^0-9.-]/g, ""));
90
+ onValueChange?.(isNaN(parsed) ? 0 : parsed);
91
+ setEditing(false);
92
+ };
93
+
94
+ const handleKeyDown = (e: React.KeyboardEvent) => {
95
+ if (e.key === "Enter") commitEdit();
96
+ if (e.key === "Escape") setEditing(false);
97
+ };
98
+
99
+ return (
100
+ <div
101
+ className={cn("flex items-center justify-between gap-3 py-2", className)}
102
+ >
103
+ {/* Label side */}
104
+ <div className="flex min-w-0 items-center gap-2">
105
+ <span
106
+ aria-hidden
107
+ className="h-5 w-5 shrink-0 border"
108
+ style={{
109
+ borderColor: color,
110
+ backgroundColor: colorMixSwatch(color, fillOpacity),
111
+ }}
112
+ />
113
+ <span className="truncate text-sm text-foreground">{label}</span>
114
+ {tooltip && (
115
+ <TooltipProvider>
116
+ <Tooltip>
117
+ <TooltipTrigger asChild>
118
+ <button
119
+ type="button"
120
+ aria-label="More info"
121
+ className="shrink-0 text-muted-foreground transition-colors hover:text-foreground"
122
+ >
123
+ <Info size={14} />
124
+ </button>
125
+ </TooltipTrigger>
126
+ <TooltipContent>
127
+ <p className="max-w-xs text-xs">{tooltip}</p>
128
+ </TooltipContent>
129
+ </Tooltip>
130
+ </TooltipProvider>
131
+ )}
132
+ </div>
133
+
134
+ {/* Value + pencil side */}
135
+ <div className="flex shrink-0 items-center gap-1">
136
+ {editing && inlineMode ? (
137
+ <Input
138
+ ref={inputRef}
139
+ autoFocus
140
+ value={draft}
141
+ onChange={(e) => setDraft(e.target.value)}
142
+ onBlur={commitEdit}
143
+ onKeyDown={handleKeyDown}
144
+ className="h-7 w-28 text-right text-base font-semibold tabular-nums"
145
+ />
146
+ ) : (
147
+ <button
148
+ type="button"
149
+ disabled={!hasEditAction}
150
+ onClick={inlineMode ? startEditing : undefined}
151
+ className={cn(
152
+ "text-base font-semibold tabular-nums text-foreground",
153
+ inlineMode && "cursor-text hover:opacity-70",
154
+ )}
155
+ >
156
+ {value}
157
+ </button>
158
+ )}
159
+
160
+ <button
161
+ type="button"
162
+ aria-label="Edit value"
163
+ onClick={startEditing}
164
+ className={cn(
165
+ "p-1 transition-colors",
166
+ hasEditAction
167
+ ? "text-muted-foreground hover:text-foreground"
168
+ : "invisible pointer-events-none",
169
+ )}
170
+ >
171
+ <Pencil size={14} />
172
+ </button>
173
+ </div>
174
+ </div>
175
+ );
176
+ }