@wealthx/shadcn 1.4.1 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +360 -180
- package/CHANGELOG.md +6 -0
- package/dist/chunk-2A53WPEC.mjs +182 -0
- package/dist/{chunk-SIVYAI3M.mjs → chunk-2LLFNGJZ.mjs} +15 -15
- package/dist/chunk-2QNOPXMQ.mjs +360 -0
- package/dist/{chunk-K5QV4TT6.mjs → chunk-2WCIORP7.mjs} +29 -4
- package/dist/{chunk-5NF6T2RS.mjs → chunk-3AREQTZU.mjs} +8 -8
- package/dist/{chunk-2EM2FRU6.mjs → chunk-3WGFIFP6.mjs} +5 -5
- package/dist/{chunk-K4KOD3KR.mjs → chunk-42NEC57Y.mjs} +44 -25
- package/dist/{chunk-FL6DZFJK.mjs → chunk-46Q4335I.mjs} +121 -39
- package/dist/chunk-4DO3WM7V.mjs +48 -0
- package/dist/chunk-5FHBC6DY.mjs +68 -0
- package/dist/chunk-5SAYZ4CI.mjs +40 -0
- package/dist/chunk-5WMFKQZ6.mjs +180 -0
- package/dist/chunk-623YVI5O.mjs +43 -0
- package/dist/{chunk-SFH2NJEJ.mjs → chunk-6OSULDEO.mjs} +3 -3
- package/dist/{chunk-UALR6JGV.mjs → chunk-6SR4K5T5.mjs} +1 -1
- package/dist/{chunk-D2NSIIXG.mjs → chunk-7KT5HPYM.mjs} +11 -11
- package/dist/chunk-A6ER36CW.mjs +456 -0
- package/dist/{chunk-QX7IFQSF.mjs → chunk-AHKHVBWR.mjs} +4 -4
- package/dist/chunk-AHSCWXYJ.mjs +113 -0
- package/dist/{chunk-7GWRPXHD.mjs → chunk-AL6GOL2Y.mjs} +1 -1
- package/dist/{chunk-OIKBW2QD.mjs → chunk-AUEUTZIC.mjs} +13 -13
- package/dist/{chunk-FYPSTTEJ.mjs → chunk-B7DD3ODQ.mjs} +1 -1
- package/dist/{chunk-TLAWKTSA.mjs → chunk-BD3DWDT4.mjs} +3 -3
- package/dist/{chunk-S2FKV4M5.mjs → chunk-BDESHD25.mjs} +4 -4
- package/dist/{chunk-OKTJFDPN.mjs → chunk-BFB3UH7V.mjs} +2 -2
- package/dist/{chunk-DGNHGNYH.mjs → chunk-C6SWS7OW.mjs} +1 -1
- package/dist/chunk-CDVG7SFT.mjs +271 -0
- package/dist/chunk-CUSHAIUL.mjs +109 -0
- package/dist/{chunk-QXKGOMUX.mjs → chunk-CW32WTZU.mjs} +4 -4
- package/dist/{chunk-SET2ANTY.mjs → chunk-D447W45Z.mjs} +3 -3
- package/dist/{chunk-RCAOCHWA.mjs → chunk-DFL5CV75.mjs} +18 -18
- package/dist/chunk-DYSVJ473.mjs +162 -0
- package/dist/chunk-E3PQDBYI.mjs +288 -0
- package/dist/chunk-EMYBNPIA.mjs +83 -0
- package/dist/chunk-EUYPMDQG.mjs +348 -0
- package/dist/{chunk-XYPW2XA5.mjs → chunk-EW72FINW.mjs} +11 -11
- package/dist/chunk-F24U4QQQ.mjs +234 -0
- package/dist/{chunk-VB5M6OZQ.mjs → chunk-F4O2YPXJ.mjs} +1 -1
- package/dist/chunk-FFXTQTB4.mjs +84 -0
- package/dist/{chunk-ZOWL2L5J.mjs → chunk-FYUSF5KO.mjs} +5 -1
- package/dist/{chunk-FTPBQVQ6.mjs → chunk-GNER6MCO.mjs} +1 -1
- package/dist/{chunk-2D3HQPFN.mjs → chunk-HF4FUBCY.mjs} +5 -5
- package/dist/{chunk-RSUIPKGX.mjs → chunk-HNDTLT5X.mjs} +1 -1
- package/dist/{chunk-N6Q5IPKT.mjs → chunk-HO6S3ECM.mjs} +46 -18
- package/dist/{chunk-L4NSRQ3T.mjs → chunk-HROG643K.mjs} +1 -1
- package/dist/chunk-I2EKKSEF.mjs +148 -0
- package/dist/{chunk-QTRSCVQ3.mjs → chunk-I3UDLWQ7.mjs} +1 -1
- package/dist/{chunk-AE7MASLF.mjs → chunk-IQGKOT7A.mjs} +9 -6
- package/dist/chunk-IXR4BQSQ.mjs +290 -0
- package/dist/{chunk-4MM7LHM5.mjs → chunk-J5NW5NCT.mjs} +1 -1
- package/dist/{chunk-OLKMCXAR.mjs → chunk-JTG5R5YV.mjs} +24 -24
- package/dist/chunk-JUBUN65Q.mjs +106 -0
- package/dist/chunk-K7TWMLLW.mjs +520 -0
- package/dist/{chunk-BOW7U26Y.mjs → chunk-K7WSRWOU.mjs} +4 -4
- package/dist/{chunk-NTYQWVLI.mjs → chunk-KAD26MCC.mjs} +1 -1
- package/dist/{chunk-KCWNDYPZ.mjs → chunk-KB7MZMED.mjs} +4 -4
- package/dist/chunk-KCKYGQVQ.mjs +61 -0
- package/dist/{chunk-VY5NEUP7.mjs → chunk-KLJLDNCA.mjs} +1 -1
- package/dist/chunk-LLAGF6BA.mjs +49 -0
- package/dist/{chunk-G27TSQLQ.mjs → chunk-M4LTX3MH.mjs} +1 -1
- package/dist/{chunk-YIZHS72Z.mjs → chunk-MHHA7QGO.mjs} +94 -54
- package/dist/{chunk-P2N2PEFY.mjs → chunk-NCUH54IZ.mjs} +4 -4
- package/dist/{chunk-PNRUH7JY.mjs → chunk-OECGKCVF.mjs} +5 -5
- package/dist/{chunk-YE67AALL.mjs → chunk-OL65UQHQ.mjs} +10 -10
- package/dist/{chunk-LQULK2E3.mjs → chunk-OYBIUEGE.mjs} +1 -1
- package/dist/{chunk-LR6LHDP3.mjs → chunk-PGR53HMH.mjs} +7 -7
- package/dist/chunk-PUJ42INK.mjs +141 -0
- package/dist/{chunk-M4VYX2PV.mjs → chunk-PV3Y7QGK.mjs} +2 -2
- package/dist/{chunk-UJZ4UHWI.mjs → chunk-PV7PNA6K.mjs} +4 -4
- package/dist/{chunk-6HIOM2HL.mjs → chunk-Q35PNFJ7.mjs} +1 -1
- package/dist/{chunk-JZY6TNIS.mjs → chunk-Q5SGEIJV.mjs} +27 -27
- package/dist/{chunk-ZFCDYW6N.mjs → chunk-QAX6HCUH.mjs} +1 -1
- package/dist/chunk-QHJDGB54.mjs +135 -0
- package/dist/chunk-QQSOZQOC.mjs +27 -0
- package/dist/chunk-RUX3OLVZ.mjs +59 -0
- package/dist/{chunk-QOJ2DQD6.mjs → chunk-S4CTM3UE.mjs} +5 -0
- package/dist/{chunk-ZEDMKQK2.mjs → chunk-TAX3KL66.mjs} +1 -1
- package/dist/chunk-TC43SMIN.mjs +133 -0
- package/dist/chunk-TGVXRD53.mjs +174 -0
- package/dist/{chunk-K5VHK7CM.mjs → chunk-TLYSVRSK.mjs} +12 -12
- package/dist/{chunk-YCWLFG27.mjs → chunk-TOIVHWNC.mjs} +1 -1
- package/dist/chunk-TXUBGKB7.mjs +160 -0
- package/dist/chunk-UEREFDAE.mjs +75 -0
- package/dist/chunk-UTCQN6XU.mjs +123 -0
- package/dist/{chunk-37AE3OM5.mjs → chunk-V4CUTCHS.mjs} +4 -4
- package/dist/{chunk-THVO2N47.mjs → chunk-VFH632TB.mjs} +9 -9
- package/dist/{chunk-3ERBUVHC.mjs → chunk-VJ3GC7W3.mjs} +95 -49
- package/dist/{chunk-V6XGXYCJ.mjs → chunk-VLELWBEW.mjs} +4 -4
- package/dist/{chunk-FEZKMUCF.mjs → chunk-WDTXHLYM.mjs} +1 -1
- package/dist/chunk-WUA546RX.mjs +129 -0
- package/dist/chunk-XHGISOX5.mjs +257 -0
- package/dist/chunk-XIY5DJXI.mjs +168 -0
- package/dist/{chunk-TOWTPLRC.mjs → chunk-XN37434W.mjs} +8 -8
- package/dist/{chunk-KLTACJ2G.mjs → chunk-XTWAJWCQ.mjs} +1 -1
- package/dist/chunk-Y24TXIFJ.mjs +518 -0
- package/dist/{chunk-DMXYRCHM.mjs → chunk-Y6UM3VTN.mjs} +4 -4
- package/dist/components/ui/about-you-form.js +1120 -0
- package/dist/components/ui/about-you-form.mjs +323 -0
- package/dist/components/ui/account-list-carousel.js +304 -0
- package/dist/components/ui/account-list-carousel.mjs +11 -0
- package/dist/components/ui/add-column-modal.js +1 -1
- package/dist/components/ui/add-column-modal.mjs +6 -6
- package/dist/components/ui/add-lead-modal.js +2 -2
- package/dist/components/ui/add-lead-modal.mjs +6 -6
- package/dist/components/ui/advisor-card.mjs +2 -2
- package/dist/components/ui/agent-evaluation-toast.js +299 -0
- package/dist/components/ui/agent-evaluation-toast.mjs +12 -0
- package/dist/components/ui/ai-assistant-drawer.mjs +5 -5
- package/dist/components/ui/ai-builder.js +3 -3
- package/dist/components/ui/ai-builder.mjs +5 -5
- package/dist/components/ui/ai-conversations.js +2 -2
- package/dist/components/ui/ai-conversations.mjs +11 -11
- package/dist/components/ui/alert-dialog.mjs +3 -3
- package/dist/components/ui/applicant-document-checklist.js +346 -0
- package/dist/components/ui/applicant-document-checklist.mjs +10 -0
- package/dist/components/ui/applicant-expenses-section.js +455 -0
- package/dist/components/ui/applicant-expenses-section.mjs +220 -0
- package/dist/components/ui/applicant-navigation-bar.js +309 -0
- package/dist/components/ui/applicant-navigation-bar.mjs +87 -0
- package/dist/components/ui/applicant-switcher.js +268 -0
- package/dist/components/ui/applicant-switcher.mjs +46 -0
- package/dist/components/ui/application-mobile-layout.js +88 -0
- package/dist/components/ui/application-mobile-layout.mjs +8 -0
- package/dist/components/ui/appointment-action-dialogs.js +1 -1
- package/dist/components/ui/appointment-action-dialogs.mjs +10 -10
- package/dist/components/ui/appointment-availability-settings.js +78 -31
- package/dist/components/ui/appointment-availability-settings.mjs +12 -10
- package/dist/components/ui/appointment-book-dialog.js +137 -58
- package/dist/components/ui/appointment-book-dialog.mjs +14 -14
- package/dist/components/ui/appointment-calendar-view.js +1 -1
- package/dist/components/ui/appointment-calendar-view.mjs +4 -4
- package/dist/components/ui/appointment-detail-sheet.js +38 -11
- package/dist/components/ui/appointment-detail-sheet.mjs +13 -13
- package/dist/components/ui/appointment-gmail-connect.mjs +2 -2
- package/dist/components/ui/appointment-time-slot-picker.mjs +2 -2
- package/dist/components/ui/appointment-upcoming-card.js +1 -1
- package/dist/components/ui/appointment-upcoming-card.mjs +10 -10
- package/dist/components/ui/asset-accordion.js +506 -0
- package/dist/components/ui/asset-accordion.mjs +202 -0
- package/dist/components/ui/assets-liabilities-side-card.js +328 -0
- package/dist/components/ui/assets-liabilities-side-card.mjs +127 -0
- package/dist/components/ui/auth-page-layout.js +3 -3
- package/dist/components/ui/auth-page-layout.mjs +1 -1
- package/dist/components/ui/backoffice-alert-history-chart.mjs +4 -4
- package/dist/components/ui/backoffice-alert-matching-chart.js +786 -0
- package/dist/components/ui/backoffice-alert-matching-chart.mjs +19 -0
- package/dist/components/ui/backoffice-alerts-chart.mjs +4 -4
- package/dist/components/ui/backoffice-connections-chart.mjs +4 -4
- package/dist/components/ui/backoffice-contact-history-chart.mjs +4 -4
- package/dist/components/ui/backoffice-contact-matching-chart.js +803 -0
- package/dist/components/ui/backoffice-contact-matching-chart.mjs +19 -0
- package/dist/components/ui/backoffice-signup-steps.js +1673 -0
- package/dist/components/ui/backoffice-signup-steps.mjs +36 -0
- package/dist/components/ui/bank-statement-document-table.js +467 -0
- package/dist/components/ui/bank-statement-document-table.mjs +12 -0
- package/dist/components/ui/bank-statement-generate-dialog.js +1517 -0
- package/dist/components/ui/bank-statement-generate-dialog.mjs +27 -0
- package/dist/components/ui/bank-statement-pdf-viewer.js +525 -0
- package/dist/components/ui/bank-statement-pdf-viewer.mjs +14 -0
- package/dist/components/ui/banking-accounts-connect.js +336 -0
- package/dist/components/ui/banking-accounts-connect.mjs +114 -0
- package/dist/components/ui/borrowing-capacity-atoms.js +382 -0
- package/dist/components/ui/borrowing-capacity-atoms.mjs +17 -0
- package/dist/components/ui/borrowing-capacity-card.js +835 -0
- package/dist/components/ui/borrowing-capacity-card.mjs +89 -0
- package/dist/components/ui/borrowing-capacity-line-chart.mjs +4 -4
- package/dist/components/ui/broker-info-panel.js +281 -0
- package/dist/components/ui/broker-info-panel.mjs +59 -0
- package/dist/components/ui/calculator-input-item.js +101 -0
- package/dist/components/ui/calculator-input-item.mjs +8 -0
- package/dist/components/ui/calculator-section.js +743 -0
- package/dist/components/ui/calculator-section.mjs +220 -0
- package/dist/components/ui/calendar.mjs +2 -2
- package/dist/components/ui/cash-balance-line-chart.mjs +5 -5
- package/dist/components/ui/cashflow-bar-chart.mjs +4 -4
- package/dist/components/ui/category-edit-dialog.js +737 -0
- package/dist/components/ui/category-edit-dialog.mjs +16 -0
- package/dist/components/ui/chat-widget.mjs +3 -3
- package/dist/components/ui/color-picker.mjs +4 -4
- package/dist/components/ui/connect-bank-step.js +511 -0
- package/dist/components/ui/connect-bank-step.mjs +287 -0
- package/dist/components/ui/contact-alert-dialog.js +1405 -0
- package/dist/components/ui/contact-alert-dialog.mjs +27 -0
- package/dist/components/ui/create-contact-modal.js +1028 -0
- package/dist/components/ui/create-contact-modal.mjs +21 -0
- package/dist/components/ui/csv-import-modal.js +583 -0
- package/dist/components/ui/csv-import-modal.mjs +14 -0
- package/dist/components/ui/currency-input.js +439 -0
- package/dist/components/ui/currency-input.mjs +13 -0
- package/dist/components/ui/dashboard-expense-categories.js +355 -0
- package/dist/components/ui/dashboard-expense-categories.mjs +186 -0
- package/dist/components/ui/dashboard-transactions-table.js +1083 -0
- package/dist/components/ui/dashboard-transactions-table.mjs +177 -0
- package/dist/components/ui/data-table.mjs +6 -6
- package/dist/components/ui/date-picker.mjs +6 -6
- package/dist/components/ui/debt-accordion.js +523 -0
- package/dist/components/ui/debt-accordion.mjs +219 -0
- package/dist/components/ui/delete-contact-component.js +479 -0
- package/dist/components/ui/delete-contact-component.mjs +14 -0
- package/dist/components/ui/dialog.js +1 -1
- package/dist/components/ui/dialog.mjs +3 -3
- package/dist/components/ui/document-checklist-template.mjs +4 -4
- package/dist/components/ui/drawer.mjs +3 -3
- package/dist/components/ui/dropdown-menu.mjs +3 -3
- package/dist/components/ui/dynamic-tabs.js +274 -0
- package/dist/components/ui/dynamic-tabs.mjs +116 -0
- package/dist/components/ui/editable-money-item.js +306 -0
- package/dist/components/ui/editable-money-item.mjs +12 -0
- package/dist/components/ui/expense-bar-chart.mjs +4 -4
- package/dist/components/ui/expense-detail-item.js +506 -0
- package/dist/components/ui/expense-detail-item.mjs +15 -0
- package/dist/components/ui/expense-work-details.js +1259 -0
- package/dist/components/ui/expense-work-details.mjs +175 -0
- package/dist/components/ui/field.mjs +2 -2
- package/dist/components/ui/file-preview-dialog.js +704 -0
- package/dist/components/ui/file-preview-dialog.mjs +17 -0
- package/dist/components/ui/financial-cards.mjs +2 -2
- package/dist/components/ui/financial-drawers.js +1 -1
- package/dist/components/ui/financial-drawers.mjs +5 -5
- package/dist/components/ui/financial-sections.mjs +4 -4
- package/dist/components/ui/form-primitives.mjs +2 -2
- package/dist/components/ui/frontend-signup-steps.js +1239 -0
- package/dist/components/ui/frontend-signup-steps.mjs +38 -0
- package/dist/components/ui/income-bar-chart.mjs +4 -4
- package/dist/components/ui/income-sources-card.js +269 -0
- package/dist/components/ui/income-sources-card.mjs +100 -0
- package/dist/components/ui/income-summary-component.js +361 -0
- package/dist/components/ui/income-summary-component.mjs +84 -0
- package/dist/components/ui/income-work-details.js +1663 -0
- package/dist/components/ui/income-work-details.mjs +28 -0
- package/dist/components/ui/incoming-outgoings-card.js +218 -0
- package/dist/components/ui/incoming-outgoings-card.mjs +82 -0
- package/dist/components/ui/interest-rate-input.js +442 -0
- package/dist/components/ui/interest-rate-input.mjs +90 -0
- package/dist/components/ui/interest-rate-section.js +337 -0
- package/dist/components/ui/interest-rate-section.mjs +84 -0
- package/dist/components/ui/interest-rate-used.js +202 -0
- package/dist/components/ui/interest-rate-used.mjs +62 -0
- package/dist/components/ui/kanban-column.js +338 -160
- package/dist/components/ui/kanban-column.mjs +13 -11
- package/dist/components/ui/loan-applicant-information.js +336 -0
- package/dist/components/ui/loan-applicant-information.mjs +59 -0
- package/dist/components/ui/loan-applicant-invite.js +319 -0
- package/dist/components/ui/loan-applicant-invite.mjs +68 -0
- package/dist/components/ui/loan-application-badge.js +236 -0
- package/dist/components/ui/loan-application-badge.mjs +10 -0
- package/dist/components/ui/loan-application-cards.js +356 -0
- package/dist/components/ui/loan-application-cards.mjs +110 -0
- package/dist/components/ui/loan-entry-shell.js +104 -0
- package/dist/components/ui/loan-entry-shell.mjs +8 -0
- package/dist/components/ui/loan-financials.js +410 -0
- package/dist/components/ui/loan-financials.mjs +76 -0
- package/dist/components/ui/loan-option-card.js +102 -0
- package/dist/components/ui/loan-option-card.mjs +41 -0
- package/dist/components/ui/loan-option-group.js +288 -0
- package/dist/components/ui/loan-option-group.mjs +10 -0
- package/dist/components/ui/loan-steps.js +1121 -0
- package/dist/components/ui/loan-steps.mjs +509 -0
- package/dist/components/ui/loan-wizard-shell.js +452 -0
- package/dist/components/ui/loan-wizard-shell.mjs +11 -0
- package/dist/components/ui/money-input-with-slider.js +210 -0
- package/dist/components/ui/money-input-with-slider.mjs +10 -0
- package/dist/components/ui/money-item-with-color-indicator.js +314 -0
- package/dist/components/ui/money-item-with-color-indicator.mjs +20 -0
- package/dist/components/ui/opportunity-card.js +235 -97
- package/dist/components/ui/opportunity-card.mjs +11 -9
- package/dist/components/ui/opportunity-edit-modals.js +1 -1
- package/dist/components/ui/opportunity-edit-modals.mjs +15 -15
- package/dist/components/ui/opportunity-summary-tab.js +1 -1
- package/dist/components/ui/opportunity-summary-tab.mjs +19 -19
- package/dist/components/ui/pagination.mjs +4 -4
- package/dist/components/ui/password-strength-tooltip.mjs +4 -4
- package/dist/components/ui/pipeline-board.js +358 -176
- package/dist/components/ui/pipeline-board.mjs +16 -14
- package/dist/components/ui/pipeline-chart.mjs +3 -3
- package/dist/components/ui/pipeline-dialogs.js +1 -1
- package/dist/components/ui/pipeline-dialogs.mjs +9 -9
- package/dist/components/ui/pipeline-primitives.js +75 -8
- package/dist/components/ui/pipeline-primitives.mjs +3 -2
- package/dist/components/ui/popover.mjs +3 -3
- package/dist/components/ui/property-asset-card.js +428 -0
- package/dist/components/ui/property-asset-card.mjs +156 -0
- package/dist/components/ui/property-cashflow-doughnut-chart.mjs +4 -4
- package/dist/components/ui/property-debt-equity-doughnut-chart.mjs +4 -4
- package/dist/components/ui/property-list-carousel.js +295 -0
- package/dist/components/ui/property-list-carousel.mjs +11 -0
- package/dist/components/ui/property-mobile-estimate-line-chart.mjs +4 -4
- package/dist/components/ui/property-report-dialog.js +1148 -0
- package/dist/components/ui/property-report-dialog.mjs +25 -0
- package/dist/components/ui/resource-center.js +748 -0
- package/dist/components/ui/resource-center.mjs +24 -0
- package/dist/components/ui/review-alerts-dialog.js +569 -0
- package/dist/components/ui/review-alerts-dialog.mjs +15 -0
- package/dist/components/ui/savings-goal-modal.js +1148 -0
- package/dist/components/ui/savings-goal-modal.mjs +160 -0
- package/dist/components/ui/scenario-drawer.js +791 -0
- package/dist/components/ui/scenario-drawer.mjs +294 -0
- package/dist/components/ui/scenario-item.js +256 -0
- package/dist/components/ui/scenario-item.mjs +11 -0
- package/dist/components/ui/scenario-list.js +507 -0
- package/dist/components/ui/scenario-list.mjs +100 -0
- package/dist/components/ui/select.mjs +3 -3
- package/dist/components/ui/share-details-dialog.js +636 -0
- package/dist/components/ui/share-details-dialog.mjs +19 -0
- package/dist/components/ui/sheet.mjs +3 -3
- package/dist/components/ui/sidebar-nav.mjs +5 -5
- package/dist/components/ui/signup-form-primitives.js +770 -0
- package/dist/components/ui/signup-form-primitives.mjs +25 -0
- package/dist/components/ui/signup-shell.js +338 -0
- package/dist/components/ui/signup-shell.mjs +13 -0
- package/dist/components/ui/stage-timeline.js +103 -33
- package/dist/components/ui/stage-timeline.mjs +5 -4
- package/dist/components/ui/submission-confirmation-card.js +284 -0
- package/dist/components/ui/submission-confirmation-card.mjs +62 -0
- package/dist/components/ui/tooltip.mjs +3 -3
- package/dist/components/ui/top-three-product.js +374 -0
- package/dist/components/ui/top-three-product.mjs +129 -0
- package/dist/components/ui/transactions-expense-categories-doughnut-chart.mjs +4 -4
- package/dist/components/ui/transactions-income-expense-bar-chart.mjs +5 -5
- package/dist/components/ui/transactions-liabilities-breakdown-doughnut-chart.mjs +4 -4
- package/dist/components/ui/transactions-summary-block.js +95 -0
- package/dist/components/ui/transactions-summary-block.mjs +34 -0
- package/dist/components/ui/two-fa-setup-form.mjs +3 -3
- package/dist/index.js +9430 -4573
- package/dist/index.mjs +404 -251
- package/dist/lib/colors.js +6 -0
- package/dist/lib/colors.mjs +3 -1
- package/dist/lib/theme-provider.mjs +2 -2
- package/dist/styles.css +1 -1
- package/package.json +366 -2
- package/src/components/index.tsx +223 -0
- package/src/components/ui/about-you-form.tsx +397 -0
- package/src/components/ui/account-list-carousel.tsx +87 -0
- package/src/components/ui/add-lead-modal.tsx +1 -1
- package/src/components/ui/agent-evaluation-toast.tsx +191 -0
- package/src/components/ui/ai-builder.tsx +3 -5
- package/src/components/ui/ai-conversations.tsx +1 -1
- package/src/components/ui/applicant-document-checklist.tsx +175 -0
- package/src/components/ui/applicant-expenses-section.tsx +260 -0
- package/src/components/ui/applicant-navigation-bar.tsx +104 -0
- package/src/components/ui/applicant-switcher.tsx +54 -0
- package/src/components/ui/application-mobile-layout.tsx +34 -0
- package/src/components/ui/appointment-availability-settings.tsx +115 -23
- package/src/components/ui/appointment-book-dialog.tsx +406 -259
- package/src/components/ui/appointment-calendar-view.tsx +4 -1
- package/src/components/ui/appointment-detail-sheet.tsx +59 -24
- package/src/components/ui/appointment-time-slot-picker.tsx +5 -1
- package/src/components/ui/asset-accordion.tsx +241 -0
- package/src/components/ui/assets-liabilities-side-card.tsx +157 -0
- package/src/components/ui/auth-page-layout.tsx +3 -3
- package/src/components/ui/backoffice-alert-matching-chart.tsx +320 -0
- package/src/components/ui/backoffice-contact-matching-chart.tsx +341 -0
- package/src/components/ui/backoffice-signup-steps.tsx +615 -0
- package/src/components/ui/bank-statement-document-table.tsx +222 -0
- package/src/components/ui/bank-statement-generate-dialog.tsx +435 -0
- package/src/components/ui/bank-statement-pdf-viewer.tsx +241 -0
- package/src/components/ui/banking-accounts-connect.tsx +187 -0
- package/src/components/ui/borrowing-capacity-atoms.tsx +228 -0
- package/src/components/ui/borrowing-capacity-card.tsx +155 -0
- package/src/components/ui/broker-info-panel.tsx +94 -0
- package/src/components/ui/calculator-input-item.tsx +49 -0
- package/src/components/ui/calculator-section.tsx +275 -0
- package/src/components/ui/category-edit-dialog.tsx +300 -0
- package/src/components/ui/connect-bank-step.tsx +379 -0
- package/src/components/ui/contact-alert-dialog.tsx +710 -0
- package/src/components/ui/create-contact-modal.tsx +237 -0
- package/src/components/ui/csv-import-modal.tsx +153 -0
- package/src/components/ui/currency-input.tsx +104 -0
- package/src/components/ui/dashboard-expense-categories.tsx +262 -0
- package/src/components/ui/dashboard-transactions-table.tsx +241 -0
- package/src/components/ui/debt-accordion.tsx +254 -0
- package/src/components/ui/delete-contact-component.tsx +87 -0
- package/src/components/ui/dialog.tsx +2 -2
- package/src/components/ui/dynamic-tabs.tsx +149 -0
- package/src/components/ui/editable-money-item.tsx +176 -0
- package/src/components/ui/expense-detail-item.tsx +216 -0
- package/src/components/ui/expense-work-details.tsx +229 -0
- package/src/components/ui/file-preview-dialog.tsx +292 -0
- package/src/components/ui/financial-drawers.tsx +1 -1
- package/src/components/ui/frontend-signup-steps.tsx +548 -0
- package/src/components/ui/income-sources-card.tsx +143 -0
- package/src/components/ui/income-summary-component.tsx +120 -0
- package/src/components/ui/income-work-details.tsx +429 -0
- package/src/components/ui/incoming-outgoings-card.tsx +111 -0
- package/src/components/ui/interest-rate-input.tsx +111 -0
- package/src/components/ui/interest-rate-section.tsx +158 -0
- package/src/components/ui/interest-rate-used.tsx +82 -0
- package/src/components/ui/kanban-column.tsx +53 -2
- package/src/components/ui/loan-applicant-information.tsx +64 -0
- package/src/components/ui/loan-applicant-invite.tsx +67 -0
- package/src/components/ui/loan-application-badge.tsx +70 -0
- package/src/components/ui/loan-application-cards.tsx +152 -0
- package/src/components/ui/loan-entry-shell.tsx +86 -0
- package/src/components/ui/loan-financials.tsx +77 -0
- package/src/components/ui/loan-option-card.tsx +62 -0
- package/src/components/ui/loan-option-group.tsx +106 -0
- package/src/components/ui/loan-steps.tsx +630 -0
- package/src/components/ui/loan-wizard-shell.tsx +235 -0
- package/src/components/ui/money-input-with-slider.tsx +77 -0
- package/src/components/ui/money-item-with-color-indicator.tsx +30 -0
- package/src/components/ui/opportunity-card.tsx +46 -18
- package/src/components/ui/pipeline-board.tsx +13 -0
- package/src/components/ui/pipeline-primitives.tsx +28 -0
- package/src/components/ui/property-asset-card.tsx +221 -0
- package/src/components/ui/property-list-carousel.tsx +73 -0
- package/src/components/ui/property-report-dialog.tsx +355 -0
- package/src/components/ui/resource-center.tsx +539 -0
- package/src/components/ui/review-alerts-dialog.tsx +163 -0
- package/src/components/ui/savings-goal-modal.tsx +169 -0
- package/src/components/ui/scenario-drawer.tsx +358 -0
- package/src/components/ui/scenario-item.tsx +141 -0
- package/src/components/ui/scenario-list.tsx +150 -0
- package/src/components/ui/share-details-dialog.tsx +238 -0
- package/src/components/ui/signup-form-primitives.tsx +212 -0
- package/src/components/ui/signup-shell.tsx +180 -0
- package/src/components/ui/stage-timeline.tsx +11 -0
- package/src/components/ui/submission-confirmation-card.tsx +68 -0
- package/src/components/ui/top-three-product.tsx +207 -0
- package/src/components/ui/transactions-summary-block.tsx +59 -0
- package/src/lib/colors.ts +12 -0
- package/src/lib/format-date.ts +2 -2
- package/src/styles/styles-css.ts +1 -1
- package/tsup.config.ts +77 -1
- package/dist/{chunk-5VOTTIXF.mjs → chunk-FRT3S72S.mjs} +3 -3
- package/dist/{chunk-7BTFGCFC.mjs → chunk-MUV4EGDW.mjs} +3 -3
- package/dist/{chunk-57ZXILTS.mjs → chunk-MXP2RX2V.mjs} +3 -3
- package/dist/{chunk-ZKWXDQDG.mjs → chunk-VCDGLN25.mjs} +3 -3
- package/dist/{chunk-FLL633WS.mjs → chunk-ZXEUBBHJ.mjs} +3 -3
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import { differenceInDays } from "date-fns";
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
|
+
import { formatCurrency } from "@/lib/format-currency";
|
|
5
|
+
import {
|
|
6
|
+
Dialog,
|
|
7
|
+
DialogContent,
|
|
8
|
+
DialogHeader,
|
|
9
|
+
DialogTitle,
|
|
10
|
+
DialogFooter,
|
|
11
|
+
} from "./dialog";
|
|
12
|
+
import { Button } from "./button";
|
|
13
|
+
import { DatePicker } from "./date-picker";
|
|
14
|
+
import { ToggleGroup, ToggleGroupItem } from "./toggle-group";
|
|
15
|
+
import { MoneyInputWithSlider } from "./money-input-with-slider";
|
|
16
|
+
|
|
17
|
+
type GoalPeriod = "90d" | "180d" | "360d" | "custom";
|
|
18
|
+
|
|
19
|
+
const PERIOD_OPTIONS: { id: GoalPeriod; label: string; days: number }[] = [
|
|
20
|
+
{ id: "90d", label: "90 Days", days: 90 },
|
|
21
|
+
{ id: "180d", label: "180 Days", days: 180 },
|
|
22
|
+
{ id: "360d", label: "360 Days", days: 360 },
|
|
23
|
+
{ id: "custom", label: "Custom", days: 0 },
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
const PERIOD_DAYS: Partial<Record<GoalPeriod, number>> = Object.fromEntries(
|
|
27
|
+
PERIOD_OPTIONS.map((p) => [p.id, p.days]),
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
function SummaryRow({ label, value }: { label: string; value: number }) {
|
|
31
|
+
return (
|
|
32
|
+
<div className="flex items-center justify-between">
|
|
33
|
+
<span className="text-sm text-foreground">{label}</span>
|
|
34
|
+
<span className="text-sm font-semibold tabular-nums text-foreground">
|
|
35
|
+
{formatCurrency(value)}
|
|
36
|
+
</span>
|
|
37
|
+
</div>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface SavingsGoalModalProps {
|
|
42
|
+
open: boolean;
|
|
43
|
+
onOpenChange: (open: boolean) => void;
|
|
44
|
+
/** Current monthly savings trend (pre-calculated) in dollars */
|
|
45
|
+
currentMonthlyTrend?: number;
|
|
46
|
+
/** Initial goal amount in dollars */
|
|
47
|
+
defaultGoalAmount?: number;
|
|
48
|
+
/** Called when user confirms the goal */
|
|
49
|
+
onSave?: (goalAmount: number, periodDays: number) => void;
|
|
50
|
+
className?: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function SavingsGoalModal({
|
|
54
|
+
open,
|
|
55
|
+
onOpenChange,
|
|
56
|
+
currentMonthlyTrend = 0,
|
|
57
|
+
defaultGoalAmount = 0,
|
|
58
|
+
onSave,
|
|
59
|
+
className,
|
|
60
|
+
}: SavingsGoalModalProps) {
|
|
61
|
+
const [goalAmount, setGoalAmount] = useState(defaultGoalAmount);
|
|
62
|
+
const [selectedPeriod, setSelectedPeriod] = useState<GoalPeriod>("90d");
|
|
63
|
+
const [customStart, setCustomStart] = useState<Date | undefined>(undefined);
|
|
64
|
+
const [customEnd, setCustomEnd] = useState<Date | undefined>(undefined);
|
|
65
|
+
|
|
66
|
+
const isCustom = selectedPeriod === "custom";
|
|
67
|
+
const today = new Date();
|
|
68
|
+
|
|
69
|
+
const periodDays = isCustom
|
|
70
|
+
? customStart && customEnd && customEnd > customStart
|
|
71
|
+
? differenceInDays(customEnd, customStart)
|
|
72
|
+
: 0
|
|
73
|
+
: (PERIOD_DAYS[selectedPeriod] ?? 90);
|
|
74
|
+
|
|
75
|
+
const monthlyTarget =
|
|
76
|
+
periodDays > 0 ? Math.round((goalAmount / periodDays) * 30) : 0;
|
|
77
|
+
|
|
78
|
+
const canSave = goalAmount > 0 && (!isCustom || periodDays > 0);
|
|
79
|
+
|
|
80
|
+
const handleSave = () => {
|
|
81
|
+
onSave?.(goalAmount, periodDays);
|
|
82
|
+
onOpenChange(false);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
87
|
+
<DialogContent className={cn("max-w-sm top-8 translate-y-0", className)}>
|
|
88
|
+
<DialogHeader>
|
|
89
|
+
<DialogTitle>Savings Target Setter</DialogTitle>
|
|
90
|
+
</DialogHeader>
|
|
91
|
+
|
|
92
|
+
<div className="flex flex-col gap-6 py-2">
|
|
93
|
+
<MoneyInputWithSlider
|
|
94
|
+
label="Savings Goal"
|
|
95
|
+
value={goalAmount}
|
|
96
|
+
min={0}
|
|
97
|
+
max={200_000}
|
|
98
|
+
step={500}
|
|
99
|
+
onChange={setGoalAmount}
|
|
100
|
+
/>
|
|
101
|
+
|
|
102
|
+
<div className="flex flex-col gap-3">
|
|
103
|
+
<p className="text-sm font-medium text-foreground">End Goal Date</p>
|
|
104
|
+
<ToggleGroup
|
|
105
|
+
type="single"
|
|
106
|
+
variant="outline"
|
|
107
|
+
size="sm"
|
|
108
|
+
className="w-full"
|
|
109
|
+
value={[selectedPeriod]}
|
|
110
|
+
onValueChange={(values) => {
|
|
111
|
+
const next = values[0] as GoalPeriod | undefined;
|
|
112
|
+
if (next) setSelectedPeriod(next);
|
|
113
|
+
}}
|
|
114
|
+
>
|
|
115
|
+
{PERIOD_OPTIONS.map((option) => (
|
|
116
|
+
<ToggleGroupItem
|
|
117
|
+
key={option.id}
|
|
118
|
+
value={option.id}
|
|
119
|
+
className="flex-1"
|
|
120
|
+
>
|
|
121
|
+
{option.label}
|
|
122
|
+
</ToggleGroupItem>
|
|
123
|
+
))}
|
|
124
|
+
</ToggleGroup>
|
|
125
|
+
|
|
126
|
+
{isCustom && (
|
|
127
|
+
<div className="grid grid-cols-2 gap-2">
|
|
128
|
+
<div className="flex flex-col gap-1">
|
|
129
|
+
<p className="text-xs text-muted-foreground">Start Date</p>
|
|
130
|
+
<DatePicker
|
|
131
|
+
value={customStart}
|
|
132
|
+
onChange={setCustomStart}
|
|
133
|
+
placeholder="Start date"
|
|
134
|
+
calendarProps={{ disabled: { before: today } }}
|
|
135
|
+
/>
|
|
136
|
+
</div>
|
|
137
|
+
<div className="flex flex-col gap-1">
|
|
138
|
+
<p className="text-xs text-muted-foreground">End Date</p>
|
|
139
|
+
<DatePicker
|
|
140
|
+
value={customEnd}
|
|
141
|
+
onChange={setCustomEnd}
|
|
142
|
+
placeholder="End date"
|
|
143
|
+
calendarProps={{
|
|
144
|
+
disabled: { before: customStart ?? today },
|
|
145
|
+
}}
|
|
146
|
+
/>
|
|
147
|
+
</div>
|
|
148
|
+
</div>
|
|
149
|
+
)}
|
|
150
|
+
</div>
|
|
151
|
+
|
|
152
|
+
<div className="flex flex-col gap-3 border-t border-border pt-4">
|
|
153
|
+
<SummaryRow label="Monthly Savings Target" value={monthlyTarget} />
|
|
154
|
+
<SummaryRow label="Current Trend" value={currentMonthlyTrend} />
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
|
|
158
|
+
<DialogFooter>
|
|
159
|
+
<Button variant="ghost" onClick={() => onOpenChange(false)}>
|
|
160
|
+
Cancel
|
|
161
|
+
</Button>
|
|
162
|
+
<Button onClick={handleSave} disabled={!canSave}>
|
|
163
|
+
Set Now
|
|
164
|
+
</Button>
|
|
165
|
+
</DialogFooter>
|
|
166
|
+
</DialogContent>
|
|
167
|
+
</Dialog>
|
|
168
|
+
);
|
|
169
|
+
}
|
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
import React, { useEffect, useState } from "react";
|
|
2
|
+
import { Pencil, X } from "lucide-react";
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
|
+
import { Button } from "./button";
|
|
5
|
+
import { Input } from "./input";
|
|
6
|
+
import { MoneyInputWithSlider } from "./money-input-with-slider";
|
|
7
|
+
import { Toggle } from "./toggle";
|
|
8
|
+
import { Sheet, SheetContent, SheetTitle } from "./sheet";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Mobile bottom-sheet organism for creating or editing a borrowing scenario.
|
|
12
|
+
*
|
|
13
|
+
* Sections (top-to-bottom):
|
|
14
|
+
* - Header: editable scenario name + close button
|
|
15
|
+
* - Content (scrollable):
|
|
16
|
+
* - Objective selector (Buy a Home / Buy an Investment / Refinance)
|
|
17
|
+
* - Applicants selector (1 / 2)
|
|
18
|
+
* - Dependants selector (0 – 3)
|
|
19
|
+
* - MoneyInputWithSlider fields (income, expenses, debt, desired loan)
|
|
20
|
+
* - Footer: Cancel + Create/Update Scenario buttons
|
|
21
|
+
*
|
|
22
|
+
* On mobile the sheet fills the viewport width. On larger screens it is
|
|
23
|
+
* capped at max-w-2xl and the slider fields flow in a 2-column grid so
|
|
24
|
+
* the drawer does not stretch the page layout.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
// ─── Types ────────────────────────────────────────────────────────────────────
|
|
28
|
+
|
|
29
|
+
export type ScenarioObjective = "buy-home" | "buy-investment" | "refinance";
|
|
30
|
+
|
|
31
|
+
export interface ScenarioDrawerValues {
|
|
32
|
+
name: string;
|
|
33
|
+
objective: ScenarioObjective;
|
|
34
|
+
/** 1 = sole applicant, 2 = two applicants */
|
|
35
|
+
applicants: 1 | 2;
|
|
36
|
+
/** Number of dependants (0–3) */
|
|
37
|
+
dependants: number;
|
|
38
|
+
/** Main applicant after-tax monthly income in dollars */
|
|
39
|
+
mainApplicantMonthlyIncome: number;
|
|
40
|
+
/** Main applicant monthly rental income in dollars */
|
|
41
|
+
mainApplicantMonthlyRentalIncome: number;
|
|
42
|
+
/** Co-applicant after-tax monthly income in dollars (applicants = 2 only) */
|
|
43
|
+
coApplicantMonthlyIncome: number;
|
|
44
|
+
/** Co-applicant monthly rental income in dollars (applicants = 2 only) */
|
|
45
|
+
coApplicantMonthlyRentalIncome: number;
|
|
46
|
+
/** Combined essential expenses per month in dollars */
|
|
47
|
+
monthlyExpense: number;
|
|
48
|
+
/** Combined debt repayments per month in dollars */
|
|
49
|
+
monthlyDebtRepayment: number;
|
|
50
|
+
/** Estimated rental income (buy-investment objective only) */
|
|
51
|
+
estimatedRentalIncome: number;
|
|
52
|
+
/** Target loan amount in dollars */
|
|
53
|
+
desiredLoanAmount: number;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface ScenarioDrawerProps {
|
|
57
|
+
open: boolean;
|
|
58
|
+
onOpenChange: (open: boolean) => void;
|
|
59
|
+
/** Initial field values — re-applied whenever the drawer opens */
|
|
60
|
+
defaultValues?: Partial<ScenarioDrawerValues>;
|
|
61
|
+
/** true = show "Update Scenario" footer button, false = "Create Scenario" */
|
|
62
|
+
isEditMode?: boolean;
|
|
63
|
+
/** Called with the final values when the user clicks Save */
|
|
64
|
+
onSave: (values: ScenarioDrawerValues) => void;
|
|
65
|
+
/** Called when the user clicks Cancel or the close button */
|
|
66
|
+
onCancel?: () => void;
|
|
67
|
+
className?: string;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ─── Constants ────────────────────────────────────────────────────────────────
|
|
71
|
+
|
|
72
|
+
const DEFAULT_VALUES: ScenarioDrawerValues = {
|
|
73
|
+
name: "New Scenario",
|
|
74
|
+
objective: "buy-home",
|
|
75
|
+
applicants: 1,
|
|
76
|
+
dependants: 0,
|
|
77
|
+
mainApplicantMonthlyIncome: 0,
|
|
78
|
+
mainApplicantMonthlyRentalIncome: 0,
|
|
79
|
+
coApplicantMonthlyIncome: 0,
|
|
80
|
+
coApplicantMonthlyRentalIncome: 0,
|
|
81
|
+
monthlyExpense: 0,
|
|
82
|
+
monthlyDebtRepayment: 0,
|
|
83
|
+
estimatedRentalIncome: 0,
|
|
84
|
+
desiredLoanAmount: 750_000,
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const OBJECTIVE_OPTIONS: { value: ScenarioObjective; label: string }[] = [
|
|
88
|
+
{ value: "buy-home", label: "Buy a Home or Move" },
|
|
89
|
+
{ value: "buy-investment", label: "Buy an Investment" },
|
|
90
|
+
{ value: "refinance", label: "Refinance My Loan" },
|
|
91
|
+
];
|
|
92
|
+
|
|
93
|
+
const APPLICANT_OPTIONS: { value: 1 | 2; label: string }[] = [
|
|
94
|
+
{ value: 1, label: "Just Me" },
|
|
95
|
+
{ value: 2, label: "Me + One" },
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
const DEPENDANT_OPTIONS: { value: number; label: string }[] = [
|
|
99
|
+
{ value: 0, label: "None" },
|
|
100
|
+
{ value: 1, label: "1" },
|
|
101
|
+
{ value: 2, label: "2" },
|
|
102
|
+
{ value: 3, label: "3" },
|
|
103
|
+
];
|
|
104
|
+
|
|
105
|
+
// ─── Internal sub-components ──────────────────────────────────────────────────
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Horizontally connected row of Toggle buttons acting as a single-select group.
|
|
109
|
+
* Uses the WealthX `Toggle` variant="outline" so the pressed state renders
|
|
110
|
+
* with `bg-primary/10 + inset-ring-primary` per the DS token.
|
|
111
|
+
*/
|
|
112
|
+
function OptionGroup<T extends string | number>({
|
|
113
|
+
label,
|
|
114
|
+
options,
|
|
115
|
+
value,
|
|
116
|
+
onChange,
|
|
117
|
+
}: {
|
|
118
|
+
label: string;
|
|
119
|
+
options: { value: T; label: string }[];
|
|
120
|
+
value: T;
|
|
121
|
+
onChange: (v: T) => void;
|
|
122
|
+
}) {
|
|
123
|
+
return (
|
|
124
|
+
<div className="flex flex-col gap-2">
|
|
125
|
+
<span className="text-sm font-medium text-foreground">{label}</span>
|
|
126
|
+
<div className="flex">
|
|
127
|
+
{options.map((opt, idx) => (
|
|
128
|
+
<Toggle
|
|
129
|
+
key={String(opt.value)}
|
|
130
|
+
variant="outline"
|
|
131
|
+
pressed={opt.value === value}
|
|
132
|
+
onPressedChange={(on) => {
|
|
133
|
+
if (on) onChange(opt.value);
|
|
134
|
+
}}
|
|
135
|
+
className={cn(
|
|
136
|
+
"flex-1 px-3 py-2 text-sm",
|
|
137
|
+
// Connect buttons: collapse shared borders
|
|
138
|
+
idx > 0 && "border-l-0",
|
|
139
|
+
)}
|
|
140
|
+
>
|
|
141
|
+
{opt.label}
|
|
142
|
+
</Toggle>
|
|
143
|
+
))}
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ─── Main component ───────────────────────────────────────────────────────────
|
|
150
|
+
|
|
151
|
+
export function ScenarioDrawer({
|
|
152
|
+
open,
|
|
153
|
+
onOpenChange,
|
|
154
|
+
defaultValues,
|
|
155
|
+
isEditMode = false,
|
|
156
|
+
onSave,
|
|
157
|
+
onCancel,
|
|
158
|
+
className,
|
|
159
|
+
}: ScenarioDrawerProps) {
|
|
160
|
+
const [values, setValues] = useState<ScenarioDrawerValues>({
|
|
161
|
+
...DEFAULT_VALUES,
|
|
162
|
+
...defaultValues,
|
|
163
|
+
});
|
|
164
|
+
const [editingName, setEditingName] = useState(false);
|
|
165
|
+
|
|
166
|
+
// Re-seed internal state whenever the drawer opens with new defaultValues
|
|
167
|
+
useEffect(() => {
|
|
168
|
+
if (open) {
|
|
169
|
+
setValues({ ...DEFAULT_VALUES, ...defaultValues });
|
|
170
|
+
}
|
|
171
|
+
}, [open]); // intentional: only react to open changing, not defaultValues
|
|
172
|
+
|
|
173
|
+
const set = <K extends keyof ScenarioDrawerValues>(
|
|
174
|
+
key: K,
|
|
175
|
+
value: ScenarioDrawerValues[K],
|
|
176
|
+
) => setValues((prev) => ({ ...prev, [key]: value }));
|
|
177
|
+
|
|
178
|
+
const handleCancel = () => {
|
|
179
|
+
onCancel?.();
|
|
180
|
+
onOpenChange(false);
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const handleSave = () => {
|
|
184
|
+
onSave(values);
|
|
185
|
+
onOpenChange(false);
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
const isTwoApplicants = values.applicants === 2;
|
|
189
|
+
const isInvestment = values.objective === "buy-investment";
|
|
190
|
+
const expenseLabel = isTwoApplicants ? "Combined" : "Estimated";
|
|
191
|
+
|
|
192
|
+
return (
|
|
193
|
+
<Sheet open={open} onOpenChange={onOpenChange}>
|
|
194
|
+
{/*
|
|
195
|
+
* showCloseButton={false}: disable the Sheet's built-in absolute close
|
|
196
|
+
* button — the drawer renders its own X in the header row.
|
|
197
|
+
*/}
|
|
198
|
+
<SheetContent
|
|
199
|
+
side="bottom"
|
|
200
|
+
showCloseButton={false}
|
|
201
|
+
className={cn("flex max-h-[90dvh] flex-col gap-0 p-0", className)}
|
|
202
|
+
>
|
|
203
|
+
{/* ── Header ─────────────────────────────────────────────────────── */}
|
|
204
|
+
<div className="flex items-center justify-between border-b border-border px-4 py-3">
|
|
205
|
+
<SheetTitle className="sr-only">
|
|
206
|
+
{isEditMode ? "Edit Scenario" : "Create Scenario"}
|
|
207
|
+
</SheetTitle>
|
|
208
|
+
|
|
209
|
+
{editingName ? (
|
|
210
|
+
<Input
|
|
211
|
+
autoFocus
|
|
212
|
+
value={values.name}
|
|
213
|
+
onChange={(e) => set("name", e.target.value)}
|
|
214
|
+
onBlur={() => setEditingName(false)}
|
|
215
|
+
onKeyDown={(e) => e.key === "Enter" && setEditingName(false)}
|
|
216
|
+
className="h-8 text-base font-semibold"
|
|
217
|
+
/>
|
|
218
|
+
) : (
|
|
219
|
+
<button
|
|
220
|
+
type="button"
|
|
221
|
+
onClick={() => setEditingName(true)}
|
|
222
|
+
className="flex items-center gap-2 text-base font-semibold text-foreground hover:opacity-70"
|
|
223
|
+
>
|
|
224
|
+
{values.name}
|
|
225
|
+
<Pencil size={14} className="shrink-0 text-muted-foreground" />
|
|
226
|
+
</button>
|
|
227
|
+
)}
|
|
228
|
+
|
|
229
|
+
<button
|
|
230
|
+
type="button"
|
|
231
|
+
onClick={handleCancel}
|
|
232
|
+
aria-label="Close"
|
|
233
|
+
className="ml-3 shrink-0 p-1 text-muted-foreground transition-colors hover:text-foreground"
|
|
234
|
+
>
|
|
235
|
+
<X size={18} />
|
|
236
|
+
</button>
|
|
237
|
+
</div>
|
|
238
|
+
|
|
239
|
+
{/* ── Scrollable content ──────────────────────────────────────────── */}
|
|
240
|
+
<div className="flex-1 overflow-y-auto px-4 py-4">
|
|
241
|
+
<div className="flex flex-col gap-6">
|
|
242
|
+
{/* Selectors */}
|
|
243
|
+
<OptionGroup
|
|
244
|
+
label="Objective"
|
|
245
|
+
options={OBJECTIVE_OPTIONS}
|
|
246
|
+
value={values.objective}
|
|
247
|
+
onChange={(v) => set("objective", v)}
|
|
248
|
+
/>
|
|
249
|
+
<OptionGroup
|
|
250
|
+
label="Applicants"
|
|
251
|
+
options={APPLICANT_OPTIONS}
|
|
252
|
+
value={values.applicants}
|
|
253
|
+
onChange={(v) => set("applicants", v)}
|
|
254
|
+
/>
|
|
255
|
+
<OptionGroup
|
|
256
|
+
label="Dependants"
|
|
257
|
+
options={DEPENDANT_OPTIONS}
|
|
258
|
+
value={values.dependants}
|
|
259
|
+
onChange={(v) => set("dependants", v)}
|
|
260
|
+
/>
|
|
261
|
+
|
|
262
|
+
{/* Income sliders — 2-col grid on md+ */}
|
|
263
|
+
<div className="grid grid-cols-1 gap-5 md:grid-cols-2">
|
|
264
|
+
<MoneyInputWithSlider
|
|
265
|
+
label="Main Applicant Monthly Income"
|
|
266
|
+
value={values.mainApplicantMonthlyIncome}
|
|
267
|
+
min={0}
|
|
268
|
+
max={30_000}
|
|
269
|
+
step={100}
|
|
270
|
+
onChange={(v) => set("mainApplicantMonthlyIncome", v)}
|
|
271
|
+
/>
|
|
272
|
+
<MoneyInputWithSlider
|
|
273
|
+
label="Main Applicant Rental Income"
|
|
274
|
+
value={values.mainApplicantMonthlyRentalIncome}
|
|
275
|
+
min={0}
|
|
276
|
+
max={30_000}
|
|
277
|
+
step={100}
|
|
278
|
+
onChange={(v) => set("mainApplicantMonthlyRentalIncome", v)}
|
|
279
|
+
/>
|
|
280
|
+
{isTwoApplicants && (
|
|
281
|
+
<>
|
|
282
|
+
<MoneyInputWithSlider
|
|
283
|
+
label="Co-Applicant Monthly Income"
|
|
284
|
+
value={values.coApplicantMonthlyIncome}
|
|
285
|
+
min={0}
|
|
286
|
+
max={30_000}
|
|
287
|
+
step={100}
|
|
288
|
+
onChange={(v) => set("coApplicantMonthlyIncome", v)}
|
|
289
|
+
/>
|
|
290
|
+
<MoneyInputWithSlider
|
|
291
|
+
label="Co-Applicant Rental Income"
|
|
292
|
+
value={values.coApplicantMonthlyRentalIncome}
|
|
293
|
+
min={0}
|
|
294
|
+
max={30_000}
|
|
295
|
+
step={100}
|
|
296
|
+
onChange={(v) => set("coApplicantMonthlyRentalIncome", v)}
|
|
297
|
+
/>
|
|
298
|
+
</>
|
|
299
|
+
)}
|
|
300
|
+
</div>
|
|
301
|
+
|
|
302
|
+
{/* Expense sliders — 2-col grid on md+ */}
|
|
303
|
+
<div className="grid grid-cols-1 gap-5 md:grid-cols-2">
|
|
304
|
+
<MoneyInputWithSlider
|
|
305
|
+
label={`${expenseLabel} Essential Expenses PM`}
|
|
306
|
+
value={values.monthlyExpense}
|
|
307
|
+
min={0}
|
|
308
|
+
max={15_000}
|
|
309
|
+
step={100}
|
|
310
|
+
onChange={(v) => set("monthlyExpense", v)}
|
|
311
|
+
/>
|
|
312
|
+
<MoneyInputWithSlider
|
|
313
|
+
label={`${expenseLabel} Debt Repayment PM`}
|
|
314
|
+
value={values.monthlyDebtRepayment}
|
|
315
|
+
min={0}
|
|
316
|
+
max={15_000}
|
|
317
|
+
step={100}
|
|
318
|
+
onChange={(v) => set("monthlyDebtRepayment", v)}
|
|
319
|
+
/>
|
|
320
|
+
{isInvestment && (
|
|
321
|
+
<MoneyInputWithSlider
|
|
322
|
+
label="Estimated Rental Income PM"
|
|
323
|
+
value={values.estimatedRentalIncome}
|
|
324
|
+
min={0}
|
|
325
|
+
max={30_000}
|
|
326
|
+
step={100}
|
|
327
|
+
onChange={(v) => set("estimatedRentalIncome", v)}
|
|
328
|
+
/>
|
|
329
|
+
)}
|
|
330
|
+
</div>
|
|
331
|
+
|
|
332
|
+
{/* Desired Loan Amount */}
|
|
333
|
+
<div className="border-t border-border pt-4">
|
|
334
|
+
<MoneyInputWithSlider
|
|
335
|
+
label="Desired Loan Amount"
|
|
336
|
+
value={values.desiredLoanAmount}
|
|
337
|
+
min={50_000}
|
|
338
|
+
max={2_000_000}
|
|
339
|
+
step={5_000}
|
|
340
|
+
onChange={(v) => set("desiredLoanAmount", v)}
|
|
341
|
+
/>
|
|
342
|
+
</div>
|
|
343
|
+
</div>
|
|
344
|
+
</div>
|
|
345
|
+
|
|
346
|
+
{/* ── Footer ──────────────────────────────────────────────────────── */}
|
|
347
|
+
<div className="flex gap-3 border-t border-border px-4 py-3">
|
|
348
|
+
<Button variant="outline" className="flex-1" onClick={handleCancel}>
|
|
349
|
+
Cancel
|
|
350
|
+
</Button>
|
|
351
|
+
<Button className="flex-1" onClick={handleSave}>
|
|
352
|
+
{isEditMode ? "Update Scenario" : "Create Scenario"}
|
|
353
|
+
</Button>
|
|
354
|
+
</div>
|
|
355
|
+
</SheetContent>
|
|
356
|
+
</Sheet>
|
|
357
|
+
);
|
|
358
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { GripVertical, MoreVertical } from "lucide-react";
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
|
+
import {
|
|
5
|
+
DropdownMenu,
|
|
6
|
+
DropdownMenuContent,
|
|
7
|
+
DropdownMenuItem,
|
|
8
|
+
DropdownMenuTrigger,
|
|
9
|
+
} from "./dropdown-menu";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A selectable scenario card used in the Borrowing Capacity left sidebar.
|
|
13
|
+
*
|
|
14
|
+
* Displays a scenario name and description. Supports selected state (right
|
|
15
|
+
* border accent), an optional drag handle for reordering, and an optional
|
|
16
|
+
* kebab menu with "Update Scenario" and "Delete Scenario" options.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
export interface ScenarioItemProps {
|
|
20
|
+
/** Scenario display name (e.g. "Default Scenario") */
|
|
21
|
+
name: string;
|
|
22
|
+
/** Secondary descriptor (e.g. "Owner Occupier") */
|
|
23
|
+
description?: string;
|
|
24
|
+
/** Highlights the item with a left border accent */
|
|
25
|
+
isSelected?: boolean;
|
|
26
|
+
/** Shows the drag grip handle and marks the root as draggable */
|
|
27
|
+
isDraggable?: boolean;
|
|
28
|
+
/** Visual drag-over highlight (consumer-controlled) */
|
|
29
|
+
isDragOver?: boolean;
|
|
30
|
+
/** Click handler — typically selects this scenario */
|
|
31
|
+
onSelect?: () => void;
|
|
32
|
+
/** Called when "Update Scenario" is clicked in the kebab menu */
|
|
33
|
+
onEdit?: () => void;
|
|
34
|
+
/** Called when "Delete Scenario" is clicked in the kebab menu */
|
|
35
|
+
onDelete?: () => void;
|
|
36
|
+
/** HTML5 drag events forwarded from parent (ScenarioList) */
|
|
37
|
+
onDragStart?: (e: React.DragEvent<HTMLDivElement>) => void;
|
|
38
|
+
onDragOver?: (e: React.DragEvent<HTMLDivElement>) => void;
|
|
39
|
+
onDragLeave?: (e: React.DragEvent<HTMLDivElement>) => void;
|
|
40
|
+
onDrop?: (e: React.DragEvent<HTMLDivElement>) => void;
|
|
41
|
+
onDragEnd?: (e: React.DragEvent<HTMLDivElement>) => void;
|
|
42
|
+
className?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function ScenarioItem({
|
|
46
|
+
name,
|
|
47
|
+
description,
|
|
48
|
+
isSelected = false,
|
|
49
|
+
isDraggable = false,
|
|
50
|
+
isDragOver = false,
|
|
51
|
+
onSelect,
|
|
52
|
+
onEdit,
|
|
53
|
+
onDelete,
|
|
54
|
+
onDragStart,
|
|
55
|
+
onDragOver,
|
|
56
|
+
onDragLeave,
|
|
57
|
+
onDrop,
|
|
58
|
+
onDragEnd,
|
|
59
|
+
className,
|
|
60
|
+
}: ScenarioItemProps) {
|
|
61
|
+
return (
|
|
62
|
+
<div
|
|
63
|
+
role="button"
|
|
64
|
+
tabIndex={0}
|
|
65
|
+
draggable={isDraggable}
|
|
66
|
+
onClick={onSelect}
|
|
67
|
+
onKeyDown={(e) => {
|
|
68
|
+
if (e.key === "Enter" || e.key === " ") onSelect?.();
|
|
69
|
+
}}
|
|
70
|
+
onDragStart={onDragStart}
|
|
71
|
+
onDragOver={onDragOver}
|
|
72
|
+
onDragLeave={onDragLeave}
|
|
73
|
+
onDrop={onDrop}
|
|
74
|
+
onDragEnd={onDragEnd}
|
|
75
|
+
className={cn(
|
|
76
|
+
"flex cursor-pointer items-center gap-2 border border-border bg-background px-3 py-3 transition-colors hover:bg-muted/50",
|
|
77
|
+
isSelected && "border-r-2 border-r-primary bg-primary/5",
|
|
78
|
+
isDragOver && "bg-primary/10",
|
|
79
|
+
className,
|
|
80
|
+
)}
|
|
81
|
+
>
|
|
82
|
+
{isDraggable && (
|
|
83
|
+
<span
|
|
84
|
+
aria-hidden
|
|
85
|
+
className="shrink-0 cursor-grab text-muted-foreground active:cursor-grabbing"
|
|
86
|
+
>
|
|
87
|
+
<GripVertical size={16} />
|
|
88
|
+
</span>
|
|
89
|
+
)}
|
|
90
|
+
|
|
91
|
+
<div className="min-w-0 flex-1">
|
|
92
|
+
<p className="truncate text-base font-semibold text-foreground">
|
|
93
|
+
{name}
|
|
94
|
+
</p>
|
|
95
|
+
{description && (
|
|
96
|
+
<p className="mt-0.5 truncate text-sm text-muted-foreground">
|
|
97
|
+
{description}
|
|
98
|
+
</p>
|
|
99
|
+
)}
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
{(onEdit || onDelete) && (
|
|
103
|
+
<DropdownMenu>
|
|
104
|
+
<DropdownMenuTrigger asChild>
|
|
105
|
+
<button
|
|
106
|
+
type="button"
|
|
107
|
+
aria-label="Scenario options"
|
|
108
|
+
onClick={(e) => e.stopPropagation()}
|
|
109
|
+
className="shrink-0 p-1 text-muted-foreground transition-colors hover:text-foreground"
|
|
110
|
+
>
|
|
111
|
+
<MoreVertical size={16} />
|
|
112
|
+
</button>
|
|
113
|
+
</DropdownMenuTrigger>
|
|
114
|
+
<DropdownMenuContent align="end">
|
|
115
|
+
{onEdit && (
|
|
116
|
+
<DropdownMenuItem
|
|
117
|
+
onClick={(e) => {
|
|
118
|
+
e.stopPropagation();
|
|
119
|
+
onEdit();
|
|
120
|
+
}}
|
|
121
|
+
>
|
|
122
|
+
Update Scenario
|
|
123
|
+
</DropdownMenuItem>
|
|
124
|
+
)}
|
|
125
|
+
{onDelete && (
|
|
126
|
+
<DropdownMenuItem
|
|
127
|
+
onClick={(e) => {
|
|
128
|
+
e.stopPropagation();
|
|
129
|
+
onDelete();
|
|
130
|
+
}}
|
|
131
|
+
className="text-destructive focus:text-destructive"
|
|
132
|
+
>
|
|
133
|
+
Delete Scenario
|
|
134
|
+
</DropdownMenuItem>
|
|
135
|
+
)}
|
|
136
|
+
</DropdownMenuContent>
|
|
137
|
+
</DropdownMenu>
|
|
138
|
+
)}
|
|
139
|
+
</div>
|
|
140
|
+
);
|
|
141
|
+
}
|