@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.
Files changed (313) hide show
  1. package/.turbo/turbo-build.log +227 -191
  2. package/CHANGELOG.md +12 -0
  3. package/dist/{chunk-2UM72RJ7.mjs → chunk-2D3HQPFN.mjs} +12 -10
  4. package/dist/chunk-2EM2FRU6.mjs +613 -0
  5. package/dist/{chunk-FH6QVUVZ.mjs → chunk-2GIYVERS.mjs} +2 -2
  6. package/dist/chunk-2P7HP7LR.mjs +68 -0
  7. package/dist/{chunk-HISNT2MG.mjs → chunk-37AE3OM5.mjs} +5 -5
  8. package/dist/{chunk-HBZLGDIN.mjs → chunk-3ERBUVHC.mjs} +169 -110
  9. package/dist/{chunk-C7CQJNMR.mjs → chunk-3VDET466.mjs} +2 -2
  10. package/dist/{chunk-462HMNO4.mjs → chunk-4MM7LHM5.mjs} +2 -2
  11. package/dist/{chunk-QMY3AZJH.mjs → chunk-4Z66LMIQ.mjs} +2 -2
  12. package/dist/{chunk-U5X52X37.mjs → chunk-57ZXILTS.mjs} +6 -6
  13. package/dist/{chunk-2A5RRQGG.mjs → chunk-5NF6T2RS.mjs} +11 -20
  14. package/dist/{chunk-3OYFOX3X.mjs → chunk-5VOTTIXF.mjs} +2 -2
  15. package/dist/{chunk-LBMRIB3G.mjs → chunk-6AJUS7VX.mjs} +1 -1
  16. package/dist/{chunk-OODBHKG7.mjs → chunk-6HIOM2HL.mjs} +7 -4
  17. package/dist/{chunk-BDYZCBRT.mjs → chunk-6QAFGZC2.mjs} +2 -2
  18. package/dist/{chunk-U4NDAF2P.mjs → chunk-6TX73WG7.mjs} +1 -1
  19. package/dist/{chunk-GD4BJDJR.mjs → chunk-7BTFGCFC.mjs} +4 -4
  20. package/dist/{chunk-FAKPBKLT.mjs → chunk-7GWRPXHD.mjs} +4 -4
  21. package/dist/{chunk-NMOI6CQD.mjs → chunk-7YI3HEBH.mjs} +5 -5
  22. package/dist/{chunk-T4BJLT57.mjs → chunk-AE7MASLF.mjs} +5 -5
  23. package/dist/{chunk-VLQZANBF.mjs → chunk-AFML43VJ.mjs} +6 -1
  24. package/dist/chunk-BBXSNDS3.mjs +260 -0
  25. package/dist/chunk-BOW7U26Y.mjs +203 -0
  26. package/dist/{chunk-34NWQURD.mjs → chunk-BS75ICOO.mjs} +2 -2
  27. package/dist/chunk-D2NSIIXG.mjs +394 -0
  28. package/dist/{chunk-3GF7OVTP.mjs → chunk-DGNHGNYH.mjs} +2 -2
  29. package/dist/{chunk-VLARHE5V.mjs → chunk-DMXYRCHM.mjs} +6 -6
  30. package/dist/{chunk-OGOYQ7BG.mjs → chunk-DQB4EPIS.mjs} +1 -1
  31. package/dist/{chunk-MIZQHHUO.mjs → chunk-FL6DZFJK.mjs} +106 -38
  32. package/dist/{chunk-I3RZS7V2.mjs → chunk-FLL633WS.mjs} +19 -33
  33. package/dist/{chunk-PBL4OQV2.mjs → chunk-FTPBQVQ6.mjs} +4 -4
  34. package/dist/chunk-FYPSTTEJ.mjs +169 -0
  35. package/dist/{chunk-6O6KD7CE.mjs → chunk-G27TSQLQ.mjs} +6 -6
  36. package/dist/{chunk-66MI7Q4B.mjs → chunk-GT3RU6GA.mjs} +2 -2
  37. package/dist/{chunk-D6ID6M4V.mjs → chunk-GTAVSBDO.mjs} +2 -2
  38. package/dist/{chunk-24FUO7TD.mjs → chunk-H6NQTIF4.mjs} +2 -2
  39. package/dist/{chunk-7DHU4VGG.mjs → chunk-HK4HUQTV.mjs} +2 -2
  40. package/dist/chunk-I4KVSZCH.mjs +101 -0
  41. package/dist/{chunk-RGVKLTLH.mjs → chunk-IKXYTCSB.mjs} +2 -2
  42. package/dist/{chunk-Y6DWJSKZ.mjs → chunk-ISUA7DSB.mjs} +1 -1
  43. package/dist/{chunk-J5UICVJS.mjs → chunk-JPGL36WQ.mjs} +2 -2
  44. package/dist/{chunk-7XJHLGUV.mjs → chunk-JTK6VJXY.mjs} +2 -2
  45. package/dist/{chunk-7YAU5CY6.mjs → chunk-JVMXMFBB.mjs} +2 -2
  46. package/dist/{chunk-IAE3F7DR.mjs → chunk-JZY6TNIS.mjs} +21 -21
  47. package/dist/{chunk-K5A5L6T2.mjs → chunk-K4KOD3KR.mjs} +12 -12
  48. package/dist/{chunk-MBON7YRJ.mjs → chunk-K5QV4TT6.mjs} +3 -3
  49. package/dist/{chunk-IHMFS7NZ.mjs → chunk-K5VHK7CM.mjs} +21 -21
  50. package/dist/{chunk-RJI6GKVF.mjs → chunk-KCWNDYPZ.mjs} +5 -5
  51. package/dist/{chunk-UFYSFDER.mjs → chunk-KFH36NKF.mjs} +1 -1
  52. package/dist/{chunk-EBXQWIYG.mjs → chunk-KLTACJ2G.mjs} +5 -5
  53. package/dist/{chunk-3TTACBDP.mjs → chunk-KWD6GANL.mjs} +4 -4
  54. package/dist/{chunk-IOJRDS6V.mjs → chunk-L4NSRQ3T.mjs} +218 -147
  55. package/dist/{chunk-GYMYRIZP.mjs → chunk-LBTHZSBT.mjs} +2 -2
  56. package/dist/{chunk-AMQZRHEZ.mjs → chunk-LQULK2E3.mjs} +5 -5
  57. package/dist/{chunk-YJG55G2H.mjs → chunk-LR6LHDP3.mjs} +5 -5
  58. package/dist/{chunk-7PV3IWCN.mjs → chunk-M4VYX2PV.mjs} +19 -1
  59. package/dist/{chunk-P76HMUI6.mjs → chunk-MDUKXXIL.mjs} +2 -2
  60. package/dist/{chunk-LV35NGVG.mjs → chunk-N6Q5IPKT.mjs} +9 -9
  61. package/dist/{chunk-DOEO3CDL.mjs → chunk-NB3ZL36B.mjs} +1 -1
  62. package/dist/{chunk-XREGSKX3.mjs → chunk-NOOEKOWY.mjs} +5 -5
  63. package/dist/{chunk-NL3ZO62D.mjs → chunk-NT4FX27K.mjs} +1 -1
  64. package/dist/{chunk-QZ4RE6NA.mjs → chunk-NTYQWVLI.mjs} +6 -6
  65. package/dist/{chunk-ERGGHC2V.mjs → chunk-OEOOYMC2.mjs} +2 -2
  66. package/dist/{chunk-4GAWMKMI.mjs → chunk-OIKBW2QD.mjs} +291 -54
  67. package/dist/{chunk-DUJTAXMH.mjs → chunk-OKTJFDPN.mjs} +6 -6
  68. package/dist/chunk-OLKMCXAR.mjs +1219 -0
  69. package/dist/{chunk-EI5F6FMT.mjs → chunk-OWFQSXVD.mjs} +3 -3
  70. package/dist/{chunk-6DZEXFNB.mjs → chunk-P2N2PEFY.mjs} +3 -3
  71. package/dist/{chunk-NSLMILBT.mjs → chunk-P7CEBZM6.mjs} +2 -2
  72. package/dist/{chunk-7S5AESZO.mjs → chunk-PNRUH7JY.mjs} +6 -6
  73. package/dist/{chunk-ZU4NV6RG.mjs → chunk-PNSYFE3K.mjs} +2 -2
  74. package/dist/{chunk-JKGDCQTZ.mjs → chunk-QTRSCVQ3.mjs} +5 -5
  75. package/dist/{chunk-ABFDMHOR.mjs → chunk-QX7IFQSF.mjs} +5 -5
  76. package/dist/{chunk-CFMQP5QS.mjs → chunk-QXKGOMUX.mjs} +6 -6
  77. package/dist/{chunk-NQPOYKAQ.mjs → chunk-R2ON6CAN.mjs} +2 -2
  78. package/dist/{chunk-DBHJ5KC3.mjs → chunk-R4HCRDU5.mjs} +1 -1
  79. package/dist/{chunk-EWRB4PAD.mjs → chunk-RCAOCHWA.mjs} +14 -14
  80. package/dist/{chunk-EFRENWEJ.mjs → chunk-RSUIPKGX.mjs} +2 -2
  81. package/dist/{chunk-DGHAXJBN.mjs → chunk-S2FKV4M5.mjs} +5 -5
  82. package/dist/{chunk-RGU7HOEC.mjs → chunk-SET2ANTY.mjs} +5 -7
  83. package/dist/chunk-SFH2NJEJ.mjs +47 -0
  84. package/dist/{chunk-6AW4KJHE.mjs → chunk-SIVYAI3M.mjs} +12 -12
  85. package/dist/{chunk-5FQIKDKP.mjs → chunk-THVO2N47.mjs} +8 -8
  86. package/dist/{chunk-JMHR3YGZ.mjs → chunk-TLAWKTSA.mjs} +3 -3
  87. package/dist/{chunk-HVY6KCCF.mjs → chunk-TOWTPLRC.mjs} +68 -72
  88. package/dist/{chunk-6JQFUE5I.mjs → chunk-UALR6JGV.mjs} +2 -2
  89. package/dist/{chunk-N6TNTQL6.mjs → chunk-UJZ4UHWI.mjs} +9 -11
  90. package/dist/{chunk-MARPPFOJ.mjs → chunk-UNACI2YK.mjs} +2 -2
  91. package/dist/{chunk-3NCUZIFP.mjs → chunk-V6XGXYCJ.mjs} +7 -7
  92. package/dist/chunk-VB5M6OZQ.mjs +57 -0
  93. package/dist/{chunk-5IS7G74I.mjs → chunk-VY5NEUP7.mjs} +6 -6
  94. package/dist/{chunk-JHJHG4GO.mjs → chunk-WE4YKBDE.mjs} +2 -2
  95. package/dist/{chunk-BKNFWEH2.mjs → chunk-WL6WVV47.mjs} +3 -3
  96. package/dist/{chunk-FWCSY2DS.mjs → chunk-WNQUEZJF.mjs} +22 -1
  97. package/dist/{chunk-2Y7YJKPE.mjs → chunk-WZ6UJCBL.mjs} +1 -1
  98. package/dist/{chunk-UMTOX62O.mjs → chunk-XYPW2XA5.mjs} +13 -10
  99. package/dist/chunk-Y2MTAVAK.mjs +34 -0
  100. package/dist/{chunk-6CR5N2JW.mjs → chunk-YCWLFG27.mjs} +6 -6
  101. package/dist/{chunk-PU4YZQXV.mjs → chunk-YE67AALL.mjs} +12 -12
  102. package/dist/{chunk-M3FV7LOK.mjs → chunk-YEWNFK5S.mjs} +6 -1
  103. package/dist/{chunk-R3VSPKNP.mjs → chunk-YIZHS72Z.mjs} +11 -12
  104. package/dist/{chunk-7PYJD5JI.mjs → chunk-ZEDMKQK2.mjs} +2 -2
  105. package/dist/{chunk-N2PT566P.mjs → chunk-ZFCDYW6N.mjs} +4 -4
  106. package/dist/chunk-ZGQIVGIN.mjs +57 -0
  107. package/dist/{chunk-Q2BGOAMG.mjs → chunk-ZKWXDQDG.mjs} +4 -4
  108. package/dist/{chunk-GHC7LLUX.mjs → chunk-ZOWL2L5J.mjs} +5 -5
  109. package/dist/components/ui/accordion.mjs +3 -3
  110. package/dist/components/ui/add-column-modal.js +2 -2
  111. package/dist/components/ui/add-column-modal.mjs +10 -10
  112. package/dist/components/ui/add-lead-modal.js +424 -82
  113. package/dist/components/ui/add-lead-modal.mjs +12 -9
  114. package/dist/components/ui/advisor-card.js +2 -2
  115. package/dist/components/ui/advisor-card.mjs +8 -8
  116. package/dist/components/ui/ai-assistant-drawer.js +2 -2
  117. package/dist/components/ui/ai-assistant-drawer.mjs +9 -9
  118. package/dist/components/ui/ai-builder.js +958 -0
  119. package/dist/components/ui/ai-builder.mjs +25 -0
  120. package/dist/components/ui/ai-conversations.js +2045 -0
  121. package/dist/components/ui/ai-conversations.mjs +41 -0
  122. package/dist/components/ui/alert-dialog.js +2 -2
  123. package/dist/components/ui/alert-dialog.mjs +5 -5
  124. package/dist/components/ui/alert.mjs +3 -3
  125. package/dist/components/ui/appointment-action-dialogs.js +19 -3
  126. package/dist/components/ui/appointment-action-dialogs.mjs +15 -14
  127. package/dist/components/ui/appointment-availability-settings.js +181 -111
  128. package/dist/components/ui/appointment-availability-settings.mjs +20 -18
  129. package/dist/components/ui/appointment-book-dialog.js +113 -24
  130. package/dist/components/ui/appointment-book-dialog.mjs +21 -20
  131. package/dist/components/ui/appointment-calendar-view.js +19 -3
  132. package/dist/components/ui/appointment-calendar-view.mjs +10 -9
  133. package/dist/components/ui/appointment-detail-sheet.js +19 -3
  134. package/dist/components/ui/appointment-detail-sheet.mjs +18 -17
  135. package/dist/components/ui/appointment-gmail-connect.js +49 -89
  136. package/dist/components/ui/appointment-gmail-connect.mjs +8 -9
  137. package/dist/components/ui/appointment-mini-card.js +2 -2
  138. package/dist/components/ui/appointment-mini-card.mjs +6 -6
  139. package/dist/components/ui/appointment-time-slot-picker.mjs +6 -6
  140. package/dist/components/ui/appointment-upcoming-card.js +19 -3
  141. package/dist/components/ui/appointment-upcoming-card.mjs +15 -14
  142. package/dist/components/ui/auth-logo.js +95 -0
  143. package/dist/components/ui/auth-logo.mjs +8 -0
  144. package/dist/components/ui/auth-page-layout.js +108 -0
  145. package/dist/components/ui/auth-page-layout.mjs +8 -0
  146. package/dist/components/ui/avatar.mjs +3 -3
  147. package/dist/components/ui/backoffice-alert-history-chart.js +2 -2
  148. package/dist/components/ui/backoffice-alert-history-chart.mjs +9 -9
  149. package/dist/components/ui/backoffice-alerts-chart.js +2 -2
  150. package/dist/components/ui/backoffice-alerts-chart.mjs +11 -11
  151. package/dist/components/ui/backoffice-connections-chart.js +2 -2
  152. package/dist/components/ui/backoffice-connections-chart.mjs +11 -11
  153. package/dist/components/ui/backoffice-contact-history-chart.js +2 -2
  154. package/dist/components/ui/backoffice-contact-history-chart.mjs +9 -9
  155. package/dist/components/ui/badge.mjs +4 -4
  156. package/dist/components/ui/borrowing-capacity-line-chart.js +145 -132
  157. package/dist/components/ui/borrowing-capacity-line-chart.mjs +9 -9
  158. package/dist/components/ui/button.js +2 -2
  159. package/dist/components/ui/button.mjs +4 -4
  160. package/dist/components/ui/calendar.js +17 -3
  161. package/dist/components/ui/calendar.mjs +6 -5
  162. package/dist/components/ui/card.mjs +3 -3
  163. package/dist/components/ui/cash-balance-line-chart.js +158 -158
  164. package/dist/components/ui/cash-balance-line-chart.mjs +9 -9
  165. package/dist/components/ui/cashflow-bar-chart.js +2 -2
  166. package/dist/components/ui/cashflow-bar-chart.mjs +9 -9
  167. package/dist/components/ui/chat-widget-primitives.js +573 -0
  168. package/dist/components/ui/chat-widget-primitives.mjs +21 -0
  169. package/dist/components/ui/chat-widget.js +1268 -0
  170. package/dist/components/ui/chat-widget.mjs +29 -0
  171. package/dist/components/ui/checkbox.mjs +3 -3
  172. package/dist/components/ui/chip.js +2 -2
  173. package/dist/components/ui/chip.mjs +6 -6
  174. package/dist/components/ui/color-picker.js +2 -2
  175. package/dist/components/ui/color-picker.mjs +7 -7
  176. package/dist/components/ui/combobox.mjs +3 -3
  177. package/dist/components/ui/data-table.js +2 -2
  178. package/dist/components/ui/data-table.mjs +12 -12
  179. package/dist/components/ui/date-picker.js +22 -6
  180. package/dist/components/ui/date-picker.mjs +9 -8
  181. package/dist/components/ui/dialog.js +2 -2
  182. package/dist/components/ui/dialog.mjs +5 -5
  183. package/dist/components/ui/document-checklist-template.js +630 -0
  184. package/dist/components/ui/document-checklist-template.mjs +15 -0
  185. package/dist/components/ui/drawer.js +2 -2
  186. package/dist/components/ui/drawer.mjs +3 -3
  187. package/dist/components/ui/dropdown-menu.mjs +3 -3
  188. package/dist/components/ui/empty.mjs +3 -3
  189. package/dist/components/ui/expense-bar-chart.js +2 -2
  190. package/dist/components/ui/expense-bar-chart.mjs +9 -9
  191. package/dist/components/ui/field.mjs +5 -5
  192. package/dist/components/ui/financial-cards.js +431 -291
  193. package/dist/components/ui/financial-cards.mjs +10 -9
  194. package/dist/components/ui/financial-drawers.js +4 -4
  195. package/dist/components/ui/financial-drawers.mjs +8 -8
  196. package/dist/components/ui/financial-primitives.mjs +3 -3
  197. package/dist/components/ui/financial-sections.js +8 -9
  198. package/dist/components/ui/financial-sections.mjs +12 -12
  199. package/dist/components/ui/form-primitives.mjs +8 -8
  200. package/dist/components/ui/income-bar-chart.js +2 -2
  201. package/dist/components/ui/income-bar-chart.mjs +9 -9
  202. package/dist/components/ui/input-group.js +2 -2
  203. package/dist/components/ui/input-group.mjs +7 -7
  204. package/dist/components/ui/input-otp.mjs +3 -3
  205. package/dist/components/ui/input.mjs +3 -3
  206. package/dist/components/ui/kanban-column.js +19 -23
  207. package/dist/components/ui/kanban-column.mjs +14 -14
  208. package/dist/components/ui/label.mjs +3 -3
  209. package/dist/components/ui/onboarding-layout.js +476 -0
  210. package/dist/components/ui/onboarding-layout.mjs +11 -0
  211. package/dist/components/ui/opportunity-card.js +2 -2
  212. package/dist/components/ui/opportunity-card.mjs +12 -12
  213. package/dist/components/ui/opportunity-edit-modals.js +22 -6
  214. package/dist/components/ui/opportunity-edit-modals.mjs +21 -20
  215. package/dist/components/ui/opportunity-summary-tab.js +991 -674
  216. package/dist/components/ui/opportunity-summary-tab.mjs +26 -26
  217. package/dist/components/ui/page-header.mjs +3 -3
  218. package/dist/components/ui/page-top-bar.mjs +3 -3
  219. package/dist/components/ui/pagination.js +2 -2
  220. package/dist/components/ui/pagination.mjs +6 -6
  221. package/dist/components/ui/password-strength-tooltip.js +197 -0
  222. package/dist/components/ui/password-strength-tooltip.mjs +11 -0
  223. package/dist/components/ui/pipeline-alerts.mjs +3 -3
  224. package/dist/components/ui/pipeline-board.js +19 -23
  225. package/dist/components/ui/pipeline-board.mjs +18 -18
  226. package/dist/components/ui/pipeline-chart.js +12 -6
  227. package/dist/components/ui/pipeline-chart.mjs +4 -3
  228. package/dist/components/ui/pipeline-dialogs.js +28 -12
  229. package/dist/components/ui/pipeline-dialogs.mjs +14 -13
  230. package/dist/components/ui/pipeline-primitives.mjs +6 -6
  231. package/dist/components/ui/popover.mjs +3 -3
  232. package/dist/components/ui/progress.mjs +3 -3
  233. package/dist/components/ui/property-cashflow-doughnut-chart.js +2 -2
  234. package/dist/components/ui/property-cashflow-doughnut-chart.mjs +9 -9
  235. package/dist/components/ui/property-debt-equity-doughnut-chart.js +2 -2
  236. package/dist/components/ui/property-debt-equity-doughnut-chart.mjs +9 -9
  237. package/dist/components/ui/property-mobile-estimate-line-chart.js +2 -2
  238. package/dist/components/ui/property-mobile-estimate-line-chart.mjs +9 -9
  239. package/dist/components/ui/radio-group.mjs +3 -3
  240. package/dist/components/ui/select.mjs +3 -3
  241. package/dist/components/ui/separator.mjs +3 -3
  242. package/dist/components/ui/sheet.mjs +3 -3
  243. package/dist/components/ui/sidebar-nav.js +6 -5
  244. package/dist/components/ui/sidebar-nav.mjs +7 -7
  245. package/dist/components/ui/skeleton.mjs +3 -3
  246. package/dist/components/ui/slider.mjs +3 -3
  247. package/dist/components/ui/sonner.mjs +2 -2
  248. package/dist/components/ui/spinner.mjs +3 -3
  249. package/dist/components/ui/stage-timeline.mjs +10 -10
  250. package/dist/components/ui/stepper.mjs +3 -3
  251. package/dist/components/ui/switch.mjs +3 -3
  252. package/dist/components/ui/table.mjs +3 -3
  253. package/dist/components/ui/tabs.mjs +3 -3
  254. package/dist/components/ui/textarea.mjs +3 -3
  255. package/dist/components/ui/toggle-group.mjs +4 -4
  256. package/dist/components/ui/toggle.mjs +3 -3
  257. package/dist/components/ui/tooltip.mjs +3 -3
  258. package/dist/components/ui/transactions-expense-categories-doughnut-chart.js +2 -2
  259. package/dist/components/ui/transactions-expense-categories-doughnut-chart.mjs +9 -9
  260. package/dist/components/ui/transactions-income-expense-bar-chart.js +2 -2
  261. package/dist/components/ui/transactions-income-expense-bar-chart.mjs +9 -9
  262. package/dist/components/ui/transactions-liabilities-breakdown-doughnut-chart.js +2 -2
  263. package/dist/components/ui/transactions-liabilities-breakdown-doughnut-chart.mjs +9 -9
  264. package/dist/components/ui/two-fa-setup-form.js +612 -0
  265. package/dist/components/ui/two-fa-setup-form.mjs +16 -0
  266. package/dist/components/ui/upload-card.js +187 -0
  267. package/dist/components/ui/upload-card.mjs +10 -0
  268. package/dist/components/ui/video-background.js +118 -0
  269. package/dist/components/ui/video-background.mjs +8 -0
  270. package/dist/index.js +12674 -9311
  271. package/dist/index.mjs +341 -245
  272. package/dist/lib/colors.mjs +1 -1
  273. package/dist/lib/theme-provider.mjs +1 -1
  274. package/dist/lib/typography.mjs +2 -2
  275. package/dist/lib/utils.js +8 -2
  276. package/dist/lib/utils.mjs +6 -4
  277. package/dist/styles.css +1 -1
  278. package/package.json +61 -1
  279. package/src/components/index.tsx +126 -1
  280. package/src/components/ui/add-lead-modal.tsx +101 -142
  281. package/src/components/ui/ai-builder.tsx +560 -0
  282. package/src/components/ui/ai-conversations.tsx +1690 -0
  283. package/src/components/ui/appointment-availability-settings.tsx +152 -101
  284. package/src/components/ui/appointment-book-dialog.tsx +138 -24
  285. package/src/components/ui/appointment-calendar-view.tsx +2 -3
  286. package/src/components/ui/appointment-gmail-connect.tsx +23 -42
  287. package/src/components/ui/auth-logo.tsx +50 -0
  288. package/src/components/ui/auth-page-layout.tsx +59 -0
  289. package/src/components/ui/borrowing-capacity-line-chart.tsx +10 -8
  290. package/src/components/ui/button.tsx +2 -2
  291. package/src/components/ui/calendar.tsx +2 -1
  292. package/src/components/ui/cash-balance-line-chart.tsx +11 -20
  293. package/src/components/ui/chart-shared.tsx +10 -0
  294. package/src/components/ui/chat-widget-primitives.tsx +336 -0
  295. package/src/components/ui/chat-widget.tsx +822 -0
  296. package/src/components/ui/document-checklist-template.tsx +264 -0
  297. package/src/components/ui/drawer.tsx +2 -2
  298. package/src/components/ui/financial-cards.tsx +176 -78
  299. package/src/components/ui/financial-drawers.tsx +2 -2
  300. package/src/components/ui/financial-sections.tsx +1 -1
  301. package/src/components/ui/kanban-column.tsx +2 -5
  302. package/src/components/ui/onboarding-layout.tsx +109 -0
  303. package/src/components/ui/opportunity-summary-tab.tsx +469 -142
  304. package/src/components/ui/password-strength-tooltip.tsx +70 -0
  305. package/src/components/ui/pipeline-chart.tsx +2 -6
  306. package/src/components/ui/sidebar-nav.tsx +1 -11
  307. package/src/components/ui/two-fa-setup-form.tsx +229 -0
  308. package/src/components/ui/upload-card.tsx +98 -0
  309. package/src/components/ui/video-background.tsx +55 -0
  310. package/src/lib/format-date.ts +26 -0
  311. package/src/lib/utils.ts +11 -0
  312. package/src/styles/styles-css.ts +1 -1
  313. 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 DAY_MAP: Record<string, number> = {
315
- Sun: 0,
316
- Mon: 1,
317
- Tue: 2,
318
- Wed: 3,
319
- Thu: 4,
320
- Fri: 5,
321
- Sat: 6,
322
- };
323
- const disabledDayOfWeek = schedule
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
- const selectedClient = clients.find((c) => c.id === clientId);
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 && meetingTypeReady && !!date && !!selectedSlot && offlineReady;
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
- {/* Meeting format */}
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.toLocaleDateString("en-AU", {
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 { Spinner } from "./spinner";
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
- state?: GmailConnectionState;
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 the user clicks the copy button next to the calendar link */
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 — mail icon + title/email + badge + connected-since + disconnect */}
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-sm text-muted-foreground">
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-destructive hover:text-destructive"
95
- onClick={onDisconnect}
75
+ className="shrink-0 gap-1.5 text-muted-foreground"
76
+ onClick={onGoToIntegrations}
96
77
  >
97
- <Unlink className="h-3.5 w-3.5" />
98
- Disconnect
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
- <ShieldCheck className="h-4 w-4 shrink-0 text-success" />
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 — only rendered when a link is available */}
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
- // Disconnected state
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">Connect your Gmail account</p>
144
+ <p className="text-base font-semibold">Gmail not connected</p>
164
145
  <p className="max-w-xs text-sm text-muted-foreground">
165
- Link your Gmail to let clients book appointments and receive
166
- confirmation emails automatically.
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
- This will allow WealthX to:
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={onConnect} className="gap-2">
183
- <Mail className="h-4 w-4" />
184
- Connect Gmail
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 d = new Date(`${iso.slice(0, 7)}-01T00:00:00`);
130
- return d.toLocaleDateString("en-US", { month: "short", year: "2-digit" });
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 d = new Date(`${iso.slice(0, 7)}-01T00:00:00`);
136
- return d.toLocaleDateString("en-US", { month: "short", year: "numeric" });
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.toLocaleDateString()}
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}