@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,221 @@
|
|
|
1
|
+
import { Building2 } from "lucide-react";
|
|
2
|
+
import { cn } from "@/lib/utils";
|
|
3
|
+
import { Badge } from "@/components/ui/badge";
|
|
4
|
+
import {
|
|
5
|
+
Accordion,
|
|
6
|
+
AccordionContent,
|
|
7
|
+
AccordionItem,
|
|
8
|
+
AccordionTrigger,
|
|
9
|
+
} from "@/components/ui/accordion";
|
|
10
|
+
import { formatCurrency } from "@/lib/format-currency";
|
|
11
|
+
|
|
12
|
+
// ─── Types ────────────────────────────────────────────────────────────────────
|
|
13
|
+
|
|
14
|
+
export type PropertyMortgage = {
|
|
15
|
+
id: string;
|
|
16
|
+
/** Display label e.g. "Mortgage" */
|
|
17
|
+
label: string;
|
|
18
|
+
/** Loan type shown as a badge e.g. "Home Loan", "Investment Loan" */
|
|
19
|
+
type: string;
|
|
20
|
+
/** Monthly repayment shown in the accordion header */
|
|
21
|
+
monthlyRepayment: number;
|
|
22
|
+
/** Outstanding loan balance */
|
|
23
|
+
loanAmount: number;
|
|
24
|
+
/** Monthly repayment shown in Loan Details */
|
|
25
|
+
repaymentAmount: number;
|
|
26
|
+
/** Annual interest rate (%) */
|
|
27
|
+
interestRate: number;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export type PropertyAssetCardProps = {
|
|
31
|
+
/** Full property address */
|
|
32
|
+
address: string;
|
|
33
|
+
/** Asset category label shown below the address */
|
|
34
|
+
assetType?: string;
|
|
35
|
+
/** URL for the lending institution's logo — falls back to a building icon */
|
|
36
|
+
institutionLogoUrl?: string;
|
|
37
|
+
/** Current estimated market value */
|
|
38
|
+
estimatedValue: number;
|
|
39
|
+
/** Loan-to-value ratio (0–100) */
|
|
40
|
+
lvr?: number;
|
|
41
|
+
/** Net equity in dollars (estimatedValue − totalOwing) */
|
|
42
|
+
netEquity?: number;
|
|
43
|
+
/** Linked mortgage records */
|
|
44
|
+
mortgages?: PropertyMortgage[];
|
|
45
|
+
className?: string;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// ─── Constants ────────────────────────────────────────────────────────────────
|
|
49
|
+
|
|
50
|
+
const EMPTY_MORTGAGES: PropertyMortgage[] = [];
|
|
51
|
+
|
|
52
|
+
// ─── Component ────────────────────────────────────────────────────────────────
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* PropertyAssetCard — summary card for a single property asset in the loan
|
|
56
|
+
* wizard Assets tab.
|
|
57
|
+
*
|
|
58
|
+
* Shows: institution logo, address, estimated value, interest rate + LVR stats,
|
|
59
|
+
* net equity highlight, and an expandable mortgage accordion with Loan Details
|
|
60
|
+
* and Property Statistics sub-sections.
|
|
61
|
+
*
|
|
62
|
+
* Figma: WealthX-Backoffice---Mobile-App — node 17418:41358
|
|
63
|
+
*/
|
|
64
|
+
export function PropertyAssetCard({
|
|
65
|
+
address,
|
|
66
|
+
assetType = "Property",
|
|
67
|
+
institutionLogoUrl,
|
|
68
|
+
estimatedValue,
|
|
69
|
+
lvr,
|
|
70
|
+
netEquity,
|
|
71
|
+
mortgages = EMPTY_MORTGAGES,
|
|
72
|
+
className,
|
|
73
|
+
}: PropertyAssetCardProps) {
|
|
74
|
+
const primaryRate = mortgages[0]?.interestRate;
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<div
|
|
78
|
+
className={cn(
|
|
79
|
+
"flex flex-col gap-4 border border-border p-4 font-sans",
|
|
80
|
+
className,
|
|
81
|
+
)}
|
|
82
|
+
>
|
|
83
|
+
<div className="flex items-start gap-3">
|
|
84
|
+
<div className="flex size-14 shrink-0 items-center justify-center bg-muted">
|
|
85
|
+
{institutionLogoUrl ? (
|
|
86
|
+
<img
|
|
87
|
+
src={institutionLogoUrl}
|
|
88
|
+
alt=""
|
|
89
|
+
className="size-full object-contain"
|
|
90
|
+
/>
|
|
91
|
+
) : (
|
|
92
|
+
<Building2
|
|
93
|
+
className="size-6 text-muted-foreground"
|
|
94
|
+
aria-hidden="true"
|
|
95
|
+
/>
|
|
96
|
+
)}
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
<div className="flex min-w-0 flex-1 items-start justify-between gap-2">
|
|
100
|
+
<div className="flex min-w-0 flex-col gap-0.5">
|
|
101
|
+
<span className="text-body-small font-semibold text-foreground">
|
|
102
|
+
{address}
|
|
103
|
+
</span>
|
|
104
|
+
<span className="text-body-small text-muted-foreground">
|
|
105
|
+
{assetType}
|
|
106
|
+
</span>
|
|
107
|
+
</div>
|
|
108
|
+
<span className="shrink-0 text-body-small font-semibold text-foreground">
|
|
109
|
+
{formatCurrency(estimatedValue)}
|
|
110
|
+
</span>
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
|
|
114
|
+
{(primaryRate != null || lvr != null) && (
|
|
115
|
+
<div className="flex gap-6 text-xs">
|
|
116
|
+
{primaryRate != null && (
|
|
117
|
+
<span className="text-muted-foreground">
|
|
118
|
+
Interest Rate:{" "}
|
|
119
|
+
<span className="font-semibold text-foreground">
|
|
120
|
+
{primaryRate}%
|
|
121
|
+
</span>
|
|
122
|
+
</span>
|
|
123
|
+
)}
|
|
124
|
+
{lvr != null && (
|
|
125
|
+
<span className="text-muted-foreground">
|
|
126
|
+
LVR <span className="font-semibold text-foreground">{lvr}%</span>
|
|
127
|
+
</span>
|
|
128
|
+
)}
|
|
129
|
+
</div>
|
|
130
|
+
)}
|
|
131
|
+
|
|
132
|
+
<div className="border-t border-border" />
|
|
133
|
+
|
|
134
|
+
{netEquity != null && (
|
|
135
|
+
<div className="flex items-center justify-between border border-primary/40 bg-primary/10 px-4 py-3">
|
|
136
|
+
<span className="text-body-small font-semibold text-foreground">
|
|
137
|
+
Net Equity
|
|
138
|
+
</span>
|
|
139
|
+
<span className="text-body-small font-semibold text-foreground">
|
|
140
|
+
{formatCurrency(netEquity)}
|
|
141
|
+
</span>
|
|
142
|
+
</div>
|
|
143
|
+
)}
|
|
144
|
+
|
|
145
|
+
{mortgages.length > 0 && (
|
|
146
|
+
<Accordion type="multiple">
|
|
147
|
+
{mortgages.map((mortgage) => (
|
|
148
|
+
<AccordionItem key={mortgage.id} value={mortgage.id}>
|
|
149
|
+
<AccordionTrigger>
|
|
150
|
+
<div className="flex flex-1 items-center justify-between pr-2">
|
|
151
|
+
<div className="flex flex-col gap-1.5 items-start">
|
|
152
|
+
<span className="text-body-small font-semibold text-foreground">
|
|
153
|
+
{mortgage.label}
|
|
154
|
+
</span>
|
|
155
|
+
<Badge variant="secondary">{mortgage.type}</Badge>
|
|
156
|
+
</div>
|
|
157
|
+
<span className="text-body-small font-semibold text-foreground">
|
|
158
|
+
{formatCurrency(mortgage.monthlyRepayment)}
|
|
159
|
+
</span>
|
|
160
|
+
</div>
|
|
161
|
+
</AccordionTrigger>
|
|
162
|
+
|
|
163
|
+
<AccordionContent>
|
|
164
|
+
<div className="flex flex-col gap-2 pt-1">
|
|
165
|
+
<div className="flex flex-col gap-2 bg-muted/50 p-4">
|
|
166
|
+
<span className="text-body-small font-semibold text-foreground">
|
|
167
|
+
Loan Details
|
|
168
|
+
</span>
|
|
169
|
+
<DetailRow
|
|
170
|
+
label="Loan Amount"
|
|
171
|
+
value={formatCurrency(mortgage.loanAmount)}
|
|
172
|
+
/>
|
|
173
|
+
<DetailRow
|
|
174
|
+
label="Repayment Amount"
|
|
175
|
+
value={formatCurrency(mortgage.repaymentAmount)}
|
|
176
|
+
/>
|
|
177
|
+
<DetailRow
|
|
178
|
+
label="Interest Rate"
|
|
179
|
+
value={`${mortgage.interestRate}%`}
|
|
180
|
+
/>
|
|
181
|
+
</div>
|
|
182
|
+
|
|
183
|
+
<div className="flex flex-col gap-2 border border-primary/40 bg-primary/10 p-4">
|
|
184
|
+
<span className="text-body-small font-semibold text-foreground">
|
|
185
|
+
Property Statistics
|
|
186
|
+
</span>
|
|
187
|
+
<DetailRow
|
|
188
|
+
label="Property Estimate"
|
|
189
|
+
value={formatCurrency(estimatedValue)}
|
|
190
|
+
/>
|
|
191
|
+
{lvr != null && <DetailRow label="LVR" value={`${lvr}%`} />}
|
|
192
|
+
{netEquity != null && (
|
|
193
|
+
<>
|
|
194
|
+
<div className="border-t border-primary/40" />
|
|
195
|
+
<DetailRow
|
|
196
|
+
label="Equity Available"
|
|
197
|
+
value={formatCurrency(netEquity)}
|
|
198
|
+
/>
|
|
199
|
+
</>
|
|
200
|
+
)}
|
|
201
|
+
</div>
|
|
202
|
+
</div>
|
|
203
|
+
</AccordionContent>
|
|
204
|
+
</AccordionItem>
|
|
205
|
+
))}
|
|
206
|
+
</Accordion>
|
|
207
|
+
)}
|
|
208
|
+
</div>
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// ─── Internal helpers ─────────────────────────────────────────────────────────
|
|
213
|
+
|
|
214
|
+
function DetailRow({ label, value }: { label: string; value: string }) {
|
|
215
|
+
return (
|
|
216
|
+
<div className="flex items-center justify-between">
|
|
217
|
+
<span className="text-xs text-muted-foreground">{label}</span>
|
|
218
|
+
<span className="text-xs font-semibold text-foreground">{value}</span>
|
|
219
|
+
</div>
|
|
220
|
+
);
|
|
221
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { cn } from "@/lib/utils";
|
|
3
|
+
import { formatCurrency } from "@/lib/format-currency";
|
|
4
|
+
import { Button } from "./button";
|
|
5
|
+
import { Plus } from "lucide-react";
|
|
6
|
+
|
|
7
|
+
export type PropertyCarouselType = "homeLoan" | "property";
|
|
8
|
+
|
|
9
|
+
export interface PropertyCarouselItem {
|
|
10
|
+
id: string;
|
|
11
|
+
address: string;
|
|
12
|
+
/** Estimated value or equity */
|
|
13
|
+
value: number;
|
|
14
|
+
/** Linked loan balance, if any */
|
|
15
|
+
loanBalance?: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface PropertyListCarouselProps {
|
|
19
|
+
properties: PropertyCarouselItem[];
|
|
20
|
+
type: PropertyCarouselType;
|
|
21
|
+
/** When provided, shows an "Add Property" button at the end (property type only) */
|
|
22
|
+
onAddProperty?: () => void;
|
|
23
|
+
className?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function PropertyCard({ item }: { item: PropertyCarouselItem }) {
|
|
27
|
+
return (
|
|
28
|
+
<div className="flex shrink-0 flex-col gap-1.5 border border-border p-3 w-44">
|
|
29
|
+
<p className="text-xs text-muted-foreground truncate">{item.address}</p>
|
|
30
|
+
<p className="text-sm font-semibold tabular-nums text-foreground">
|
|
31
|
+
{formatCurrency(item.value)}
|
|
32
|
+
</p>
|
|
33
|
+
{item.loanBalance !== undefined && (
|
|
34
|
+
<p className="text-xs text-muted-foreground tabular-nums">
|
|
35
|
+
Loan: {formatCurrency(item.loanBalance)}
|
|
36
|
+
</p>
|
|
37
|
+
)}
|
|
38
|
+
</div>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function PropertyListCarousel({
|
|
43
|
+
properties,
|
|
44
|
+
type,
|
|
45
|
+
onAddProperty,
|
|
46
|
+
className,
|
|
47
|
+
}: PropertyListCarouselProps) {
|
|
48
|
+
const showAddButton = type === "property" && onAddProperty;
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<div
|
|
52
|
+
className={cn(
|
|
53
|
+
"flex gap-2 overflow-x-auto pb-1 scrollbar-none",
|
|
54
|
+
className,
|
|
55
|
+
)}
|
|
56
|
+
>
|
|
57
|
+
{properties.map((property) => (
|
|
58
|
+
<PropertyCard key={property.id} item={property} />
|
|
59
|
+
))}
|
|
60
|
+
|
|
61
|
+
{showAddButton && (
|
|
62
|
+
<Button
|
|
63
|
+
variant="outline"
|
|
64
|
+
onClick={onAddProperty}
|
|
65
|
+
className="shrink-0 h-auto w-44 flex-col gap-1.5 border-dashed p-3 text-muted-foreground"
|
|
66
|
+
>
|
|
67
|
+
<Plus className="h-4 w-4" />
|
|
68
|
+
<span className="text-xs">Add Property</span>
|
|
69
|
+
</Button>
|
|
70
|
+
)}
|
|
71
|
+
</div>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
2
|
+
import { Trash2, Plus } from "lucide-react";
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
|
+
import { formatCurrency } from "@/lib/format-currency";
|
|
5
|
+
import { Button } from "@/components/ui/button";
|
|
6
|
+
import { Checkbox } from "@/components/ui/checkbox";
|
|
7
|
+
import {
|
|
8
|
+
Dialog,
|
|
9
|
+
DialogContent,
|
|
10
|
+
DialogFooter,
|
|
11
|
+
DialogHeader,
|
|
12
|
+
DialogTitle,
|
|
13
|
+
} from "@/components/ui/dialog";
|
|
14
|
+
import { Field, FieldLabel } from "@/components/ui/field";
|
|
15
|
+
import {
|
|
16
|
+
AddressAutocomplete,
|
|
17
|
+
type AddressOption,
|
|
18
|
+
} from "@/components/ui/form-primitives";
|
|
19
|
+
import { Input } from "@/components/ui/input";
|
|
20
|
+
import { Separator } from "@/components/ui/separator";
|
|
21
|
+
import { Spinner } from "@/components/ui/spinner";
|
|
22
|
+
import {
|
|
23
|
+
Table,
|
|
24
|
+
TableBody,
|
|
25
|
+
TableCell,
|
|
26
|
+
TableHead,
|
|
27
|
+
TableHeader,
|
|
28
|
+
TableRow,
|
|
29
|
+
} from "@/components/ui/table";
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* PropertyReportDialog — WealthX DS (L4 Template)
|
|
33
|
+
*
|
|
34
|
+
* Dialog for generating a property valuation report from the scenario's
|
|
35
|
+
* linked properties. Opened from the Bank Statement tab of
|
|
36
|
+
* `OpportunityDetailsDrawer` when the user selects "Property Report" and
|
|
37
|
+
* clicks "Generate".
|
|
38
|
+
*
|
|
39
|
+
* Internal state:
|
|
40
|
+
* - Report name (Input)
|
|
41
|
+
* - Manually added properties (via AddressAutocomplete + Add button)
|
|
42
|
+
* - Selected property IDs (Table with Checkboxes)
|
|
43
|
+
*
|
|
44
|
+
* Data handed in via props (consumer owns fetching):
|
|
45
|
+
* - `properties` — pre-loaded properties from the scenario (API list)
|
|
46
|
+
* - `isLoadingProperties` — spinner while fetching
|
|
47
|
+
* - `searchResults` — address search results driven by `onSearchQueryChange`
|
|
48
|
+
*
|
|
49
|
+
* Layer: L4 Template
|
|
50
|
+
*/
|
|
51
|
+
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
// Types
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
|
|
56
|
+
export interface PropertyItem {
|
|
57
|
+
id: string;
|
|
58
|
+
address?: string;
|
|
59
|
+
propertyType?: string;
|
|
60
|
+
/** Estimated value in dollars. */
|
|
61
|
+
estimateValue?: number;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface PropertySearchResult {
|
|
65
|
+
/** Unique property identifier returned by the address search service. */
|
|
66
|
+
id: string;
|
|
67
|
+
/** Full address string shown in the dropdown. */
|
|
68
|
+
address: string;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface PropertyReportPayload {
|
|
72
|
+
reportName: string;
|
|
73
|
+
selectedPropertyIds: string[];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface PropertyReportDialogProps {
|
|
77
|
+
open: boolean;
|
|
78
|
+
onClose: () => void;
|
|
79
|
+
/**
|
|
80
|
+
* Called when the user clicks Generate (only when form is valid).
|
|
81
|
+
* Consumer is responsible for the actual API call.
|
|
82
|
+
*/
|
|
83
|
+
onSubmit: (payload: PropertyReportPayload) => void;
|
|
84
|
+
/** Pre-loaded properties from the scenario's loan data. */
|
|
85
|
+
properties: PropertyItem[];
|
|
86
|
+
/** Provide `true` while the consumer is fetching the scenario's properties. */
|
|
87
|
+
isLoadingProperties?: boolean;
|
|
88
|
+
/**
|
|
89
|
+
* Address search results for the autocomplete dropdown.
|
|
90
|
+
* Consumer debounces and fetches when `onSearchQueryChange` fires.
|
|
91
|
+
*/
|
|
92
|
+
searchResults?: PropertySearchResult[];
|
|
93
|
+
/**
|
|
94
|
+
* Called when the user types in the address search input (debounce externally).
|
|
95
|
+
* Pass an empty string when the query is cleared.
|
|
96
|
+
*/
|
|
97
|
+
onSearchQueryChange?: (query: string) => void;
|
|
98
|
+
/** Show a loading indicator on the Generate button while the API call is in flight. */
|
|
99
|
+
isLoading?: boolean;
|
|
100
|
+
className?: string;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
// PropertyReportDialog
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
|
|
107
|
+
export function PropertyReportDialog({
|
|
108
|
+
open,
|
|
109
|
+
onClose,
|
|
110
|
+
onSubmit,
|
|
111
|
+
properties,
|
|
112
|
+
isLoadingProperties = false,
|
|
113
|
+
searchResults = [],
|
|
114
|
+
onSearchQueryChange,
|
|
115
|
+
isLoading = false,
|
|
116
|
+
className,
|
|
117
|
+
}: PropertyReportDialogProps) {
|
|
118
|
+
const [reportName, setReportName] = useState("Property Report 1");
|
|
119
|
+
const [selectedPropertyIds, setSelectedPropertyIds] = useState<string[]>([]);
|
|
120
|
+
|
|
121
|
+
// Properties added manually via the address search (not in the API list)
|
|
122
|
+
const [manualProperties, setManualProperties] = useState<PropertyItem[]>([]);
|
|
123
|
+
|
|
124
|
+
// Address autocomplete state
|
|
125
|
+
const [addressInputValue, setAddressInputValue] = useState("");
|
|
126
|
+
const [selectedSuggestion, setSelectedSuggestion] =
|
|
127
|
+
useState<PropertySearchResult | null>(null);
|
|
128
|
+
|
|
129
|
+
// Convert searchResults → AddressOption[] for AddressAutocomplete
|
|
130
|
+
const addressOptions = useMemo<AddressOption[]>(
|
|
131
|
+
() => searchResults.map((r) => ({ id: r.id, label: r.address })),
|
|
132
|
+
[searchResults],
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
// Reset form whenever the dialog opens
|
|
136
|
+
useEffect(() => {
|
|
137
|
+
if (!open) return;
|
|
138
|
+
setReportName("Property Report 1");
|
|
139
|
+
setSelectedPropertyIds([]);
|
|
140
|
+
setManualProperties([]);
|
|
141
|
+
setAddressInputValue("");
|
|
142
|
+
setSelectedSuggestion(null);
|
|
143
|
+
onSearchQueryChange?.("");
|
|
144
|
+
}, [open]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
145
|
+
|
|
146
|
+
// Combined list shown in the table
|
|
147
|
+
const allProperties = useMemo<PropertyItem[]>(
|
|
148
|
+
() => [...properties, ...manualProperties],
|
|
149
|
+
[properties, manualProperties],
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
// ── Property selection ───────────────────────────────────────────────────
|
|
153
|
+
|
|
154
|
+
const areAllSelected =
|
|
155
|
+
allProperties.length > 0 &&
|
|
156
|
+
selectedPropertyIds.length === allProperties.length;
|
|
157
|
+
const isSomeSelected = selectedPropertyIds.length > 0 && !areAllSelected;
|
|
158
|
+
|
|
159
|
+
const handleToggleProperty = (id: string) => {
|
|
160
|
+
setSelectedPropertyIds((prev) =>
|
|
161
|
+
prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id],
|
|
162
|
+
);
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
const handleToggleAll = () => {
|
|
166
|
+
if (areAllSelected) {
|
|
167
|
+
setSelectedPropertyIds([]);
|
|
168
|
+
} else {
|
|
169
|
+
setSelectedPropertyIds(allProperties.map((p) => p.id));
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
// ── Address search & manual add ──────────────────────────────────────────
|
|
174
|
+
|
|
175
|
+
const handleAddProperty = useCallback(() => {
|
|
176
|
+
if (!selectedSuggestion) return;
|
|
177
|
+
const { id, address } = selectedSuggestion;
|
|
178
|
+
|
|
179
|
+
const alreadyExists = allProperties.some((p) => p.id === id);
|
|
180
|
+
if (alreadyExists) return; // silently skip (button is disabled when already added)
|
|
181
|
+
|
|
182
|
+
setManualProperties((prev) => [...prev, { id, address }]);
|
|
183
|
+
setSelectedSuggestion(null);
|
|
184
|
+
setAddressInputValue("");
|
|
185
|
+
onSearchQueryChange?.("");
|
|
186
|
+
}, [selectedSuggestion, allProperties, onSearchQueryChange]);
|
|
187
|
+
|
|
188
|
+
const handleRemoveManual = useCallback((id: string) => {
|
|
189
|
+
setManualProperties((prev) => prev.filter((p) => p.id !== id));
|
|
190
|
+
setSelectedPropertyIds((prev) => prev.filter((x) => x !== id));
|
|
191
|
+
}, []);
|
|
192
|
+
|
|
193
|
+
const isManual = (id: string) => manualProperties.some((p) => p.id === id);
|
|
194
|
+
|
|
195
|
+
const isAddDisabled =
|
|
196
|
+
!selectedSuggestion ||
|
|
197
|
+
allProperties.some((p) => p.id === selectedSuggestion.id);
|
|
198
|
+
|
|
199
|
+
// ── Submit guard ─────────────────────────────────────────────────────────
|
|
200
|
+
|
|
201
|
+
const isSubmitDisabled =
|
|
202
|
+
!reportName.trim() || selectedPropertyIds.length === 0;
|
|
203
|
+
|
|
204
|
+
const handleSubmit = () => {
|
|
205
|
+
if (isSubmitDisabled) return;
|
|
206
|
+
onSubmit({ reportName, selectedPropertyIds });
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
// ── Render ───────────────────────────────────────────────────────────────
|
|
210
|
+
|
|
211
|
+
return (
|
|
212
|
+
<Dialog open={open} onOpenChange={(o) => !o && onClose()}>
|
|
213
|
+
<DialogContent
|
|
214
|
+
size="auto"
|
|
215
|
+
className={cn(className)}
|
|
216
|
+
data-slot="property-report-dialog"
|
|
217
|
+
>
|
|
218
|
+
<DialogHeader>
|
|
219
|
+
<DialogTitle>Property Report</DialogTitle>
|
|
220
|
+
</DialogHeader>
|
|
221
|
+
|
|
222
|
+
<Separator />
|
|
223
|
+
|
|
224
|
+
<div className="flex flex-col gap-4 pt-1">
|
|
225
|
+
{/* Report name */}
|
|
226
|
+
<Field>
|
|
227
|
+
<FieldLabel>Name</FieldLabel>
|
|
228
|
+
<Input
|
|
229
|
+
value={reportName}
|
|
230
|
+
onChange={(e) => setReportName(e.target.value)}
|
|
231
|
+
placeholder="Property Report 1"
|
|
232
|
+
/>
|
|
233
|
+
</Field>
|
|
234
|
+
|
|
235
|
+
{/* Address search + Add button */}
|
|
236
|
+
<div className="flex items-end gap-2">
|
|
237
|
+
<Field className="flex-1">
|
|
238
|
+
<FieldLabel>Search property</FieldLabel>
|
|
239
|
+
<AddressAutocomplete
|
|
240
|
+
value={addressInputValue}
|
|
241
|
+
suggestions={addressOptions}
|
|
242
|
+
placeholder="Start typing an address…"
|
|
243
|
+
onValueChange={(v) => {
|
|
244
|
+
setAddressInputValue(v);
|
|
245
|
+
setSelectedSuggestion(null);
|
|
246
|
+
onSearchQueryChange?.(v);
|
|
247
|
+
}}
|
|
248
|
+
onSelect={(opt) => {
|
|
249
|
+
setSelectedSuggestion({ id: opt.id, address: opt.label });
|
|
250
|
+
}}
|
|
251
|
+
/>
|
|
252
|
+
</Field>
|
|
253
|
+
<Button
|
|
254
|
+
variant="outline"
|
|
255
|
+
onClick={handleAddProperty}
|
|
256
|
+
disabled={isAddDisabled}
|
|
257
|
+
>
|
|
258
|
+
<Plus className="size-4" />
|
|
259
|
+
Add
|
|
260
|
+
</Button>
|
|
261
|
+
</div>
|
|
262
|
+
|
|
263
|
+
{/* Properties table */}
|
|
264
|
+
<div>
|
|
265
|
+
{isLoadingProperties ? (
|
|
266
|
+
<div className="flex items-center justify-center p-6">
|
|
267
|
+
<Spinner size="lg" />
|
|
268
|
+
</div>
|
|
269
|
+
) : allProperties.length === 0 ? (
|
|
270
|
+
<p className="py-4 text-center text-body-medium text-muted-foreground">
|
|
271
|
+
No properties found. Use the search above to add properties.
|
|
272
|
+
</p>
|
|
273
|
+
) : (
|
|
274
|
+
<Table>
|
|
275
|
+
<TableHeader>
|
|
276
|
+
<TableRow>
|
|
277
|
+
<TableHead className="w-10">
|
|
278
|
+
<Checkbox
|
|
279
|
+
checked={areAllSelected}
|
|
280
|
+
indeterminate={isSomeSelected}
|
|
281
|
+
onCheckedChange={handleToggleAll}
|
|
282
|
+
aria-label="Select all properties"
|
|
283
|
+
/>
|
|
284
|
+
</TableHead>
|
|
285
|
+
<TableHead>Address</TableHead>
|
|
286
|
+
<TableHead>Property Type</TableHead>
|
|
287
|
+
<TableHead>Estimate</TableHead>
|
|
288
|
+
<TableHead className="w-10" />
|
|
289
|
+
</TableRow>
|
|
290
|
+
</TableHeader>
|
|
291
|
+
<TableBody>
|
|
292
|
+
{allProperties.map((property) => (
|
|
293
|
+
<TableRow key={property.id}>
|
|
294
|
+
<TableCell>
|
|
295
|
+
<Checkbox
|
|
296
|
+
checked={selectedPropertyIds.includes(property.id)}
|
|
297
|
+
onCheckedChange={() =>
|
|
298
|
+
handleToggleProperty(property.id)
|
|
299
|
+
}
|
|
300
|
+
aria-label={`Select ${property.address ?? property.id}`}
|
|
301
|
+
/>
|
|
302
|
+
</TableCell>
|
|
303
|
+
<TableCell>
|
|
304
|
+
<span className="text-body-medium font-semibold">
|
|
305
|
+
{property.address ?? "—"}
|
|
306
|
+
</span>
|
|
307
|
+
</TableCell>
|
|
308
|
+
<TableCell>
|
|
309
|
+
<span className="text-body-medium">
|
|
310
|
+
{property.propertyType ?? "—"}
|
|
311
|
+
</span>
|
|
312
|
+
</TableCell>
|
|
313
|
+
<TableCell>
|
|
314
|
+
<span className="text-body-medium">
|
|
315
|
+
{property.estimateValue
|
|
316
|
+
? formatCurrency(property.estimateValue)
|
|
317
|
+
: "—"}
|
|
318
|
+
</span>
|
|
319
|
+
</TableCell>
|
|
320
|
+
<TableCell>
|
|
321
|
+
{isManual(property.id) && (
|
|
322
|
+
<Button
|
|
323
|
+
variant="ghost"
|
|
324
|
+
size="icon-sm"
|
|
325
|
+
aria-label="Remove property"
|
|
326
|
+
onClick={() => handleRemoveManual(property.id)}
|
|
327
|
+
>
|
|
328
|
+
<Trash2 className="size-4" />
|
|
329
|
+
</Button>
|
|
330
|
+
)}
|
|
331
|
+
</TableCell>
|
|
332
|
+
</TableRow>
|
|
333
|
+
))}
|
|
334
|
+
</TableBody>
|
|
335
|
+
</Table>
|
|
336
|
+
)}
|
|
337
|
+
</div>
|
|
338
|
+
</div>
|
|
339
|
+
|
|
340
|
+
<DialogFooter>
|
|
341
|
+
<Button variant="ghost" onClick={onClose}>
|
|
342
|
+
Cancel
|
|
343
|
+
</Button>
|
|
344
|
+
<Button
|
|
345
|
+
onClick={handleSubmit}
|
|
346
|
+
disabled={isSubmitDisabled}
|
|
347
|
+
loading={isLoading}
|
|
348
|
+
>
|
|
349
|
+
Generate
|
|
350
|
+
</Button>
|
|
351
|
+
</DialogFooter>
|
|
352
|
+
</DialogContent>
|
|
353
|
+
</Dialog>
|
|
354
|
+
);
|
|
355
|
+
}
|