@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,254 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "@/lib/utils";
|
|
3
|
+
import {
|
|
4
|
+
Accordion,
|
|
5
|
+
AccordionContent,
|
|
6
|
+
AccordionItem,
|
|
7
|
+
AccordionTrigger,
|
|
8
|
+
} from "@/components/ui/accordion";
|
|
9
|
+
import { Button } from "@/components/ui/button";
|
|
10
|
+
import { formatCurrency } from "@/lib/format-currency";
|
|
11
|
+
|
|
12
|
+
export type DebtItem = {
|
|
13
|
+
id: string;
|
|
14
|
+
description: string;
|
|
15
|
+
currentOwing: number;
|
|
16
|
+
monthlyRepayment?: number;
|
|
17
|
+
interestRate?: number;
|
|
18
|
+
subLabel?: string;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type DebtGroup = {
|
|
22
|
+
debtType: string;
|
|
23
|
+
label: string;
|
|
24
|
+
items: DebtItem[];
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export type DebtAccordionProps = {
|
|
28
|
+
groups: DebtGroup[];
|
|
29
|
+
openGroups?: string[];
|
|
30
|
+
onToggleGroup?: (debtType: string) => void;
|
|
31
|
+
onAddItem?: (debtType: string) => void;
|
|
32
|
+
onEditItem?: (debtType: string, itemId: string) => void;
|
|
33
|
+
onDeleteItem?: (debtType: string, itemId: string) => void;
|
|
34
|
+
onPrev?: () => void;
|
|
35
|
+
onNext?: () => void;
|
|
36
|
+
nextLabel?: string;
|
|
37
|
+
/** Accessible label for the accordion landmark region. */
|
|
38
|
+
"aria-label"?: string;
|
|
39
|
+
className?: string;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export function DebtAccordion({
|
|
43
|
+
groups,
|
|
44
|
+
openGroups,
|
|
45
|
+
onToggleGroup,
|
|
46
|
+
onAddItem,
|
|
47
|
+
onEditItem,
|
|
48
|
+
onDeleteItem,
|
|
49
|
+
onPrev,
|
|
50
|
+
onNext,
|
|
51
|
+
nextLabel = "Next",
|
|
52
|
+
"aria-label": ariaLabel = "Debt list",
|
|
53
|
+
className,
|
|
54
|
+
}: DebtAccordionProps) {
|
|
55
|
+
const totalOwing = groups
|
|
56
|
+
.flatMap((g) => g.items)
|
|
57
|
+
.reduce((sum, item) => sum + item.currentOwing, 0);
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<div className={cn("flex flex-col gap-4 font-sans", className)}>
|
|
61
|
+
<div className="flex items-center justify-between pb-2">
|
|
62
|
+
<h3 className="text-sm font-semibold text-foreground">Debts</h3>
|
|
63
|
+
<span className="text-sm font-medium text-foreground">
|
|
64
|
+
Total owing: {formatCurrency(totalOwing)}
|
|
65
|
+
</span>
|
|
66
|
+
</div>
|
|
67
|
+
|
|
68
|
+
<Accordion
|
|
69
|
+
type="multiple"
|
|
70
|
+
aria-label={ariaLabel}
|
|
71
|
+
value={openGroups}
|
|
72
|
+
onValueChange={(vals) => {
|
|
73
|
+
const prev = new Set(openGroups ?? []);
|
|
74
|
+
const next = new Set(vals as string[]);
|
|
75
|
+
for (const v of next) {
|
|
76
|
+
if (!prev.has(v)) onToggleGroup?.(v);
|
|
77
|
+
}
|
|
78
|
+
for (const v of prev) {
|
|
79
|
+
if (!next.has(v)) onToggleGroup?.(v);
|
|
80
|
+
}
|
|
81
|
+
}}
|
|
82
|
+
>
|
|
83
|
+
{groups.map((group) => (
|
|
84
|
+
<AccordionItem key={group.debtType} value={group.debtType}>
|
|
85
|
+
<AccordionTrigger>
|
|
86
|
+
<div className="flex w-full items-center justify-between pr-2">
|
|
87
|
+
<span>{group.label}</span>
|
|
88
|
+
<span className="text-xs text-muted-foreground">
|
|
89
|
+
{group.items.length}{" "}
|
|
90
|
+
{group.items.length === 1 ? "item" : "items"} ·{" "}
|
|
91
|
+
{formatCurrency(
|
|
92
|
+
group.items.reduce((s, i) => s + i.currentOwing, 0),
|
|
93
|
+
)}{" "}
|
|
94
|
+
owing
|
|
95
|
+
</span>
|
|
96
|
+
</div>
|
|
97
|
+
</AccordionTrigger>
|
|
98
|
+
<AccordionContent>
|
|
99
|
+
<div className="flex flex-col gap-2 pb-2">
|
|
100
|
+
{group.items.length === 0 ? (
|
|
101
|
+
<p className="text-xs text-muted-foreground">
|
|
102
|
+
No {group.label.toLowerCase()} added yet.
|
|
103
|
+
</p>
|
|
104
|
+
) : (
|
|
105
|
+
group.items.map((item) => (
|
|
106
|
+
<DebtItemRow
|
|
107
|
+
key={item.id}
|
|
108
|
+
item={item}
|
|
109
|
+
onEdit={() => onEditItem?.(group.debtType, item.id)}
|
|
110
|
+
onDelete={() => onDeleteItem?.(group.debtType, item.id)}
|
|
111
|
+
/>
|
|
112
|
+
))
|
|
113
|
+
)}
|
|
114
|
+
<button
|
|
115
|
+
type="button"
|
|
116
|
+
onClick={() => onAddItem?.(group.debtType)}
|
|
117
|
+
className="mt-1 flex items-center gap-1 text-xs text-primary hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
118
|
+
>
|
|
119
|
+
<PlusIcon className="size-3" />
|
|
120
|
+
Add {group.label}
|
|
121
|
+
</button>
|
|
122
|
+
</div>
|
|
123
|
+
</AccordionContent>
|
|
124
|
+
</AccordionItem>
|
|
125
|
+
))}
|
|
126
|
+
</Accordion>
|
|
127
|
+
|
|
128
|
+
<div className="flex items-center justify-between pt-2">
|
|
129
|
+
{onPrev ? (
|
|
130
|
+
<Button variant="outline" onClick={onPrev}>
|
|
131
|
+
<ChevronLeftIcon className="mr-1 size-4" />
|
|
132
|
+
Previous
|
|
133
|
+
</Button>
|
|
134
|
+
) : (
|
|
135
|
+
<span />
|
|
136
|
+
)}
|
|
137
|
+
{onNext && (
|
|
138
|
+
<Button onClick={onNext}>
|
|
139
|
+
{nextLabel}
|
|
140
|
+
<ChevronRightIcon className="ml-1 size-4" />
|
|
141
|
+
</Button>
|
|
142
|
+
)}
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function DebtItemRow({
|
|
149
|
+
item,
|
|
150
|
+
onEdit,
|
|
151
|
+
onDelete,
|
|
152
|
+
}: {
|
|
153
|
+
item: DebtItem;
|
|
154
|
+
onEdit?: () => void;
|
|
155
|
+
onDelete?: () => void;
|
|
156
|
+
}) {
|
|
157
|
+
return (
|
|
158
|
+
<div className="flex items-start justify-between border border-border p-3">
|
|
159
|
+
<div className="flex flex-col gap-0.5">
|
|
160
|
+
<span className="text-sm font-medium text-foreground">
|
|
161
|
+
{item.description}
|
|
162
|
+
</span>
|
|
163
|
+
{item.subLabel && (
|
|
164
|
+
<span className="text-xs text-muted-foreground">{item.subLabel}</span>
|
|
165
|
+
)}
|
|
166
|
+
<div className="flex flex-wrap gap-3">
|
|
167
|
+
<span className="text-sm text-foreground">
|
|
168
|
+
Owing: {formatCurrency(item.currentOwing)}
|
|
169
|
+
</span>
|
|
170
|
+
{item.monthlyRepayment != null && (
|
|
171
|
+
<span className="text-xs text-muted-foreground">
|
|
172
|
+
Repayment: {formatCurrency(item.monthlyRepayment)}/mo
|
|
173
|
+
</span>
|
|
174
|
+
)}
|
|
175
|
+
{item.interestRate != null && (
|
|
176
|
+
<span className="text-xs text-muted-foreground">
|
|
177
|
+
Rate: {item.interestRate}%
|
|
178
|
+
</span>
|
|
179
|
+
)}
|
|
180
|
+
</div>
|
|
181
|
+
</div>
|
|
182
|
+
<div className="flex shrink-0 items-center gap-2">
|
|
183
|
+
<button
|
|
184
|
+
type="button"
|
|
185
|
+
onClick={onEdit}
|
|
186
|
+
className="text-xs text-primary hover:underline focus-visible:outline-none"
|
|
187
|
+
>
|
|
188
|
+
Edit
|
|
189
|
+
</button>
|
|
190
|
+
<button
|
|
191
|
+
type="button"
|
|
192
|
+
onClick={onDelete}
|
|
193
|
+
className="text-xs text-destructive hover:underline focus-visible:outline-none"
|
|
194
|
+
>
|
|
195
|
+
Delete
|
|
196
|
+
</button>
|
|
197
|
+
</div>
|
|
198
|
+
</div>
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function PlusIcon({ className }: { className?: string }) {
|
|
203
|
+
return (
|
|
204
|
+
<svg
|
|
205
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
206
|
+
viewBox="0 0 24 24"
|
|
207
|
+
fill="none"
|
|
208
|
+
stroke="currentColor"
|
|
209
|
+
strokeWidth={2}
|
|
210
|
+
strokeLinecap="round"
|
|
211
|
+
strokeLinejoin="round"
|
|
212
|
+
className={className}
|
|
213
|
+
aria-hidden="true"
|
|
214
|
+
>
|
|
215
|
+
<path d="M12 5v14M5 12h14" />
|
|
216
|
+
</svg>
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function ChevronLeftIcon({ className }: { className?: string }) {
|
|
221
|
+
return (
|
|
222
|
+
<svg
|
|
223
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
224
|
+
viewBox="0 0 24 24"
|
|
225
|
+
fill="none"
|
|
226
|
+
stroke="currentColor"
|
|
227
|
+
strokeWidth={2}
|
|
228
|
+
strokeLinecap="round"
|
|
229
|
+
strokeLinejoin="round"
|
|
230
|
+
className={className}
|
|
231
|
+
aria-hidden="true"
|
|
232
|
+
>
|
|
233
|
+
<path d="m15 18-6-6 6-6" />
|
|
234
|
+
</svg>
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function ChevronRightIcon({ className }: { className?: string }) {
|
|
239
|
+
return (
|
|
240
|
+
<svg
|
|
241
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
242
|
+
viewBox="0 0 24 24"
|
|
243
|
+
fill="none"
|
|
244
|
+
stroke="currentColor"
|
|
245
|
+
strokeWidth={2}
|
|
246
|
+
strokeLinecap="round"
|
|
247
|
+
strokeLinejoin="round"
|
|
248
|
+
className={className}
|
|
249
|
+
aria-hidden="true"
|
|
250
|
+
>
|
|
251
|
+
<path d="m9 18 6-6-6-6" />
|
|
252
|
+
</svg>
|
|
253
|
+
);
|
|
254
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import {
|
|
3
|
+
AlertDialog,
|
|
4
|
+
AlertDialogAction,
|
|
5
|
+
AlertDialogCancel,
|
|
6
|
+
AlertDialogContent,
|
|
7
|
+
AlertDialogDescription,
|
|
8
|
+
AlertDialogFooter,
|
|
9
|
+
AlertDialogHeader,
|
|
10
|
+
AlertDialogTitle,
|
|
11
|
+
} from "@/components/ui/alert-dialog";
|
|
12
|
+
import { Spinner } from "@/components/ui/spinner";
|
|
13
|
+
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// Types
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
export interface DeleteContactComponentProps {
|
|
19
|
+
open: boolean;
|
|
20
|
+
onOpenChange: (open: boolean) => void;
|
|
21
|
+
/** "single" — confirm deleting one contact by name. */
|
|
22
|
+
mode: "single" | "bulk";
|
|
23
|
+
/** Required when mode="single". */
|
|
24
|
+
contactName?: string;
|
|
25
|
+
/** Required when mode="bulk". Number of contacts to delete. */
|
|
26
|
+
count?: number;
|
|
27
|
+
onConfirm: () => void;
|
|
28
|
+
isLoading?: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
// Component
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
|
|
35
|
+
export function DeleteContactComponent({
|
|
36
|
+
open,
|
|
37
|
+
onOpenChange,
|
|
38
|
+
mode,
|
|
39
|
+
contactName,
|
|
40
|
+
count = 0,
|
|
41
|
+
onConfirm,
|
|
42
|
+
isLoading = false,
|
|
43
|
+
}: DeleteContactComponentProps) {
|
|
44
|
+
const isSingle = mode === "single";
|
|
45
|
+
|
|
46
|
+
const title = isSingle
|
|
47
|
+
? `Delete ${contactName ?? "this contact"}?`
|
|
48
|
+
: `Delete ${count} contact${count !== 1 ? "s" : ""}?`;
|
|
49
|
+
|
|
50
|
+
const description = isSingle
|
|
51
|
+
? `This will permanently remove ${contactName ?? "this contact"} and all their associated data. This action cannot be undone.`
|
|
52
|
+
: `This will permanently remove ${count} selected contact${count !== 1 ? "s" : ""} and all their associated data. This action cannot be undone.`;
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<AlertDialog
|
|
56
|
+
open={open}
|
|
57
|
+
onOpenChange={isLoading ? undefined : onOpenChange}
|
|
58
|
+
>
|
|
59
|
+
<AlertDialogContent>
|
|
60
|
+
<AlertDialogHeader>
|
|
61
|
+
<AlertDialogTitle className="text-destructive">
|
|
62
|
+
{title}
|
|
63
|
+
</AlertDialogTitle>
|
|
64
|
+
<AlertDialogDescription>{description}</AlertDialogDescription>
|
|
65
|
+
</AlertDialogHeader>
|
|
66
|
+
|
|
67
|
+
<AlertDialogFooter>
|
|
68
|
+
<AlertDialogCancel disabled={isLoading}>Cancel</AlertDialogCancel>
|
|
69
|
+
<AlertDialogAction
|
|
70
|
+
onClick={onConfirm}
|
|
71
|
+
disabled={isLoading}
|
|
72
|
+
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
|
73
|
+
>
|
|
74
|
+
{isLoading ? (
|
|
75
|
+
<>
|
|
76
|
+
<Spinner className="size-3.5" />
|
|
77
|
+
Deleting…
|
|
78
|
+
</>
|
|
79
|
+
) : (
|
|
80
|
+
"Delete"
|
|
81
|
+
)}
|
|
82
|
+
</AlertDialogAction>
|
|
83
|
+
</AlertDialogFooter>
|
|
84
|
+
</AlertDialogContent>
|
|
85
|
+
</AlertDialog>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
@@ -100,7 +100,7 @@ export type DialogContentProps = React.ComponentProps<
|
|
|
100
100
|
* instead of appending to document.body.
|
|
101
101
|
*/
|
|
102
102
|
container?: HTMLElement | null;
|
|
103
|
-
/** Vertical alignment of the dialog. "top" anchors to top-4; "center"
|
|
103
|
+
/** Vertical alignment of the dialog. "top" (default) anchors to top-4; "center" vertically centers. */
|
|
104
104
|
align?: "center" | "top";
|
|
105
105
|
/**
|
|
106
106
|
* Width behaviour of the dialog panel.
|
|
@@ -125,7 +125,7 @@ function DialogContent({
|
|
|
125
125
|
showCloseButton = true,
|
|
126
126
|
style,
|
|
127
127
|
container,
|
|
128
|
-
align = "
|
|
128
|
+
align = "top",
|
|
129
129
|
size = "lg",
|
|
130
130
|
minWidth,
|
|
131
131
|
...props
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
|
+
import {
|
|
5
|
+
DropdownMenu,
|
|
6
|
+
DropdownMenuCheckboxItem,
|
|
7
|
+
DropdownMenuContent,
|
|
8
|
+
DropdownMenuTrigger,
|
|
9
|
+
DropdownMenuPortal,
|
|
10
|
+
} from "@/components/ui/dropdown-menu";
|
|
11
|
+
import { Button } from "@/components/ui/button";
|
|
12
|
+
|
|
13
|
+
export type DynamicTabItem = {
|
|
14
|
+
value: string;
|
|
15
|
+
label: string;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type DynamicTabsProps = {
|
|
19
|
+
/** Full list of available tabs (selected + unselected). */
|
|
20
|
+
allTabs: DynamicTabItem[];
|
|
21
|
+
/** Values of the tabs currently shown in the bar. */
|
|
22
|
+
selectedTabs: string[];
|
|
23
|
+
/** Currently active tab value. */
|
|
24
|
+
activeTab: string;
|
|
25
|
+
onTabChange: (value: string) => void;
|
|
26
|
+
/** Called when the user adds/removes tabs via the overflow dropdown. */
|
|
27
|
+
onTabsChange?: (next: string[]) => void;
|
|
28
|
+
/** Label for the overflow toggle button. */
|
|
29
|
+
expandLabel?: string;
|
|
30
|
+
className?: string;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export function DynamicTabs({
|
|
34
|
+
allTabs,
|
|
35
|
+
selectedTabs,
|
|
36
|
+
activeTab,
|
|
37
|
+
onTabChange,
|
|
38
|
+
onTabsChange,
|
|
39
|
+
expandLabel = "More",
|
|
40
|
+
className,
|
|
41
|
+
}: DynamicTabsProps) {
|
|
42
|
+
// Staged selections inside the dropdown (committed on close).
|
|
43
|
+
const [staged, setStaged] = useState<Set<string>>(new Set());
|
|
44
|
+
const [open, setOpen] = useState(false);
|
|
45
|
+
|
|
46
|
+
const selectedSet = new Set(selectedTabs);
|
|
47
|
+
|
|
48
|
+
const handleOpenChange = (next: boolean) => {
|
|
49
|
+
if (!next && staged.size > 0) {
|
|
50
|
+
// Commit: append new tabs preserving original order
|
|
51
|
+
const ordered = allTabs
|
|
52
|
+
.filter((t) => staged.has(t.value))
|
|
53
|
+
.map((t) => t.value);
|
|
54
|
+
onTabsChange?.([...selectedTabs, ...ordered]);
|
|
55
|
+
setStaged(new Set());
|
|
56
|
+
}
|
|
57
|
+
setOpen(next);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const handleToggle = (value: string, checked: boolean) => {
|
|
61
|
+
if (selectedSet.has(value)) return; // already shown — no-op
|
|
62
|
+
setStaged((prev) => {
|
|
63
|
+
const next = new Set(prev);
|
|
64
|
+
checked ? next.add(value) : next.delete(value);
|
|
65
|
+
return next;
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const hasOverflow = allTabs.some((t) => !selectedSet.has(t.value));
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
// Outer wrapper is NOT role="tablist" — the dropdown must live outside it
|
|
73
|
+
<div
|
|
74
|
+
className={cn(
|
|
75
|
+
"flex items-end gap-0 border-b border-border font-sans",
|
|
76
|
+
className,
|
|
77
|
+
)}
|
|
78
|
+
>
|
|
79
|
+
{/* Tab buttons only — valid children of tablist */}
|
|
80
|
+
<div role="tablist" className="flex min-w-0 flex-1 items-end gap-0">
|
|
81
|
+
{selectedTabs.map((value) => {
|
|
82
|
+
const tab = allTabs.find((t) => t.value === value);
|
|
83
|
+
if (!tab) return null;
|
|
84
|
+
const isActive = value === activeTab;
|
|
85
|
+
return (
|
|
86
|
+
<button
|
|
87
|
+
key={value}
|
|
88
|
+
role="tab"
|
|
89
|
+
aria-selected={isActive}
|
|
90
|
+
onClick={() => onTabChange(value)}
|
|
91
|
+
className={cn(
|
|
92
|
+
"relative shrink-0 cursor-pointer px-4 py-2.5 text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
93
|
+
isActive
|
|
94
|
+
? "border-b-2 border-primary text-foreground"
|
|
95
|
+
: "border-b-2 border-transparent text-muted-foreground hover:text-foreground",
|
|
96
|
+
)}
|
|
97
|
+
>
|
|
98
|
+
{tab.label}
|
|
99
|
+
</button>
|
|
100
|
+
);
|
|
101
|
+
})}
|
|
102
|
+
</div>
|
|
103
|
+
|
|
104
|
+
{/* Overflow dropdown lives outside the tablist to satisfy aria-required-children */}
|
|
105
|
+
{hasOverflow && (
|
|
106
|
+
<DropdownMenu open={open} onOpenChange={handleOpenChange}>
|
|
107
|
+
<DropdownMenuTrigger className="mb-px shrink-0 inline-flex items-center gap-1 px-3 py-2 text-sm font-medium text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring">
|
|
108
|
+
{expandLabel}
|
|
109
|
+
<ChevronDownIcon className="size-3" />
|
|
110
|
+
</DropdownMenuTrigger>
|
|
111
|
+
<DropdownMenuPortal>
|
|
112
|
+
<DropdownMenuContent align="end" className="min-w-48">
|
|
113
|
+
{allTabs.map((tab) => (
|
|
114
|
+
<DropdownMenuCheckboxItem
|
|
115
|
+
key={tab.value}
|
|
116
|
+
checked={selectedSet.has(tab.value) || staged.has(tab.value)}
|
|
117
|
+
disabled={selectedSet.has(tab.value)}
|
|
118
|
+
onCheckedChange={(checked) =>
|
|
119
|
+
handleToggle(tab.value, checked)
|
|
120
|
+
}
|
|
121
|
+
>
|
|
122
|
+
{tab.label}
|
|
123
|
+
</DropdownMenuCheckboxItem>
|
|
124
|
+
))}
|
|
125
|
+
</DropdownMenuContent>
|
|
126
|
+
</DropdownMenuPortal>
|
|
127
|
+
</DropdownMenu>
|
|
128
|
+
)}
|
|
129
|
+
</div>
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function ChevronDownIcon({ className }: { className?: string }) {
|
|
134
|
+
return (
|
|
135
|
+
<svg
|
|
136
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
137
|
+
viewBox="0 0 24 24"
|
|
138
|
+
fill="none"
|
|
139
|
+
stroke="currentColor"
|
|
140
|
+
strokeWidth={2}
|
|
141
|
+
strokeLinecap="round"
|
|
142
|
+
strokeLinejoin="round"
|
|
143
|
+
className={className}
|
|
144
|
+
aria-hidden="true"
|
|
145
|
+
>
|
|
146
|
+
<path d="m6 9 6 6 6-6" />
|
|
147
|
+
</svg>
|
|
148
|
+
);
|
|
149
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import React, { useRef, useState } from "react";
|
|
2
|
+
import { Info, Pencil } from "lucide-react";
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
|
+
import { colorMixSwatch } from "@/lib/colors";
|
|
5
|
+
import { Input } from "./input";
|
|
6
|
+
import {
|
|
7
|
+
Tooltip,
|
|
8
|
+
TooltipContent,
|
|
9
|
+
TooltipProvider,
|
|
10
|
+
TooltipTrigger,
|
|
11
|
+
} from "./tooltip";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* A calculator-section row showing a color swatch, label, optional info
|
|
15
|
+
* tooltip, a money value, and an optional pencil edit trigger.
|
|
16
|
+
*
|
|
17
|
+
* Two modes:
|
|
18
|
+
*
|
|
19
|
+
* **Display + external callback** (`onEdit` only):
|
|
20
|
+
* Shows a formatted string and calls `onEdit` when the pencil is clicked.
|
|
21
|
+
* The parent handles the edit flow (e.g. opens a drawer).
|
|
22
|
+
*
|
|
23
|
+
* **Inline edit** (`numericValue` + `onValueChange`):
|
|
24
|
+
* Shows a formatted value in display state. Clicking the pencil (or the
|
|
25
|
+
* value itself) switches the row to an `<Input>` field. Committing via
|
|
26
|
+
* blur or Enter saves the value and returns to display state.
|
|
27
|
+
* `value` is used as the display-mode formatted string; `numericValue` /
|
|
28
|
+
* `onValueChange` drive the input.
|
|
29
|
+
*
|
|
30
|
+
* The pencil slot is always reserved (invisible when no edit prop) so value
|
|
31
|
+
* text stays column-aligned in mixed read-only / editable row groups.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
export interface EditableMoneyItemProps {
|
|
35
|
+
/** Row label (e.g. "Current Expenses") */
|
|
36
|
+
label: string;
|
|
37
|
+
/** Pre-formatted display string (e.g. "$5,000") */
|
|
38
|
+
value: string;
|
|
39
|
+
/** CSS color used for the swatch border and tinted fill */
|
|
40
|
+
color: string;
|
|
41
|
+
/** Fill opacity for the swatch background (default: 0.4) */
|
|
42
|
+
fillOpacity?: number;
|
|
43
|
+
/** Optional tooltip text shown on the info icon */
|
|
44
|
+
tooltip?: string;
|
|
45
|
+
|
|
46
|
+
// ── External callback mode ──────────────────────────────────────────────────
|
|
47
|
+
/** Called when the pencil is clicked (use without numericValue / onValueChange) */
|
|
48
|
+
onEdit?: () => void;
|
|
49
|
+
|
|
50
|
+
// ── Inline edit mode ────────────────────────────────────────────────────────
|
|
51
|
+
/** Raw numeric value — enables inline edit when paired with onValueChange */
|
|
52
|
+
numericValue?: number;
|
|
53
|
+
/** Called with the new number when the inline input is committed */
|
|
54
|
+
onValueChange?: (value: number) => void;
|
|
55
|
+
|
|
56
|
+
className?: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function EditableMoneyItem({
|
|
60
|
+
label,
|
|
61
|
+
value,
|
|
62
|
+
color,
|
|
63
|
+
fillOpacity = 0.4,
|
|
64
|
+
tooltip,
|
|
65
|
+
onEdit,
|
|
66
|
+
numericValue,
|
|
67
|
+
onValueChange,
|
|
68
|
+
className,
|
|
69
|
+
}: EditableMoneyItemProps) {
|
|
70
|
+
const [editing, setEditing] = useState(false);
|
|
71
|
+
const [draft, setDraft] = useState("");
|
|
72
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
73
|
+
|
|
74
|
+
const inlineMode = numericValue !== undefined && onValueChange !== undefined;
|
|
75
|
+
const hasEditAction = inlineMode || Boolean(onEdit);
|
|
76
|
+
|
|
77
|
+
const startEditing = () => {
|
|
78
|
+
if (!inlineMode) {
|
|
79
|
+
onEdit?.();
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
setDraft(String(numericValue ?? 0));
|
|
83
|
+
setEditing(true);
|
|
84
|
+
// Focus after React re-renders
|
|
85
|
+
setTimeout(() => inputRef.current?.select(), 0);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const commitEdit = () => {
|
|
89
|
+
const parsed = Number(draft.replace(/[^0-9.-]/g, ""));
|
|
90
|
+
onValueChange?.(isNaN(parsed) ? 0 : parsed);
|
|
91
|
+
setEditing(false);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const handleKeyDown = (e: React.KeyboardEvent) => {
|
|
95
|
+
if (e.key === "Enter") commitEdit();
|
|
96
|
+
if (e.key === "Escape") setEditing(false);
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<div
|
|
101
|
+
className={cn("flex items-center justify-between gap-3 py-2", className)}
|
|
102
|
+
>
|
|
103
|
+
{/* Label side */}
|
|
104
|
+
<div className="flex min-w-0 items-center gap-2">
|
|
105
|
+
<span
|
|
106
|
+
aria-hidden
|
|
107
|
+
className="h-5 w-5 shrink-0 border"
|
|
108
|
+
style={{
|
|
109
|
+
borderColor: color,
|
|
110
|
+
backgroundColor: colorMixSwatch(color, fillOpacity),
|
|
111
|
+
}}
|
|
112
|
+
/>
|
|
113
|
+
<span className="truncate text-sm text-foreground">{label}</span>
|
|
114
|
+
{tooltip && (
|
|
115
|
+
<TooltipProvider>
|
|
116
|
+
<Tooltip>
|
|
117
|
+
<TooltipTrigger asChild>
|
|
118
|
+
<button
|
|
119
|
+
type="button"
|
|
120
|
+
aria-label="More info"
|
|
121
|
+
className="shrink-0 text-muted-foreground transition-colors hover:text-foreground"
|
|
122
|
+
>
|
|
123
|
+
<Info size={14} />
|
|
124
|
+
</button>
|
|
125
|
+
</TooltipTrigger>
|
|
126
|
+
<TooltipContent>
|
|
127
|
+
<p className="max-w-xs text-xs">{tooltip}</p>
|
|
128
|
+
</TooltipContent>
|
|
129
|
+
</Tooltip>
|
|
130
|
+
</TooltipProvider>
|
|
131
|
+
)}
|
|
132
|
+
</div>
|
|
133
|
+
|
|
134
|
+
{/* Value + pencil side */}
|
|
135
|
+
<div className="flex shrink-0 items-center gap-1">
|
|
136
|
+
{editing && inlineMode ? (
|
|
137
|
+
<Input
|
|
138
|
+
ref={inputRef}
|
|
139
|
+
autoFocus
|
|
140
|
+
value={draft}
|
|
141
|
+
onChange={(e) => setDraft(e.target.value)}
|
|
142
|
+
onBlur={commitEdit}
|
|
143
|
+
onKeyDown={handleKeyDown}
|
|
144
|
+
className="h-7 w-28 text-right text-base font-semibold tabular-nums"
|
|
145
|
+
/>
|
|
146
|
+
) : (
|
|
147
|
+
<button
|
|
148
|
+
type="button"
|
|
149
|
+
disabled={!hasEditAction}
|
|
150
|
+
onClick={inlineMode ? startEditing : undefined}
|
|
151
|
+
className={cn(
|
|
152
|
+
"text-base font-semibold tabular-nums text-foreground",
|
|
153
|
+
inlineMode && "cursor-text hover:opacity-70",
|
|
154
|
+
)}
|
|
155
|
+
>
|
|
156
|
+
{value}
|
|
157
|
+
</button>
|
|
158
|
+
)}
|
|
159
|
+
|
|
160
|
+
<button
|
|
161
|
+
type="button"
|
|
162
|
+
aria-label="Edit value"
|
|
163
|
+
onClick={startEditing}
|
|
164
|
+
className={cn(
|
|
165
|
+
"p-1 transition-colors",
|
|
166
|
+
hasEditAction
|
|
167
|
+
? "text-muted-foreground hover:text-foreground"
|
|
168
|
+
: "invisible pointer-events-none",
|
|
169
|
+
)}
|
|
170
|
+
>
|
|
171
|
+
<Pencil size={14} />
|
|
172
|
+
</button>
|
|
173
|
+
</div>
|
|
174
|
+
</div>
|
|
175
|
+
);
|
|
176
|
+
}
|