@wealthx/shadcn 1.3.2 → 1.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +227 -191
- package/CHANGELOG.md +12 -0
- package/dist/{chunk-2UM72RJ7.mjs → chunk-2D3HQPFN.mjs} +12 -10
- package/dist/chunk-2EM2FRU6.mjs +613 -0
- package/dist/{chunk-FH6QVUVZ.mjs → chunk-2GIYVERS.mjs} +2 -2
- package/dist/chunk-2P7HP7LR.mjs +68 -0
- package/dist/{chunk-HISNT2MG.mjs → chunk-37AE3OM5.mjs} +5 -5
- package/dist/{chunk-HBZLGDIN.mjs → chunk-3ERBUVHC.mjs} +169 -110
- package/dist/{chunk-C7CQJNMR.mjs → chunk-3VDET466.mjs} +2 -2
- package/dist/{chunk-462HMNO4.mjs → chunk-4MM7LHM5.mjs} +2 -2
- package/dist/{chunk-QMY3AZJH.mjs → chunk-4Z66LMIQ.mjs} +2 -2
- package/dist/{chunk-U5X52X37.mjs → chunk-57ZXILTS.mjs} +6 -6
- package/dist/{chunk-2A5RRQGG.mjs → chunk-5NF6T2RS.mjs} +11 -20
- package/dist/{chunk-3OYFOX3X.mjs → chunk-5VOTTIXF.mjs} +2 -2
- package/dist/{chunk-LBMRIB3G.mjs → chunk-6AJUS7VX.mjs} +1 -1
- package/dist/{chunk-OODBHKG7.mjs → chunk-6HIOM2HL.mjs} +7 -4
- package/dist/{chunk-BDYZCBRT.mjs → chunk-6QAFGZC2.mjs} +2 -2
- package/dist/{chunk-U4NDAF2P.mjs → chunk-6TX73WG7.mjs} +1 -1
- package/dist/{chunk-GD4BJDJR.mjs → chunk-7BTFGCFC.mjs} +4 -4
- package/dist/{chunk-FAKPBKLT.mjs → chunk-7GWRPXHD.mjs} +4 -4
- package/dist/{chunk-NMOI6CQD.mjs → chunk-7YI3HEBH.mjs} +5 -5
- package/dist/{chunk-T4BJLT57.mjs → chunk-AE7MASLF.mjs} +5 -5
- package/dist/{chunk-VLQZANBF.mjs → chunk-AFML43VJ.mjs} +6 -1
- package/dist/chunk-BBXSNDS3.mjs +260 -0
- package/dist/chunk-BOW7U26Y.mjs +203 -0
- package/dist/{chunk-34NWQURD.mjs → chunk-BS75ICOO.mjs} +2 -2
- package/dist/chunk-D2NSIIXG.mjs +394 -0
- package/dist/{chunk-3GF7OVTP.mjs → chunk-DGNHGNYH.mjs} +2 -2
- package/dist/{chunk-VLARHE5V.mjs → chunk-DMXYRCHM.mjs} +6 -6
- package/dist/{chunk-OGOYQ7BG.mjs → chunk-DQB4EPIS.mjs} +1 -1
- package/dist/{chunk-MIZQHHUO.mjs → chunk-FL6DZFJK.mjs} +106 -38
- package/dist/{chunk-I3RZS7V2.mjs → chunk-FLL633WS.mjs} +19 -33
- package/dist/{chunk-PBL4OQV2.mjs → chunk-FTPBQVQ6.mjs} +4 -4
- package/dist/chunk-FYPSTTEJ.mjs +169 -0
- package/dist/{chunk-6O6KD7CE.mjs → chunk-G27TSQLQ.mjs} +6 -6
- package/dist/{chunk-66MI7Q4B.mjs → chunk-GT3RU6GA.mjs} +2 -2
- package/dist/{chunk-D6ID6M4V.mjs → chunk-GTAVSBDO.mjs} +2 -2
- package/dist/{chunk-24FUO7TD.mjs → chunk-H6NQTIF4.mjs} +2 -2
- package/dist/{chunk-7DHU4VGG.mjs → chunk-HK4HUQTV.mjs} +2 -2
- package/dist/chunk-I4KVSZCH.mjs +101 -0
- package/dist/{chunk-RGVKLTLH.mjs → chunk-IKXYTCSB.mjs} +2 -2
- package/dist/{chunk-Y6DWJSKZ.mjs → chunk-ISUA7DSB.mjs} +1 -1
- package/dist/{chunk-J5UICVJS.mjs → chunk-JPGL36WQ.mjs} +2 -2
- package/dist/{chunk-7XJHLGUV.mjs → chunk-JTK6VJXY.mjs} +2 -2
- package/dist/{chunk-7YAU5CY6.mjs → chunk-JVMXMFBB.mjs} +2 -2
- package/dist/{chunk-IAE3F7DR.mjs → chunk-JZY6TNIS.mjs} +21 -21
- package/dist/{chunk-K5A5L6T2.mjs → chunk-K4KOD3KR.mjs} +12 -12
- package/dist/{chunk-MBON7YRJ.mjs → chunk-K5QV4TT6.mjs} +3 -3
- package/dist/{chunk-IHMFS7NZ.mjs → chunk-K5VHK7CM.mjs} +21 -21
- package/dist/{chunk-RJI6GKVF.mjs → chunk-KCWNDYPZ.mjs} +5 -5
- package/dist/{chunk-UFYSFDER.mjs → chunk-KFH36NKF.mjs} +1 -1
- package/dist/{chunk-EBXQWIYG.mjs → chunk-KLTACJ2G.mjs} +5 -5
- package/dist/{chunk-3TTACBDP.mjs → chunk-KWD6GANL.mjs} +4 -4
- package/dist/{chunk-IOJRDS6V.mjs → chunk-L4NSRQ3T.mjs} +218 -147
- package/dist/{chunk-GYMYRIZP.mjs → chunk-LBTHZSBT.mjs} +2 -2
- package/dist/{chunk-AMQZRHEZ.mjs → chunk-LQULK2E3.mjs} +5 -5
- package/dist/{chunk-YJG55G2H.mjs → chunk-LR6LHDP3.mjs} +5 -5
- package/dist/{chunk-7PV3IWCN.mjs → chunk-M4VYX2PV.mjs} +19 -1
- package/dist/{chunk-P76HMUI6.mjs → chunk-MDUKXXIL.mjs} +2 -2
- package/dist/{chunk-LV35NGVG.mjs → chunk-N6Q5IPKT.mjs} +9 -9
- package/dist/{chunk-DOEO3CDL.mjs → chunk-NB3ZL36B.mjs} +1 -1
- package/dist/{chunk-XREGSKX3.mjs → chunk-NOOEKOWY.mjs} +5 -5
- package/dist/{chunk-NL3ZO62D.mjs → chunk-NT4FX27K.mjs} +1 -1
- package/dist/{chunk-QZ4RE6NA.mjs → chunk-NTYQWVLI.mjs} +6 -6
- package/dist/{chunk-ERGGHC2V.mjs → chunk-OEOOYMC2.mjs} +2 -2
- package/dist/{chunk-4GAWMKMI.mjs → chunk-OIKBW2QD.mjs} +291 -54
- package/dist/{chunk-DUJTAXMH.mjs → chunk-OKTJFDPN.mjs} +6 -6
- package/dist/chunk-OLKMCXAR.mjs +1219 -0
- package/dist/{chunk-EI5F6FMT.mjs → chunk-OWFQSXVD.mjs} +3 -3
- package/dist/{chunk-6DZEXFNB.mjs → chunk-P2N2PEFY.mjs} +3 -3
- package/dist/{chunk-NSLMILBT.mjs → chunk-P7CEBZM6.mjs} +2 -2
- package/dist/{chunk-7S5AESZO.mjs → chunk-PNRUH7JY.mjs} +6 -6
- package/dist/{chunk-ZU4NV6RG.mjs → chunk-PNSYFE3K.mjs} +2 -2
- package/dist/{chunk-JKGDCQTZ.mjs → chunk-QTRSCVQ3.mjs} +5 -5
- package/dist/{chunk-ABFDMHOR.mjs → chunk-QX7IFQSF.mjs} +5 -5
- package/dist/{chunk-CFMQP5QS.mjs → chunk-QXKGOMUX.mjs} +6 -6
- package/dist/{chunk-NQPOYKAQ.mjs → chunk-R2ON6CAN.mjs} +2 -2
- package/dist/{chunk-DBHJ5KC3.mjs → chunk-R4HCRDU5.mjs} +1 -1
- package/dist/{chunk-EWRB4PAD.mjs → chunk-RCAOCHWA.mjs} +14 -14
- package/dist/{chunk-EFRENWEJ.mjs → chunk-RSUIPKGX.mjs} +2 -2
- package/dist/{chunk-DGHAXJBN.mjs → chunk-S2FKV4M5.mjs} +5 -5
- package/dist/{chunk-RGU7HOEC.mjs → chunk-SET2ANTY.mjs} +5 -7
- package/dist/chunk-SFH2NJEJ.mjs +47 -0
- package/dist/{chunk-6AW4KJHE.mjs → chunk-SIVYAI3M.mjs} +12 -12
- package/dist/{chunk-5FQIKDKP.mjs → chunk-THVO2N47.mjs} +8 -8
- package/dist/{chunk-JMHR3YGZ.mjs → chunk-TLAWKTSA.mjs} +3 -3
- package/dist/{chunk-HVY6KCCF.mjs → chunk-TOWTPLRC.mjs} +68 -72
- package/dist/{chunk-6JQFUE5I.mjs → chunk-UALR6JGV.mjs} +2 -2
- package/dist/{chunk-N6TNTQL6.mjs → chunk-UJZ4UHWI.mjs} +9 -11
- package/dist/{chunk-MARPPFOJ.mjs → chunk-UNACI2YK.mjs} +2 -2
- package/dist/{chunk-3NCUZIFP.mjs → chunk-V6XGXYCJ.mjs} +7 -7
- package/dist/chunk-VB5M6OZQ.mjs +57 -0
- package/dist/{chunk-5IS7G74I.mjs → chunk-VY5NEUP7.mjs} +6 -6
- package/dist/{chunk-JHJHG4GO.mjs → chunk-WE4YKBDE.mjs} +2 -2
- package/dist/{chunk-BKNFWEH2.mjs → chunk-WL6WVV47.mjs} +3 -3
- package/dist/{chunk-FWCSY2DS.mjs → chunk-WNQUEZJF.mjs} +22 -1
- package/dist/{chunk-2Y7YJKPE.mjs → chunk-WZ6UJCBL.mjs} +1 -1
- package/dist/{chunk-UMTOX62O.mjs → chunk-XYPW2XA5.mjs} +13 -10
- package/dist/chunk-Y2MTAVAK.mjs +34 -0
- package/dist/{chunk-6CR5N2JW.mjs → chunk-YCWLFG27.mjs} +6 -6
- package/dist/{chunk-PU4YZQXV.mjs → chunk-YE67AALL.mjs} +12 -12
- package/dist/{chunk-M3FV7LOK.mjs → chunk-YEWNFK5S.mjs} +6 -1
- package/dist/{chunk-R3VSPKNP.mjs → chunk-YIZHS72Z.mjs} +11 -12
- package/dist/{chunk-7PYJD5JI.mjs → chunk-ZEDMKQK2.mjs} +2 -2
- package/dist/{chunk-N2PT566P.mjs → chunk-ZFCDYW6N.mjs} +4 -4
- package/dist/chunk-ZGQIVGIN.mjs +57 -0
- package/dist/{chunk-Q2BGOAMG.mjs → chunk-ZKWXDQDG.mjs} +4 -4
- package/dist/{chunk-GHC7LLUX.mjs → chunk-ZOWL2L5J.mjs} +5 -5
- package/dist/components/ui/accordion.mjs +3 -3
- package/dist/components/ui/add-column-modal.js +2 -2
- package/dist/components/ui/add-column-modal.mjs +10 -10
- package/dist/components/ui/add-lead-modal.js +424 -82
- package/dist/components/ui/add-lead-modal.mjs +12 -9
- package/dist/components/ui/advisor-card.js +2 -2
- package/dist/components/ui/advisor-card.mjs +8 -8
- package/dist/components/ui/ai-assistant-drawer.js +2 -2
- package/dist/components/ui/ai-assistant-drawer.mjs +9 -9
- package/dist/components/ui/ai-builder.js +958 -0
- package/dist/components/ui/ai-builder.mjs +25 -0
- package/dist/components/ui/ai-conversations.js +2045 -0
- package/dist/components/ui/ai-conversations.mjs +41 -0
- package/dist/components/ui/alert-dialog.js +2 -2
- package/dist/components/ui/alert-dialog.mjs +5 -5
- package/dist/components/ui/alert.mjs +3 -3
- package/dist/components/ui/appointment-action-dialogs.js +19 -3
- package/dist/components/ui/appointment-action-dialogs.mjs +15 -14
- package/dist/components/ui/appointment-availability-settings.js +181 -111
- package/dist/components/ui/appointment-availability-settings.mjs +20 -18
- package/dist/components/ui/appointment-book-dialog.js +113 -24
- package/dist/components/ui/appointment-book-dialog.mjs +21 -20
- package/dist/components/ui/appointment-calendar-view.js +19 -3
- package/dist/components/ui/appointment-calendar-view.mjs +10 -9
- package/dist/components/ui/appointment-detail-sheet.js +19 -3
- package/dist/components/ui/appointment-detail-sheet.mjs +18 -17
- package/dist/components/ui/appointment-gmail-connect.js +49 -89
- package/dist/components/ui/appointment-gmail-connect.mjs +8 -9
- package/dist/components/ui/appointment-mini-card.js +2 -2
- package/dist/components/ui/appointment-mini-card.mjs +6 -6
- package/dist/components/ui/appointment-time-slot-picker.mjs +6 -6
- package/dist/components/ui/appointment-upcoming-card.js +19 -3
- package/dist/components/ui/appointment-upcoming-card.mjs +15 -14
- package/dist/components/ui/auth-logo.js +95 -0
- package/dist/components/ui/auth-logo.mjs +8 -0
- package/dist/components/ui/auth-page-layout.js +108 -0
- package/dist/components/ui/auth-page-layout.mjs +8 -0
- package/dist/components/ui/avatar.mjs +3 -3
- package/dist/components/ui/backoffice-alert-history-chart.js +2 -2
- package/dist/components/ui/backoffice-alert-history-chart.mjs +9 -9
- package/dist/components/ui/backoffice-alerts-chart.js +2 -2
- package/dist/components/ui/backoffice-alerts-chart.mjs +11 -11
- package/dist/components/ui/backoffice-connections-chart.js +2 -2
- package/dist/components/ui/backoffice-connections-chart.mjs +11 -11
- package/dist/components/ui/backoffice-contact-history-chart.js +2 -2
- package/dist/components/ui/backoffice-contact-history-chart.mjs +9 -9
- package/dist/components/ui/badge.mjs +4 -4
- package/dist/components/ui/borrowing-capacity-line-chart.js +145 -132
- package/dist/components/ui/borrowing-capacity-line-chart.mjs +9 -9
- package/dist/components/ui/button.js +2 -2
- package/dist/components/ui/button.mjs +4 -4
- package/dist/components/ui/calendar.js +17 -3
- package/dist/components/ui/calendar.mjs +6 -5
- package/dist/components/ui/card.mjs +3 -3
- package/dist/components/ui/cash-balance-line-chart.js +158 -158
- package/dist/components/ui/cash-balance-line-chart.mjs +9 -9
- package/dist/components/ui/cashflow-bar-chart.js +2 -2
- package/dist/components/ui/cashflow-bar-chart.mjs +9 -9
- package/dist/components/ui/chat-widget-primitives.js +573 -0
- package/dist/components/ui/chat-widget-primitives.mjs +21 -0
- package/dist/components/ui/chat-widget.js +1268 -0
- package/dist/components/ui/chat-widget.mjs +29 -0
- package/dist/components/ui/checkbox.mjs +3 -3
- package/dist/components/ui/chip.js +2 -2
- package/dist/components/ui/chip.mjs +6 -6
- package/dist/components/ui/color-picker.js +2 -2
- package/dist/components/ui/color-picker.mjs +7 -7
- package/dist/components/ui/combobox.mjs +3 -3
- package/dist/components/ui/data-table.js +2 -2
- package/dist/components/ui/data-table.mjs +12 -12
- package/dist/components/ui/date-picker.js +22 -6
- package/dist/components/ui/date-picker.mjs +9 -8
- package/dist/components/ui/dialog.js +2 -2
- package/dist/components/ui/dialog.mjs +5 -5
- package/dist/components/ui/document-checklist-template.js +630 -0
- package/dist/components/ui/document-checklist-template.mjs +15 -0
- package/dist/components/ui/drawer.js +2 -2
- package/dist/components/ui/drawer.mjs +3 -3
- package/dist/components/ui/dropdown-menu.mjs +3 -3
- package/dist/components/ui/empty.mjs +3 -3
- package/dist/components/ui/expense-bar-chart.js +2 -2
- package/dist/components/ui/expense-bar-chart.mjs +9 -9
- package/dist/components/ui/field.mjs +5 -5
- package/dist/components/ui/financial-cards.js +431 -291
- package/dist/components/ui/financial-cards.mjs +10 -9
- package/dist/components/ui/financial-drawers.js +4 -4
- package/dist/components/ui/financial-drawers.mjs +8 -8
- package/dist/components/ui/financial-primitives.mjs +3 -3
- package/dist/components/ui/financial-sections.js +8 -9
- package/dist/components/ui/financial-sections.mjs +12 -12
- package/dist/components/ui/form-primitives.mjs +8 -8
- package/dist/components/ui/income-bar-chart.js +2 -2
- package/dist/components/ui/income-bar-chart.mjs +9 -9
- package/dist/components/ui/input-group.js +2 -2
- package/dist/components/ui/input-group.mjs +7 -7
- package/dist/components/ui/input-otp.mjs +3 -3
- package/dist/components/ui/input.mjs +3 -3
- package/dist/components/ui/kanban-column.js +19 -23
- package/dist/components/ui/kanban-column.mjs +14 -14
- package/dist/components/ui/label.mjs +3 -3
- package/dist/components/ui/onboarding-layout.js +476 -0
- package/dist/components/ui/onboarding-layout.mjs +11 -0
- package/dist/components/ui/opportunity-card.js +2 -2
- package/dist/components/ui/opportunity-card.mjs +12 -12
- package/dist/components/ui/opportunity-edit-modals.js +22 -6
- package/dist/components/ui/opportunity-edit-modals.mjs +21 -20
- package/dist/components/ui/opportunity-summary-tab.js +991 -674
- package/dist/components/ui/opportunity-summary-tab.mjs +26 -26
- package/dist/components/ui/page-header.mjs +3 -3
- package/dist/components/ui/page-top-bar.mjs +3 -3
- package/dist/components/ui/pagination.js +2 -2
- package/dist/components/ui/pagination.mjs +6 -6
- package/dist/components/ui/password-strength-tooltip.js +197 -0
- package/dist/components/ui/password-strength-tooltip.mjs +11 -0
- package/dist/components/ui/pipeline-alerts.mjs +3 -3
- package/dist/components/ui/pipeline-board.js +19 -23
- package/dist/components/ui/pipeline-board.mjs +18 -18
- package/dist/components/ui/pipeline-chart.js +12 -6
- package/dist/components/ui/pipeline-chart.mjs +4 -3
- package/dist/components/ui/pipeline-dialogs.js +28 -12
- package/dist/components/ui/pipeline-dialogs.mjs +14 -13
- package/dist/components/ui/pipeline-primitives.mjs +6 -6
- package/dist/components/ui/popover.mjs +3 -3
- package/dist/components/ui/progress.mjs +3 -3
- package/dist/components/ui/property-cashflow-doughnut-chart.js +2 -2
- package/dist/components/ui/property-cashflow-doughnut-chart.mjs +9 -9
- package/dist/components/ui/property-debt-equity-doughnut-chart.js +2 -2
- package/dist/components/ui/property-debt-equity-doughnut-chart.mjs +9 -9
- package/dist/components/ui/property-mobile-estimate-line-chart.js +2 -2
- package/dist/components/ui/property-mobile-estimate-line-chart.mjs +9 -9
- package/dist/components/ui/radio-group.mjs +3 -3
- package/dist/components/ui/select.mjs +3 -3
- package/dist/components/ui/separator.mjs +3 -3
- package/dist/components/ui/sheet.mjs +3 -3
- package/dist/components/ui/sidebar-nav.js +6 -5
- package/dist/components/ui/sidebar-nav.mjs +7 -7
- package/dist/components/ui/skeleton.mjs +3 -3
- package/dist/components/ui/slider.mjs +3 -3
- package/dist/components/ui/sonner.mjs +2 -2
- package/dist/components/ui/spinner.mjs +3 -3
- package/dist/components/ui/stage-timeline.mjs +10 -10
- package/dist/components/ui/stepper.mjs +3 -3
- package/dist/components/ui/switch.mjs +3 -3
- package/dist/components/ui/table.mjs +3 -3
- package/dist/components/ui/tabs.mjs +3 -3
- package/dist/components/ui/textarea.mjs +3 -3
- package/dist/components/ui/toggle-group.mjs +4 -4
- package/dist/components/ui/toggle.mjs +3 -3
- package/dist/components/ui/tooltip.mjs +3 -3
- package/dist/components/ui/transactions-expense-categories-doughnut-chart.js +2 -2
- package/dist/components/ui/transactions-expense-categories-doughnut-chart.mjs +9 -9
- package/dist/components/ui/transactions-income-expense-bar-chart.js +2 -2
- package/dist/components/ui/transactions-income-expense-bar-chart.mjs +9 -9
- package/dist/components/ui/transactions-liabilities-breakdown-doughnut-chart.js +2 -2
- package/dist/components/ui/transactions-liabilities-breakdown-doughnut-chart.mjs +9 -9
- package/dist/components/ui/two-fa-setup-form.js +612 -0
- package/dist/components/ui/two-fa-setup-form.mjs +16 -0
- package/dist/components/ui/upload-card.js +187 -0
- package/dist/components/ui/upload-card.mjs +10 -0
- package/dist/components/ui/video-background.js +118 -0
- package/dist/components/ui/video-background.mjs +8 -0
- package/dist/index.js +12674 -9311
- package/dist/index.mjs +341 -245
- package/dist/lib/colors.mjs +1 -1
- package/dist/lib/theme-provider.mjs +1 -1
- package/dist/lib/typography.mjs +2 -2
- package/dist/lib/utils.js +8 -2
- package/dist/lib/utils.mjs +6 -4
- package/dist/styles.css +1 -1
- package/package.json +61 -1
- package/src/components/index.tsx +126 -1
- package/src/components/ui/add-lead-modal.tsx +101 -142
- package/src/components/ui/ai-builder.tsx +560 -0
- package/src/components/ui/ai-conversations.tsx +1690 -0
- package/src/components/ui/appointment-availability-settings.tsx +152 -101
- package/src/components/ui/appointment-book-dialog.tsx +138 -24
- package/src/components/ui/appointment-calendar-view.tsx +2 -3
- package/src/components/ui/appointment-gmail-connect.tsx +23 -42
- package/src/components/ui/auth-logo.tsx +50 -0
- package/src/components/ui/auth-page-layout.tsx +59 -0
- package/src/components/ui/borrowing-capacity-line-chart.tsx +10 -8
- package/src/components/ui/button.tsx +2 -2
- package/src/components/ui/calendar.tsx +2 -1
- package/src/components/ui/cash-balance-line-chart.tsx +11 -20
- package/src/components/ui/chart-shared.tsx +10 -0
- package/src/components/ui/chat-widget-primitives.tsx +336 -0
- package/src/components/ui/chat-widget.tsx +822 -0
- package/src/components/ui/document-checklist-template.tsx +264 -0
- package/src/components/ui/drawer.tsx +2 -2
- package/src/components/ui/financial-cards.tsx +176 -78
- package/src/components/ui/financial-drawers.tsx +2 -2
- package/src/components/ui/financial-sections.tsx +1 -1
- package/src/components/ui/kanban-column.tsx +2 -5
- package/src/components/ui/onboarding-layout.tsx +109 -0
- package/src/components/ui/opportunity-summary-tab.tsx +469 -142
- package/src/components/ui/password-strength-tooltip.tsx +70 -0
- package/src/components/ui/pipeline-chart.tsx +2 -6
- package/src/components/ui/sidebar-nav.tsx +1 -11
- package/src/components/ui/two-fa-setup-form.tsx +229 -0
- package/src/components/ui/upload-card.tsx +98 -0
- package/src/components/ui/video-background.tsx +55 -0
- package/src/lib/format-date.ts +26 -0
- package/src/lib/utils.ts +11 -0
- package/src/styles/styles-css.ts +1 -1
- package/tsup.config.ts +13 -0
|
@@ -27,6 +27,7 @@ import { Toggle } from "./toggle";
|
|
|
27
27
|
import { Badge } from "./badge";
|
|
28
28
|
import { Avatar, AvatarFallback } from "./avatar";
|
|
29
29
|
import { MapPin, Phone, Video } from "lucide-react";
|
|
30
|
+
import { formatDateLong } from "@/lib/format-date";
|
|
30
31
|
import {
|
|
31
32
|
AppointmentSlotSection,
|
|
32
33
|
type AppointmentMeetingFormat,
|
|
@@ -58,6 +59,20 @@ export type AppointmentBookingSlot = AppointmentTimeSlot;
|
|
|
58
59
|
|
|
59
60
|
export type AppointmentOfflineLocation = "office" | "home" | "custom";
|
|
60
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Guest identity for **client / public booking mode**.
|
|
64
|
+
*
|
|
65
|
+
* - **Authenticated user** (e.g. app.wealthx.au): pass all three fields →
|
|
66
|
+
* the name/email/phone form is hidden and the values are submitted silently.
|
|
67
|
+
* - **Public booking** (unauthenticated): omit this prop or leave fields
|
|
68
|
+
* empty → the form is shown so the guest can fill in their details.
|
|
69
|
+
*/
|
|
70
|
+
export interface AppointmentGuestInfo {
|
|
71
|
+
name?: string;
|
|
72
|
+
email?: string;
|
|
73
|
+
phone?: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
61
76
|
export interface AppointmentBookDialogProps {
|
|
62
77
|
open: boolean;
|
|
63
78
|
onOpenChange: (v: boolean) => void;
|
|
@@ -79,6 +94,11 @@ export interface AppointmentBookDialogProps {
|
|
|
79
94
|
meetingTypes?: string[];
|
|
80
95
|
amSlots: AppointmentTimeSlot[];
|
|
81
96
|
pmSlots: AppointmentTimeSlot[];
|
|
97
|
+
/**
|
|
98
|
+
* Fired when the user selects a different date in the calendar.
|
|
99
|
+
* Use this to fetch fresh `amSlots`/`pmSlots` for the new date.
|
|
100
|
+
*/
|
|
101
|
+
onDateChange?: (date: Date) => void;
|
|
82
102
|
/**
|
|
83
103
|
* Advisor's weekly availability schedule. Days with `enabled: false` are
|
|
84
104
|
* disabled in the date picker so clients cannot select them.
|
|
@@ -104,6 +124,17 @@ export interface AppointmentBookDialogProps {
|
|
|
104
124
|
* Used when rebooking from a cancelled appointment via `AppointmentDetailSheet`.
|
|
105
125
|
*/
|
|
106
126
|
initialClientId?: string;
|
|
127
|
+
/**
|
|
128
|
+
* Guest identity for **client / public booking mode** only.
|
|
129
|
+
* When all fields are provided the guest form is hidden; when any field is
|
|
130
|
+
* missing the guest must fill in their own details.
|
|
131
|
+
* Ignored in advisor mode.
|
|
132
|
+
*
|
|
133
|
+
* @remarks Mount-time initialiser only — state is seeded from this prop on
|
|
134
|
+
* first render. Subsequent prop changes are ignored. Use the `key` prop to
|
|
135
|
+
* force a full reset when `guestInfo` changes.
|
|
136
|
+
*/
|
|
137
|
+
guestInfo?: AppointmentGuestInfo;
|
|
107
138
|
onBook?: (data: {
|
|
108
139
|
/** Empty string in client mode */
|
|
109
140
|
clientId: string;
|
|
@@ -116,6 +147,12 @@ export interface AppointmentBookDialogProps {
|
|
|
116
147
|
offlineLocation?: AppointmentOfflineLocation;
|
|
117
148
|
customAddress?: string;
|
|
118
149
|
callPhone?: string;
|
|
150
|
+
/** Client mode only — name of the guest/user making the booking */
|
|
151
|
+
guestName?: string;
|
|
152
|
+
/** Client mode only — email of the guest/user */
|
|
153
|
+
guestEmail?: string;
|
|
154
|
+
/** Client mode only — phone of the guest/user (optional) */
|
|
155
|
+
guestPhone?: string;
|
|
119
156
|
}) => void;
|
|
120
157
|
}
|
|
121
158
|
|
|
@@ -291,6 +328,20 @@ function MeetingFormatSection({
|
|
|
291
328
|
);
|
|
292
329
|
}
|
|
293
330
|
|
|
331
|
+
// ---------------------------------------------------------------------------
|
|
332
|
+
// Constants
|
|
333
|
+
// ---------------------------------------------------------------------------
|
|
334
|
+
|
|
335
|
+
const DAY_MAP: Record<string, number> = {
|
|
336
|
+
Sun: 0,
|
|
337
|
+
Mon: 1,
|
|
338
|
+
Tue: 2,
|
|
339
|
+
Wed: 3,
|
|
340
|
+
Thu: 4,
|
|
341
|
+
Fri: 5,
|
|
342
|
+
Sat: 6,
|
|
343
|
+
};
|
|
344
|
+
|
|
294
345
|
// ---------------------------------------------------------------------------
|
|
295
346
|
// Main component
|
|
296
347
|
// ---------------------------------------------------------------------------
|
|
@@ -302,28 +353,27 @@ export function AppointmentBookDialog({
|
|
|
302
353
|
meetingTypes = [],
|
|
303
354
|
amSlots,
|
|
304
355
|
pmSlots,
|
|
356
|
+
onDateChange,
|
|
305
357
|
schedule,
|
|
306
358
|
advisorOfficeAddress,
|
|
307
359
|
clientHomeAddress,
|
|
308
360
|
advisorInfo,
|
|
309
361
|
initialClientId,
|
|
362
|
+
guestInfo,
|
|
310
363
|
onBook,
|
|
311
364
|
}: AppointmentBookDialogProps) {
|
|
312
365
|
const isClientMode = clients.length === 0;
|
|
313
|
-
|
|
314
|
-
const
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
?.filter((d) => !d.enabled)
|
|
325
|
-
.map((d) => DAY_MAP[d.day])
|
|
326
|
-
.filter((n): n is number => n !== undefined);
|
|
366
|
+
// Guest form is shown in client mode when at least one identity field is missing.
|
|
367
|
+
const showGuestForm = isClientMode && !(guestInfo?.name && guestInfo?.email);
|
|
368
|
+
|
|
369
|
+
const disabledDayOfWeek = React.useMemo(
|
|
370
|
+
() =>
|
|
371
|
+
schedule
|
|
372
|
+
?.filter((d) => !d.enabled)
|
|
373
|
+
.map((d) => DAY_MAP[d.day])
|
|
374
|
+
.filter((n): n is number => n !== undefined),
|
|
375
|
+
[schedule],
|
|
376
|
+
);
|
|
327
377
|
|
|
328
378
|
const [clientId, setClientId] = React.useState<string | undefined>(undefined);
|
|
329
379
|
const [meetingType, setMeetingType] = React.useState("");
|
|
@@ -343,7 +393,15 @@ export function AppointmentBookDialog({
|
|
|
343
393
|
undefined,
|
|
344
394
|
);
|
|
345
395
|
|
|
346
|
-
|
|
396
|
+
// Guest identity — client / public booking mode only
|
|
397
|
+
const [guestName, setGuestName] = React.useState(guestInfo?.name ?? "");
|
|
398
|
+
const [guestEmail, setGuestEmail] = React.useState(guestInfo?.email ?? "");
|
|
399
|
+
const [guestPhone, setGuestPhone] = React.useState(guestInfo?.phone ?? "");
|
|
400
|
+
|
|
401
|
+
const selectedClient = React.useMemo(
|
|
402
|
+
() => clients.find((c) => c.id === clientId),
|
|
403
|
+
[clients, clientId],
|
|
404
|
+
);
|
|
347
405
|
|
|
348
406
|
// Pre-select client when dialog opens with an initialClientId (e.g. rebook after cancellation)
|
|
349
407
|
React.useEffect(() => {
|
|
@@ -368,9 +426,16 @@ export function AppointmentBookDialog({
|
|
|
368
426
|
meetingFormat !== "offline" ||
|
|
369
427
|
offlineLocation !== "custom" ||
|
|
370
428
|
!!customAddress.trim();
|
|
429
|
+
const guestReady =
|
|
430
|
+
!showGuestForm || (!!guestName.trim() && !!guestEmail.trim());
|
|
371
431
|
|
|
372
432
|
const canSubmit =
|
|
373
|
-
clientReady &&
|
|
433
|
+
clientReady &&
|
|
434
|
+
meetingTypeReady &&
|
|
435
|
+
!!date &&
|
|
436
|
+
!!selectedSlot &&
|
|
437
|
+
offlineReady &&
|
|
438
|
+
guestReady;
|
|
374
439
|
|
|
375
440
|
const handleOpenChange = (next: boolean) => {
|
|
376
441
|
if (!next) {
|
|
@@ -383,6 +448,9 @@ export function AppointmentBookDialog({
|
|
|
383
448
|
setSelectedSlot(undefined);
|
|
384
449
|
setNotes("");
|
|
385
450
|
setSelectedPhone(undefined);
|
|
451
|
+
setGuestName(guestInfo?.name ?? "");
|
|
452
|
+
setGuestEmail(guestInfo?.email ?? "");
|
|
453
|
+
setGuestPhone(guestInfo?.phone ?? "");
|
|
386
454
|
}
|
|
387
455
|
onOpenChange(next);
|
|
388
456
|
};
|
|
@@ -421,7 +489,6 @@ export function AppointmentBookDialog({
|
|
|
421
489
|
<Separator />
|
|
422
490
|
|
|
423
491
|
<div className="flex flex-col gap-4">
|
|
424
|
-
{/* Client search — advisor mode only */}
|
|
425
492
|
{!isClientMode && (
|
|
426
493
|
<div className="flex flex-col gap-1.5">
|
|
427
494
|
<Label>Client</Label>
|
|
@@ -433,7 +500,6 @@ export function AppointmentBookDialog({
|
|
|
433
500
|
</div>
|
|
434
501
|
)}
|
|
435
502
|
|
|
436
|
-
{/* Appointment type — shown only when meetingTypes provided */}
|
|
437
503
|
{meetingTypes.length > 0 && (
|
|
438
504
|
<div className="flex flex-col gap-1.5">
|
|
439
505
|
<Label htmlFor="book-apt-type">
|
|
@@ -454,7 +520,49 @@ export function AppointmentBookDialog({
|
|
|
454
520
|
</div>
|
|
455
521
|
)}
|
|
456
522
|
|
|
457
|
-
{/*
|
|
523
|
+
{/* Guest identity — public booking only */}
|
|
524
|
+
{showGuestForm && (
|
|
525
|
+
<>
|
|
526
|
+
<div className="flex flex-col gap-1.5">
|
|
527
|
+
<Label htmlFor="guest-name">Full name</Label>
|
|
528
|
+
<Input
|
|
529
|
+
id="guest-name"
|
|
530
|
+
placeholder="Your full name"
|
|
531
|
+
value={guestName}
|
|
532
|
+
onChange={(e) => setGuestName(e.target.value)}
|
|
533
|
+
autoComplete="name"
|
|
534
|
+
/>
|
|
535
|
+
</div>
|
|
536
|
+
<div className="flex flex-col gap-1.5">
|
|
537
|
+
<Label htmlFor="guest-email">Email</Label>
|
|
538
|
+
<Input
|
|
539
|
+
id="guest-email"
|
|
540
|
+
type="email"
|
|
541
|
+
placeholder="your@email.com"
|
|
542
|
+
value={guestEmail}
|
|
543
|
+
onChange={(e) => setGuestEmail(e.target.value)}
|
|
544
|
+
autoComplete="email"
|
|
545
|
+
/>
|
|
546
|
+
</div>
|
|
547
|
+
<div className="flex flex-col gap-1.5">
|
|
548
|
+
<Label htmlFor="guest-phone">
|
|
549
|
+
Phone{" "}
|
|
550
|
+
<span className="font-normal text-muted-foreground">
|
|
551
|
+
(optional)
|
|
552
|
+
</span>
|
|
553
|
+
</Label>
|
|
554
|
+
<Input
|
|
555
|
+
id="guest-phone"
|
|
556
|
+
type="tel"
|
|
557
|
+
placeholder="+61 4XX XXX XXX"
|
|
558
|
+
value={guestPhone}
|
|
559
|
+
onChange={(e) => setGuestPhone(e.target.value)}
|
|
560
|
+
autoComplete="tel"
|
|
561
|
+
/>
|
|
562
|
+
</div>
|
|
563
|
+
</>
|
|
564
|
+
)}
|
|
565
|
+
|
|
458
566
|
<div className="flex flex-col gap-1.5">
|
|
459
567
|
<Label>Meeting format</Label>
|
|
460
568
|
<MeetingFormatSection
|
|
@@ -515,6 +623,7 @@ export function AppointmentBookDialog({
|
|
|
515
623
|
onSelect={(d) => {
|
|
516
624
|
setDate(d);
|
|
517
625
|
setSelectedSlot(undefined);
|
|
626
|
+
if (d) onDateChange?.(d);
|
|
518
627
|
}}
|
|
519
628
|
captionLayout="label"
|
|
520
629
|
fromDate={new Date()}
|
|
@@ -531,11 +640,7 @@ export function AppointmentBookDialog({
|
|
|
531
640
|
{date ? (
|
|
532
641
|
<div className="flex flex-col gap-3">
|
|
533
642
|
<p className="text-xs text-muted-foreground">
|
|
534
|
-
{date
|
|
535
|
-
weekday: "long",
|
|
536
|
-
day: "numeric",
|
|
537
|
-
month: "long",
|
|
538
|
-
})}
|
|
643
|
+
{formatDateLong(date)}
|
|
539
644
|
</p>
|
|
540
645
|
<AppointmentSlotSection
|
|
541
646
|
label="Morning"
|
|
@@ -604,6 +709,15 @@ export function AppointmentBookDialog({
|
|
|
604
709
|
: undefined,
|
|
605
710
|
callPhone:
|
|
606
711
|
meetingFormat === "call" ? selectedPhone : undefined,
|
|
712
|
+
guestName: isClientMode
|
|
713
|
+
? guestName.trim() || undefined
|
|
714
|
+
: undefined,
|
|
715
|
+
guestEmail: isClientMode
|
|
716
|
+
? guestEmail.trim() || undefined
|
|
717
|
+
: undefined,
|
|
718
|
+
guestPhone: isClientMode
|
|
719
|
+
? guestPhone.trim() || undefined
|
|
720
|
+
: undefined,
|
|
607
721
|
});
|
|
608
722
|
handleOpenChange(false);
|
|
609
723
|
}
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
CalendarClock,
|
|
14
14
|
} from "lucide-react";
|
|
15
15
|
import type { AppointmentStatus } from "./appointment-time-slot-picker";
|
|
16
|
+
import { formatWeekdayShort } from "@/lib/format-date";
|
|
16
17
|
|
|
17
18
|
// Re-export so consumers who import AppointmentStatus from this module still work
|
|
18
19
|
export type { AppointmentStatus } from "./appointment-time-slot-picker";
|
|
@@ -137,9 +138,7 @@ function DayView({
|
|
|
137
138
|
onSelectAppointment?: (apt: AppointmentCalendarItem) => void;
|
|
138
139
|
}) {
|
|
139
140
|
const d = new Date(date + "T00:00:00");
|
|
140
|
-
const dayLabel = d
|
|
141
|
-
.toLocaleDateString("en-AU", { weekday: "short" })
|
|
142
|
-
.toUpperCase();
|
|
141
|
+
const dayLabel = formatWeekdayShort(d);
|
|
143
142
|
const dayShort = d.getDate();
|
|
144
143
|
const isToday = date === today;
|
|
145
144
|
const dayApts = appointments.filter((a) => a.date === date);
|
|
@@ -2,15 +2,12 @@ import { Badge } from "./badge";
|
|
|
2
2
|
import { Button } from "./button";
|
|
3
3
|
import { Input } from "./input";
|
|
4
4
|
import { Separator } from "./separator";
|
|
5
|
-
import {
|
|
6
|
-
import { Check, Copy, Link2, Mail, ShieldCheck, Unlink } from "lucide-react";
|
|
5
|
+
import { Check, Copy, ExternalLink, Link2, Mail, Settings } from "lucide-react";
|
|
7
6
|
|
|
8
7
|
// ---------------------------------------------------------------------------
|
|
9
8
|
// Types
|
|
10
9
|
// ---------------------------------------------------------------------------
|
|
11
10
|
|
|
12
|
-
export type GmailConnectionState = "disconnected" | "connecting" | "connected";
|
|
13
|
-
|
|
14
11
|
export interface GmailConnection {
|
|
15
12
|
connected: boolean;
|
|
16
13
|
email?: string;
|
|
@@ -19,14 +16,14 @@ export interface GmailConnection {
|
|
|
19
16
|
}
|
|
20
17
|
|
|
21
18
|
export interface AppointmentGmailConnectProps {
|
|
19
|
+
/** Global Gmail connection state sourced from Settings > Integrations */
|
|
22
20
|
connection: GmailConnection;
|
|
23
|
-
|
|
24
|
-
onConnect?: () => void;
|
|
25
|
-
onDisconnect?: () => void;
|
|
26
|
-
/** Booking link to display in the calendar link section (connected state only) */
|
|
21
|
+
/** Booking link to display when connected */
|
|
27
22
|
calendarLink?: string;
|
|
28
|
-
/** Called when
|
|
23
|
+
/** Called when user clicks the copy button next to the calendar link */
|
|
29
24
|
onCopyLink?: () => void;
|
|
25
|
+
/** Called when user clicks "Go to Integrations" in the not-connected state */
|
|
26
|
+
onGoToIntegrations?: () => void;
|
|
30
27
|
}
|
|
31
28
|
|
|
32
29
|
// ---------------------------------------------------------------------------
|
|
@@ -45,30 +42,14 @@ const PERMISSIONS = [
|
|
|
45
42
|
|
|
46
43
|
export function AppointmentGmailConnect({
|
|
47
44
|
connection,
|
|
48
|
-
state = "disconnected",
|
|
49
|
-
onConnect,
|
|
50
|
-
onDisconnect,
|
|
51
45
|
calendarLink,
|
|
52
46
|
onCopyLink,
|
|
47
|
+
onGoToIntegrations,
|
|
53
48
|
}: AppointmentGmailConnectProps) {
|
|
54
|
-
if (state === "connecting") {
|
|
55
|
-
return (
|
|
56
|
-
<div className="flex flex-col items-center gap-4 border border-border bg-card px-6 py-8 text-center">
|
|
57
|
-
<Spinner className="h-6 w-6 text-primary" />
|
|
58
|
-
<div className="flex flex-col gap-1">
|
|
59
|
-
<p className="text-sm font-medium">Connecting to Gmail…</p>
|
|
60
|
-
<p className="text-xs text-muted-foreground">
|
|
61
|
-
Complete the sign-in in the popup window.
|
|
62
|
-
</p>
|
|
63
|
-
</div>
|
|
64
|
-
</div>
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
49
|
if (connection.connected) {
|
|
69
50
|
return (
|
|
70
51
|
<div className="flex flex-col border border-border bg-card">
|
|
71
|
-
{/* Header
|
|
52
|
+
{/* Header */}
|
|
72
53
|
<div className="flex items-center gap-3 px-5 py-4">
|
|
73
54
|
<div className="flex h-10 w-10 shrink-0 items-center justify-center bg-primary/10">
|
|
74
55
|
<Mail className="h-5 w-5 text-primary" />
|
|
@@ -84,18 +65,18 @@ export function AppointmentGmailConnect({
|
|
|
84
65
|
Active
|
|
85
66
|
</Badge>
|
|
86
67
|
{connection.connectedAt && (
|
|
87
|
-
<p className="shrink-0 text-
|
|
68
|
+
<p className="shrink-0 text-xs text-muted-foreground">
|
|
88
69
|
Connected since {connection.connectedAt}
|
|
89
70
|
</p>
|
|
90
71
|
)}
|
|
91
72
|
<Button
|
|
92
73
|
size="sm"
|
|
93
74
|
variant="ghost"
|
|
94
|
-
className="shrink-0 gap-1.5 text-
|
|
95
|
-
onClick={
|
|
75
|
+
className="shrink-0 gap-1.5 text-muted-foreground"
|
|
76
|
+
onClick={onGoToIntegrations}
|
|
96
77
|
>
|
|
97
|
-
<
|
|
98
|
-
|
|
78
|
+
<Settings className="h-3.5 w-3.5" />
|
|
79
|
+
Manage
|
|
99
80
|
</Button>
|
|
100
81
|
</div>
|
|
101
82
|
|
|
@@ -108,13 +89,13 @@ export function AppointmentGmailConnect({
|
|
|
108
89
|
</p>
|
|
109
90
|
{PERMISSIONS.map((perm) => (
|
|
110
91
|
<div key={perm} className="flex items-center gap-2">
|
|
111
|
-
<
|
|
92
|
+
<Check className="h-3.5 w-3.5 shrink-0 text-success" />
|
|
112
93
|
<span className="text-sm text-muted-foreground">{perm}</span>
|
|
113
94
|
</div>
|
|
114
95
|
))}
|
|
115
96
|
</div>
|
|
116
97
|
|
|
117
|
-
{/* Calendar appointment link
|
|
98
|
+
{/* Calendar appointment link */}
|
|
118
99
|
{calendarLink && (
|
|
119
100
|
<>
|
|
120
101
|
<Separator />
|
|
@@ -152,7 +133,7 @@ export function AppointmentGmailConnect({
|
|
|
152
133
|
);
|
|
153
134
|
}
|
|
154
135
|
|
|
155
|
-
//
|
|
136
|
+
// Not connected — direct user to Integrations instead of a second OAuth flow
|
|
156
137
|
return (
|
|
157
138
|
<div className="flex flex-col items-center gap-4 border border-border bg-card px-6 py-8 text-center">
|
|
158
139
|
<div className="flex h-12 w-12 items-center justify-center bg-muted">
|
|
@@ -160,16 +141,16 @@ export function AppointmentGmailConnect({
|
|
|
160
141
|
</div>
|
|
161
142
|
|
|
162
143
|
<div className="flex flex-col gap-1.5">
|
|
163
|
-
<p className="text-base font-semibold">
|
|
144
|
+
<p className="text-base font-semibold">Gmail not connected</p>
|
|
164
145
|
<p className="max-w-xs text-sm text-muted-foreground">
|
|
165
|
-
|
|
166
|
-
|
|
146
|
+
Connect your Gmail account in Integrations to enable calendar booking
|
|
147
|
+
and appointment confirmations.
|
|
167
148
|
</p>
|
|
168
149
|
</div>
|
|
169
150
|
|
|
170
151
|
<div className="flex w-full max-w-xs flex-col gap-2 text-left">
|
|
171
152
|
<p className="text-sm font-medium text-muted-foreground">
|
|
172
|
-
|
|
153
|
+
Once connected, WealthX will be able to:
|
|
173
154
|
</p>
|
|
174
155
|
{PERMISSIONS.map((perm) => (
|
|
175
156
|
<div key={perm} className="flex items-center gap-2">
|
|
@@ -179,9 +160,9 @@ export function AppointmentGmailConnect({
|
|
|
179
160
|
))}
|
|
180
161
|
</div>
|
|
181
162
|
|
|
182
|
-
<Button onClick={
|
|
183
|
-
<
|
|
184
|
-
|
|
163
|
+
<Button onClick={onGoToIntegrations} className="gap-2">
|
|
164
|
+
<ExternalLink className="h-4 w-4" />
|
|
165
|
+
Go to Integrations
|
|
185
166
|
</Button>
|
|
186
167
|
</div>
|
|
187
168
|
);
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "@/lib/utils";
|
|
3
|
+
|
|
4
|
+
export type AuthLogoProps = {
|
|
5
|
+
logoUrl?: string;
|
|
6
|
+
wealthxLogoUrl?: string;
|
|
7
|
+
className?: string;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export function AuthLogo({
|
|
11
|
+
logoUrl,
|
|
12
|
+
wealthxLogoUrl,
|
|
13
|
+
className,
|
|
14
|
+
}: AuthLogoProps) {
|
|
15
|
+
const wealthxMark = wealthxLogoUrl ? (
|
|
16
|
+
<img src={wealthxLogoUrl} alt="WealthX" className="h-5 w-auto" />
|
|
17
|
+
) : (
|
|
18
|
+
<span className="text-sm font-semibold tracking-tight text-foreground">
|
|
19
|
+
WealthX
|
|
20
|
+
</span>
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
if (!logoUrl) {
|
|
24
|
+
return (
|
|
25
|
+
<div className={cn("flex items-center justify-center", className)}>
|
|
26
|
+
{wealthxLogoUrl ? (
|
|
27
|
+
<img src={wealthxLogoUrl} alt="WealthX" className="h-8 w-auto" />
|
|
28
|
+
) : (
|
|
29
|
+
<span className="text-lg font-semibold tracking-tight text-foreground">
|
|
30
|
+
WealthX
|
|
31
|
+
</span>
|
|
32
|
+
)}
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<div className={cn("flex items-center gap-3", className)}>
|
|
39
|
+
<img
|
|
40
|
+
src={logoUrl}
|
|
41
|
+
alt="Partner logo"
|
|
42
|
+
className="h-8 w-auto object-contain"
|
|
43
|
+
/>
|
|
44
|
+
<div className="flex items-center gap-1.5">
|
|
45
|
+
<span className="text-xs text-muted-foreground">Powered by</span>
|
|
46
|
+
{wealthxMark}
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "@/lib/utils";
|
|
3
|
+
|
|
4
|
+
export type AuthPageLayoutProps = {
|
|
5
|
+
logo?: React.ReactNode;
|
|
6
|
+
title?: string;
|
|
7
|
+
subtitle?: string;
|
|
8
|
+
wide?: boolean;
|
|
9
|
+
className?: string;
|
|
10
|
+
children: React.ReactNode;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export function AuthPageLayout({
|
|
14
|
+
logo,
|
|
15
|
+
title,
|
|
16
|
+
subtitle,
|
|
17
|
+
wide = false,
|
|
18
|
+
className,
|
|
19
|
+
children,
|
|
20
|
+
}: AuthPageLayoutProps) {
|
|
21
|
+
return (
|
|
22
|
+
<div
|
|
23
|
+
className={cn(
|
|
24
|
+
"relative flex min-h-screen w-full flex-col items-center justify-center px-10 py-16 font-sans",
|
|
25
|
+
className,
|
|
26
|
+
)}
|
|
27
|
+
>
|
|
28
|
+
{logo && (
|
|
29
|
+
<div className="absolute top-8 left-1/2 -translate-x-1/2">{logo}</div>
|
|
30
|
+
)}
|
|
31
|
+
|
|
32
|
+
<div
|
|
33
|
+
className={cn(
|
|
34
|
+
"flex w-full flex-col items-center gap-10",
|
|
35
|
+
wide ? "max-w-full" : "max-w-[581px]",
|
|
36
|
+
)}
|
|
37
|
+
>
|
|
38
|
+
{(title || subtitle) && (
|
|
39
|
+
<div className="flex w-[320px] flex-col items-center gap-2 text-center">
|
|
40
|
+
{title && (
|
|
41
|
+
<h1 className="m-0 text-[30px] font-semibold leading-9 text-card-foreground">
|
|
42
|
+
{title}
|
|
43
|
+
</h1>
|
|
44
|
+
)}
|
|
45
|
+
{subtitle && (
|
|
46
|
+
<p className="m-0 text-sm font-normal leading-5 text-muted-foreground">
|
|
47
|
+
{subtitle}
|
|
48
|
+
</p>
|
|
49
|
+
)}
|
|
50
|
+
</div>
|
|
51
|
+
)}
|
|
52
|
+
|
|
53
|
+
<div className={cn("w-full", wide ? "max-w-full" : "w-[320px]")}>
|
|
54
|
+
{children}
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
@@ -12,10 +12,6 @@ import {
|
|
|
12
12
|
} from "chart.js";
|
|
13
13
|
import { Chart } from "react-chartjs-2";
|
|
14
14
|
import { useThemeVars } from "@/lib/theme-provider";
|
|
15
|
-
import { Card, CardContent, CardHeader, CardTitle } from "./card";
|
|
16
|
-
import { Empty, EmptyDescription } from "./empty";
|
|
17
|
-
import { Skeleton } from "./skeleton";
|
|
18
|
-
import { cn } from "@/lib/utils";
|
|
19
15
|
import {
|
|
20
16
|
FALLBACK_TICK,
|
|
21
17
|
FALLBACK_PRIMARY,
|
|
@@ -24,7 +20,13 @@ import {
|
|
|
24
20
|
FALLBACK_GRID_COLOR,
|
|
25
21
|
ChartLegendItem,
|
|
26
22
|
formatAbbrev,
|
|
23
|
+
formatMonthLabel,
|
|
24
|
+
formatTooltipDate,
|
|
27
25
|
} from "./chart-shared";
|
|
26
|
+
import { Card, CardContent, CardHeader, CardTitle } from "./card";
|
|
27
|
+
import { Empty, EmptyDescription } from "./empty";
|
|
28
|
+
import { Skeleton } from "./skeleton";
|
|
29
|
+
import { cn } from "@/lib/utils";
|
|
28
30
|
import { formatCurrency } from "@/lib/format-currency";
|
|
29
31
|
|
|
30
32
|
ChartJS.register(
|
|
@@ -126,14 +128,14 @@ const DASH_PATTERN: number[] = [6, 4];
|
|
|
126
128
|
|
|
127
129
|
/** "2025-01" or "2025-01-15" → "Jan '25" for x-axis ticks */
|
|
128
130
|
function formatDateLabel(iso: string): string {
|
|
129
|
-
const
|
|
130
|
-
return
|
|
131
|
+
const normalized = `${iso.slice(0, 7)}-01T00:00:00`;
|
|
132
|
+
return formatMonthLabel(normalized);
|
|
131
133
|
}
|
|
132
134
|
|
|
133
135
|
/** "2025-01" → "Jan 2025" for tooltip header */
|
|
134
136
|
function formatDateTooltip(iso: string): string {
|
|
135
|
-
const
|
|
136
|
-
return
|
|
137
|
+
const normalized = `${iso.slice(0, 7)}-01T00:00:00`;
|
|
138
|
+
return formatTooltipDate(normalized, "monthly");
|
|
137
139
|
}
|
|
138
140
|
|
|
139
141
|
// ---------------------------------------------------------------------------
|
|
@@ -23,11 +23,11 @@ const buttonVariants = cva(
|
|
|
23
23
|
destructive:
|
|
24
24
|
"bg-destructive text-destructive-foreground shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40",
|
|
25
25
|
outline:
|
|
26
|
-
"border border-input bg-background shadow-xs hover:bg-accent hover:text-accent-foreground focus-visible:ring-border/50 dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
|
|
26
|
+
"border border-input bg-background shadow-xs hover:bg-accent hover:text-accent-foreground focus-visible:border-border focus-visible:ring-border/50 dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
|
|
27
27
|
"outline-primary":
|
|
28
28
|
"border border-primary text-foreground bg-transparent shadow-xs hover:bg-primary/5 focus-visible:ring-primary/50",
|
|
29
29
|
"outline-secondary":
|
|
30
|
-
"border border-brand-secondary text-brand-secondary bg-transparent shadow-xs hover:bg-brand-secondary/10 focus-visible:ring-brand-secondary/30",
|
|
30
|
+
"border border-brand-secondary text-brand-secondary bg-transparent shadow-xs hover:bg-brand-secondary/10 focus-visible:border-brand-secondary focus-visible:ring-brand-secondary/30",
|
|
31
31
|
ghost:
|
|
32
32
|
"hover:bg-accent hover:text-accent-foreground hover:shadow-xs focus-visible:ring-border/50 dark:hover:bg-accent/50",
|
|
33
33
|
link: "text-primary underline-offset-4 hover:underline",
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
type DayButton,
|
|
12
12
|
} from "react-day-picker";
|
|
13
13
|
import { cn } from "@/lib/utils";
|
|
14
|
+
import { formatDateShort } from "@/lib/format-date";
|
|
14
15
|
import { Button, buttonVariants } from "@/components/ui/button";
|
|
15
16
|
|
|
16
17
|
/**
|
|
@@ -222,7 +223,7 @@ function CalendarDayButton({
|
|
|
222
223
|
defaultClassNames.day,
|
|
223
224
|
className,
|
|
224
225
|
)}
|
|
225
|
-
data-day={day.date.
|
|
226
|
+
data-day={formatDateShort(day.date.toISOString())}
|
|
226
227
|
data-range-end={modifiers.range_end}
|
|
227
228
|
data-range-middle={modifiers.range_middle}
|
|
228
229
|
data-range-start={modifiers.range_start}
|