@wealthx/shadcn 1.2.1 → 1.3.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.
Files changed (247) hide show
  1. package/.turbo/turbo-build.log +203 -150
  2. package/CHANGELOG.md +29 -0
  3. package/dist/{chunk-4Y6R4WEC.mjs → chunk-2A5RRQGG.mjs} +9 -22
  4. package/dist/{chunk-TS2ZX2VS.mjs → chunk-2UM72RJ7.mjs} +11 -15
  5. package/dist/{chunk-A56YQQHG.mjs → chunk-3NCUZIFP.mjs} +2 -2
  6. package/dist/chunk-3OYFOX3X.mjs +79 -0
  7. package/dist/{chunk-RP3SQYA3.mjs → chunk-3TTACBDP.mjs} +9 -4
  8. package/dist/chunk-4GAWMKMI.mjs +710 -0
  9. package/dist/{chunk-SYOD63OZ.mjs → chunk-5FQIKDKP.mjs} +6 -6
  10. package/dist/{chunk-K3JYD4IU.mjs → chunk-5IS7G74I.mjs} +11 -4
  11. package/dist/chunk-6AW4KJHE.mjs +235 -0
  12. package/dist/chunk-6CR5N2JW.mjs +302 -0
  13. package/dist/{chunk-XIRTEFKH.mjs → chunk-6DZEXFNB.mjs} +36 -8
  14. package/dist/chunk-6O6KD7CE.mjs +271 -0
  15. package/dist/chunk-7PV3IWCN.mjs +33 -0
  16. package/dist/{chunk-SPJ5KXW7.mjs → chunk-7S5AESZO.mjs} +5 -5
  17. package/dist/{chunk-RYCLWMZ7.mjs → chunk-ABFDMHOR.mjs} +9 -7
  18. package/dist/{chunk-SWGT756Z.mjs → chunk-AMQZRHEZ.mjs} +10 -4
  19. package/dist/{chunk-WOEHFRGB.mjs → chunk-BDYZCBRT.mjs} +4 -4
  20. package/dist/{chunk-WAZD7NFU.mjs → chunk-BKNFWEH2.mjs} +6 -6
  21. package/dist/{chunk-CLIN5525.mjs → chunk-C7CQJNMR.mjs} +1 -1
  22. package/dist/{chunk-D4ILTPOG.mjs → chunk-CFMQP5QS.mjs} +5 -4
  23. package/dist/{chunk-VPBN3WOO.mjs → chunk-DGHAXJBN.mjs} +9 -7
  24. package/dist/chunk-DOEO3CDL.mjs +27 -0
  25. package/dist/{chunk-KUDCQ4FI.mjs → chunk-DUJTAXMH.mjs} +9 -4
  26. package/dist/{chunk-GGM2UYGG.mjs → chunk-EBXQWIYG.mjs} +10 -4
  27. package/dist/{chunk-PMB3A7V3.mjs → chunk-EI5F6FMT.mjs} +1 -1
  28. package/dist/chunk-EWRB4PAD.mjs +468 -0
  29. package/dist/chunk-FAKPBKLT.mjs +253 -0
  30. package/dist/chunk-FNQXOAYJ.mjs +169 -0
  31. package/dist/{chunk-4CX4SBRO.mjs → chunk-GHC7LLUX.mjs} +14 -5
  32. package/dist/chunk-HBZLGDIN.mjs +507 -0
  33. package/dist/{chunk-SIZMLSRU.mjs → chunk-HISNT2MG.mjs} +8 -6
  34. package/dist/{chunk-PR6V5XKM.mjs → chunk-HVY6KCCF.mjs} +7 -4
  35. package/dist/chunk-I3RZS7V2.mjs +136 -0
  36. package/dist/chunk-IAE3F7DR.mjs +1962 -0
  37. package/dist/{chunk-ZRO5JO3H.mjs → chunk-IHMFS7NZ.mjs} +81 -84
  38. package/dist/{chunk-PCPLO5HT.mjs → chunk-IOJRDS6V.mjs} +96 -14
  39. package/dist/{chunk-LHYCMLVA.mjs → chunk-JKGDCQTZ.mjs} +11 -4
  40. package/dist/{chunk-H45TKD34.mjs → chunk-JMHR3YGZ.mjs} +1 -1
  41. package/dist/{chunk-4MN6UQHG.mjs → chunk-K5A5L6T2.mjs} +17 -39
  42. package/dist/{chunk-CSDO6VBW.mjs → chunk-LBMRIB3G.mjs} +10 -10
  43. package/dist/chunk-LV35NGVG.mjs +272 -0
  44. package/dist/{chunk-FZIXGLMV.mjs → chunk-M3FV7LOK.mjs} +5 -12
  45. package/dist/{chunk-FMAXJ2SI.mjs → chunk-MBON7YRJ.mjs} +1 -1
  46. package/dist/chunk-MIZQHHUO.mjs +441 -0
  47. package/dist/chunk-MN5NYQCL.mjs +29 -0
  48. package/dist/chunk-NL3ZO62D.mjs +31 -0
  49. package/dist/{chunk-Q76O3RIQ.mjs → chunk-NMOI6CQD.mjs} +1 -1
  50. package/dist/{chunk-P6AM5V7O.mjs → chunk-OODBHKG7.mjs} +1 -1
  51. package/dist/chunk-PBL4OQV2.mjs +283 -0
  52. package/dist/{chunk-3WMX6KWS.mjs → chunk-PU4YZQXV.mjs} +11 -12
  53. package/dist/chunk-QMY3AZJH.mjs +80 -0
  54. package/dist/{chunk-BL3DXM2X.mjs → chunk-QZ4RE6NA.mjs} +11 -4
  55. package/dist/{chunk-VACKZOMY.mjs → chunk-R3VSPKNP.mjs} +3 -3
  56. package/dist/{chunk-OPNQAVVH.mjs → chunk-RJI6GKVF.mjs} +8 -6
  57. package/dist/{chunk-WG6JGJXB.mjs → chunk-T4BJLT57.mjs} +1 -1
  58. package/dist/chunk-U4NDAF2P.mjs +207 -0
  59. package/dist/{chunk-DOH3EHX7.mjs → chunk-U5X52X37.mjs} +1 -1
  60. package/dist/chunk-UMTOX62O.mjs +415 -0
  61. package/dist/{chunk-7MMXNK3C.mjs → chunk-VLARHE5V.mjs} +8 -6
  62. package/dist/{chunk-2I5S2AMY.mjs → chunk-XREGSKX3.mjs} +2 -2
  63. package/dist/{chunk-JNQORUPP.mjs → chunk-YJG55G2H.mjs} +14 -11
  64. package/dist/chunk-ZC45IGZO.mjs +388 -0
  65. package/dist/components/ui/add-column-modal.js +42 -14
  66. package/dist/components/ui/add-column-modal.mjs +4 -4
  67. package/dist/components/ui/add-lead-modal.js +42 -11
  68. package/dist/components/ui/add-lead-modal.mjs +3 -3
  69. package/dist/components/ui/advisor-card.js +497 -0
  70. package/dist/components/ui/advisor-card.mjs +13 -0
  71. package/dist/components/ui/ai-assistant-drawer.js +11 -10
  72. package/dist/components/ui/ai-assistant-drawer.mjs +3 -3
  73. package/dist/components/ui/alert-dialog.js +2 -2
  74. package/dist/components/ui/alert-dialog.mjs +2 -2
  75. package/dist/components/ui/appointment-action-dialogs.js +1160 -0
  76. package/dist/components/ui/appointment-action-dialogs.mjs +23 -0
  77. package/dist/components/ui/appointment-availability-settings.js +1590 -0
  78. package/dist/components/ui/appointment-availability-settings.mjs +23 -0
  79. package/dist/components/ui/appointment-book-dialog.js +1744 -0
  80. package/dist/components/ui/appointment-book-dialog.mjs +27 -0
  81. package/dist/components/ui/appointment-calendar-view.js +833 -0
  82. package/dist/components/ui/appointment-calendar-view.mjs +14 -0
  83. package/dist/components/ui/appointment-detail-sheet.js +1517 -0
  84. package/dist/components/ui/appointment-detail-sheet.mjs +24 -0
  85. package/dist/components/ui/appointment-gmail-connect.js +467 -0
  86. package/dist/components/ui/appointment-gmail-connect.mjs +14 -0
  87. package/dist/components/ui/appointment-mini-card.js +345 -0
  88. package/dist/components/ui/appointment-mini-card.mjs +11 -0
  89. package/dist/components/ui/appointment-time-slot-picker.js +311 -0
  90. package/dist/components/ui/appointment-time-slot-picker.mjs +13 -0
  91. package/dist/components/ui/appointment-upcoming-card.js +1268 -0
  92. package/dist/components/ui/appointment-upcoming-card.mjs +21 -0
  93. package/dist/components/ui/backoffice-alert-history-chart.js +11 -5
  94. package/dist/components/ui/backoffice-alert-history-chart.mjs +5 -4
  95. package/dist/components/ui/backoffice-alerts-chart.js +786 -0
  96. package/dist/components/ui/backoffice-alerts-chart.mjs +19 -0
  97. package/dist/components/ui/backoffice-connections-chart.js +817 -0
  98. package/dist/components/ui/backoffice-connections-chart.mjs +19 -0
  99. package/dist/components/ui/backoffice-contact-history-chart.js +11 -5
  100. package/dist/components/ui/backoffice-contact-history-chart.mjs +5 -4
  101. package/dist/components/ui/badge.js +6 -6
  102. package/dist/components/ui/badge.mjs +1 -1
  103. package/dist/components/ui/borrowing-capacity-line-chart.js +30 -21
  104. package/dist/components/ui/borrowing-capacity-line-chart.mjs +5 -4
  105. package/dist/components/ui/button.js +2 -2
  106. package/dist/components/ui/button.mjs +1 -1
  107. package/dist/components/ui/calendar.js +2 -2
  108. package/dist/components/ui/calendar.mjs +2 -2
  109. package/dist/components/ui/card.js +1 -1
  110. package/dist/components/ui/card.mjs +1 -1
  111. package/dist/components/ui/cash-balance-line-chart.js +31 -23
  112. package/dist/components/ui/cash-balance-line-chart.mjs +5 -4
  113. package/dist/components/ui/cashflow-bar-chart.js +12 -5
  114. package/dist/components/ui/cashflow-bar-chart.mjs +5 -4
  115. package/dist/components/ui/chip.js +97 -18
  116. package/dist/components/ui/chip.mjs +3 -2
  117. package/dist/components/ui/color-picker.js +547 -0
  118. package/dist/components/ui/color-picker.mjs +24 -0
  119. package/dist/components/ui/data-table.js +182 -129
  120. package/dist/components/ui/data-table.mjs +3 -2
  121. package/dist/components/ui/date-picker.js +48 -27
  122. package/dist/components/ui/date-picker.mjs +4 -3
  123. package/dist/components/ui/dialog.js +37 -9
  124. package/dist/components/ui/dialog.mjs +2 -2
  125. package/dist/components/ui/expense-bar-chart.js +12 -5
  126. package/dist/components/ui/expense-bar-chart.mjs +5 -4
  127. package/dist/components/ui/field.mjs +2 -2
  128. package/dist/components/ui/financial-cards.js +322 -155
  129. package/dist/components/ui/financial-cards.mjs +5 -3
  130. package/dist/components/ui/financial-drawers.js +2 -2
  131. package/dist/components/ui/financial-drawers.mjs +3 -3
  132. package/dist/components/ui/financial-sections.js +14 -10
  133. package/dist/components/ui/financial-sections.mjs +6 -5
  134. package/dist/components/ui/form-primitives.js +4 -4
  135. package/dist/components/ui/form-primitives.mjs +3 -3
  136. package/dist/components/ui/income-bar-chart.js +12 -5
  137. package/dist/components/ui/income-bar-chart.mjs +5 -4
  138. package/dist/components/ui/input-group.js +2 -2
  139. package/dist/components/ui/input-group.mjs +2 -2
  140. package/dist/components/ui/kanban-column.js +52 -44
  141. package/dist/components/ui/kanban-column.mjs +7 -5
  142. package/dist/components/ui/opportunity-card.js +52 -44
  143. package/dist/components/ui/opportunity-card.mjs +6 -4
  144. package/dist/components/ui/opportunity-edit-modals.js +1371 -1267
  145. package/dist/components/ui/opportunity-edit-modals.mjs +10 -10
  146. package/dist/components/ui/opportunity-summary-tab.js +2748 -2161
  147. package/dist/components/ui/opportunity-summary-tab.mjs +16 -16
  148. package/dist/components/ui/page-header.js +92 -0
  149. package/dist/components/ui/page-header.mjs +8 -0
  150. package/dist/components/ui/page-top-bar.js +88 -0
  151. package/dist/components/ui/page-top-bar.mjs +8 -0
  152. package/dist/components/ui/pagination.js +303 -19
  153. package/dist/components/ui/pagination.mjs +11 -4
  154. package/dist/components/ui/pipeline-board.js +209 -195
  155. package/dist/components/ui/pipeline-board.mjs +10 -8
  156. package/dist/components/ui/pipeline-dialogs.js +118 -69
  157. package/dist/components/ui/pipeline-dialogs.mjs +8 -7
  158. package/dist/components/ui/pipeline-primitives.js +6 -6
  159. package/dist/components/ui/pipeline-primitives.mjs +2 -2
  160. package/dist/components/ui/property-cashflow-doughnut-chart.js +14 -12
  161. package/dist/components/ui/property-cashflow-doughnut-chart.mjs +5 -4
  162. package/dist/components/ui/property-debt-equity-doughnut-chart.js +14 -12
  163. package/dist/components/ui/property-debt-equity-doughnut-chart.mjs +5 -4
  164. package/dist/components/ui/property-mobile-estimate-line-chart.js +16 -14
  165. package/dist/components/ui/property-mobile-estimate-line-chart.mjs +5 -4
  166. package/dist/components/ui/sidebar-nav.js +679 -0
  167. package/dist/components/ui/sidebar-nav.mjs +14 -0
  168. package/dist/components/ui/stage-timeline.js +6 -6
  169. package/dist/components/ui/stage-timeline.mjs +3 -3
  170. package/dist/components/ui/stepper.js +283 -0
  171. package/dist/components/ui/stepper.mjs +18 -0
  172. package/dist/components/ui/toggle-group.js +4 -4
  173. package/dist/components/ui/toggle-group.mjs +2 -2
  174. package/dist/components/ui/toggle.js +4 -4
  175. package/dist/components/ui/toggle.mjs +1 -1
  176. package/dist/components/ui/transactions-expense-categories-doughnut-chart.js +18 -16
  177. package/dist/components/ui/transactions-expense-categories-doughnut-chart.mjs +5 -4
  178. package/dist/components/ui/transactions-income-expense-bar-chart.js +28 -12
  179. package/dist/components/ui/transactions-income-expense-bar-chart.mjs +5 -4
  180. package/dist/components/ui/transactions-liabilities-breakdown-doughnut-chart.js +18 -16
  181. package/dist/components/ui/transactions-liabilities-breakdown-doughnut-chart.mjs +5 -4
  182. package/dist/index.js +12927 -8522
  183. package/dist/index.mjs +288 -190
  184. package/dist/lib/typography.js +10 -10
  185. package/dist/lib/typography.mjs +1 -1
  186. package/dist/styles.css +1 -1
  187. package/package.json +86 -1
  188. package/src/components/index.tsx +146 -0
  189. package/src/components/ui/add-column-modal.tsx +7 -7
  190. package/src/components/ui/add-lead-modal.tsx +6 -3
  191. package/src/components/ui/advisor-card.tsx +227 -0
  192. package/src/components/ui/ai-assistant-drawer.tsx +4 -3
  193. package/src/components/ui/appointment-action-dialogs.tsx +297 -0
  194. package/src/components/ui/appointment-availability-settings.tsx +645 -0
  195. package/src/components/ui/appointment-book-dialog.tsx +618 -0
  196. package/src/components/ui/appointment-calendar-view.tsx +510 -0
  197. package/src/components/ui/appointment-detail-sheet.tsx +415 -0
  198. package/src/components/ui/appointment-gmail-connect.tsx +188 -0
  199. package/src/components/ui/appointment-mini-card.tsx +104 -0
  200. package/src/components/ui/appointment-time-slot-picker.tsx +123 -0
  201. package/src/components/ui/appointment-upcoming-card.tsx +635 -0
  202. package/src/components/ui/backoffice-alert-history-chart.tsx +10 -2
  203. package/src/components/ui/backoffice-alerts-chart.tsx +312 -0
  204. package/src/components/ui/backoffice-connections-chart.tsx +339 -0
  205. package/src/components/ui/backoffice-contact-history-chart.tsx +10 -2
  206. package/src/components/ui/badge.tsx +12 -6
  207. package/src/components/ui/borrowing-capacity-line-chart.tsx +4 -11
  208. package/src/components/ui/button.tsx +2 -2
  209. package/src/components/ui/card.tsx +1 -1
  210. package/src/components/ui/cash-balance-line-chart.tsx +4 -23
  211. package/src/components/ui/cashflow-bar-chart.tsx +9 -2
  212. package/src/components/ui/chart-shared.tsx +4 -11
  213. package/src/components/ui/chip.tsx +23 -19
  214. package/src/components/ui/color-picker.tsx +309 -0
  215. package/src/components/ui/data-table.tsx +117 -83
  216. package/src/components/ui/date-picker.tsx +42 -37
  217. package/src/components/ui/dialog.tsx +72 -6
  218. package/src/components/ui/expense-bar-chart.tsx +11 -2
  219. package/src/components/ui/financial-cards.tsx +99 -10
  220. package/src/components/ui/income-bar-chart.tsx +11 -2
  221. package/src/components/ui/opportunity-card.tsx +10 -39
  222. package/src/components/ui/opportunity-edit-modals.tsx +98 -36
  223. package/src/components/ui/opportunity-summary-tab.tsx +548 -232
  224. package/src/components/ui/page-header.tsx +57 -0
  225. package/src/components/ui/page-top-bar.tsx +48 -0
  226. package/src/components/ui/pagination.tsx +171 -22
  227. package/src/components/ui/pipeline-board.tsx +12 -5
  228. package/src/components/ui/property-cashflow-doughnut-chart.tsx +3 -1
  229. package/src/components/ui/property-debt-equity-doughnut-chart.tsx +3 -1
  230. package/src/components/ui/property-mobile-estimate-line-chart.tsx +3 -1
  231. package/src/components/ui/sidebar-nav.tsx +516 -0
  232. package/src/components/ui/stepper.tsx +347 -0
  233. package/src/components/ui/toggle.tsx +4 -4
  234. package/src/components/ui/transactions-expense-categories-doughnut-chart.tsx +3 -1
  235. package/src/components/ui/transactions-income-expense-bar-chart.tsx +12 -9
  236. package/src/components/ui/transactions-liabilities-breakdown-doughnut-chart.tsx +3 -1
  237. package/src/lib/format-currency.ts +44 -0
  238. package/src/lib/format-date.ts +50 -0
  239. package/src/lib/opportunity-constants.ts +12 -0
  240. package/src/lib/typography.ts +11 -11
  241. package/src/styles/globals.css +36 -34
  242. package/src/styles/styles-css.ts +1 -1
  243. package/tsup.config.ts +17 -0
  244. package/dist/chunk-PG6K5XEC.mjs +0 -475
  245. package/dist/chunk-WA6O6EUR.mjs +0 -1885
  246. package/dist/chunk-WNGWBVLV.mjs +0 -148
  247. package/dist/{chunk-LLVQKSU3.mjs → chunk-GD4BJDJR.mjs} +3 -3
@@ -0,0 +1,347 @@
1
+ import * as React from "react";
2
+ import { Check } from "lucide-react";
3
+ import { cn } from "@/lib/utils";
4
+
5
+ /**
6
+ * Stepper — WealthX Design System
7
+ *
8
+ * Built from scratch (no Base UI primitive). Supports horizontal and vertical
9
+ * orientations with active, completed, and error step states.
10
+ *
11
+ * Figma: https://www.figma.com/design/9V9F0NGVsif8LGmEhVjOcT/Design-System---shadcn
12
+ *
13
+ * Composition:
14
+ * <Stepper activeStep={1}>
15
+ * <Step><StepItem label="..." description="..." /></Step>
16
+ * ...
17
+ * </Stepper>
18
+ *
19
+ * Vertical with expandable content:
20
+ * <Stepper activeStep={1} orientation="vertical">
21
+ * <Step>
22
+ * <div className="flex gap-4">
23
+ * <StepIndicator />
24
+ * <StepLabel description="...">Label</StepLabel>
25
+ * </div>
26
+ * <StepContent>...</StepContent>
27
+ * </Step>
28
+ * </Stepper>
29
+ */
30
+
31
+ // ---------------------------------------------------------------------------
32
+ // Context
33
+ // ---------------------------------------------------------------------------
34
+
35
+ interface StepperContextValue {
36
+ activeStep: number;
37
+ orientation: "horizontal" | "vertical";
38
+ totalSteps: number;
39
+ }
40
+
41
+ const StepperContext = React.createContext<StepperContextValue>({
42
+ activeStep: 0,
43
+ orientation: "horizontal",
44
+ totalSteps: 0,
45
+ });
46
+
47
+ interface StepContextValue {
48
+ index: number;
49
+ isActive: boolean;
50
+ isCompleted: boolean;
51
+ isFirst: boolean;
52
+ isLast: boolean;
53
+ }
54
+
55
+ const StepContext = React.createContext<StepContextValue>({
56
+ index: 0,
57
+ isActive: false,
58
+ isCompleted: false,
59
+ isFirst: true,
60
+ isLast: false,
61
+ });
62
+
63
+ // ---------------------------------------------------------------------------
64
+ // Stepper
65
+ // ---------------------------------------------------------------------------
66
+
67
+ export interface StepperProps {
68
+ /** 0-based index of the currently active step. */
69
+ activeStep?: number;
70
+ orientation?: "horizontal" | "vertical";
71
+ className?: string;
72
+ children: React.ReactNode;
73
+ }
74
+
75
+ function Stepper({
76
+ activeStep = 0,
77
+ orientation = "horizontal",
78
+ className,
79
+ children,
80
+ }: StepperProps) {
81
+ const totalSteps = React.Children.count(children);
82
+
83
+ return (
84
+ <StepperContext.Provider value={{ activeStep, orientation, totalSteps }}>
85
+ <div
86
+ data-slot="stepper"
87
+ className={cn(
88
+ "font-sans",
89
+ orientation === "horizontal"
90
+ ? "flex w-full items-start"
91
+ : "flex flex-col",
92
+ className,
93
+ )}
94
+ >
95
+ {React.Children.map(children, (step, index) =>
96
+ React.isValidElement(step)
97
+ ? React.cloneElement(
98
+ step as React.ReactElement<StepProps>,
99
+ { _index: index } as Partial<StepProps>,
100
+ )
101
+ : step,
102
+ )}
103
+ </div>
104
+ </StepperContext.Provider>
105
+ );
106
+ }
107
+
108
+ // ---------------------------------------------------------------------------
109
+ // Step
110
+ // ---------------------------------------------------------------------------
111
+
112
+ export interface StepProps {
113
+ /**
114
+ * Injected by `<Stepper>` via `React.cloneElement` — do not pass manually.
115
+ * @internal
116
+ */
117
+ _index?: number;
118
+ /** Mark this step as errored regardless of active state. */
119
+ error?: boolean;
120
+ className?: string;
121
+ children: React.ReactNode;
122
+ }
123
+
124
+ function Step({ _index = 0, error, className, children }: StepProps) {
125
+ const { activeStep, orientation, totalSteps } =
126
+ React.useContext(StepperContext);
127
+
128
+ const isActive = _index === activeStep;
129
+ const isCompleted = _index < activeStep;
130
+ const isFirst = _index === 0;
131
+ const isLast = _index === totalSteps - 1;
132
+
133
+ return (
134
+ <StepContext.Provider
135
+ value={{ index: _index, isActive, isCompleted, isFirst, isLast }}
136
+ >
137
+ <div
138
+ data-slot="step"
139
+ aria-current={isActive ? "step" : undefined}
140
+ data-active={isActive || undefined}
141
+ data-completed={isCompleted || undefined}
142
+ data-error={error || undefined}
143
+ className={cn(
144
+ orientation === "horizontal"
145
+ ? "flex flex-1 flex-col items-center"
146
+ : "flex flex-row gap-3",
147
+ className,
148
+ )}
149
+ >
150
+ {children}
151
+ </div>
152
+ </StepContext.Provider>
153
+ );
154
+ }
155
+
156
+ // ---------------------------------------------------------------------------
157
+ // StepIndicator — circle + connector lines
158
+ // ---------------------------------------------------------------------------
159
+
160
+ export interface StepIndicatorProps {
161
+ /** Pass `true` to render the circle in the error/destructive state. */
162
+ error?: boolean;
163
+ className?: string;
164
+ }
165
+
166
+ function StepIndicator({ error, className }: StepIndicatorProps) {
167
+ const { orientation } = React.useContext(StepperContext);
168
+ const { index, isActive, isCompleted, isFirst, isLast } =
169
+ React.useContext(StepContext);
170
+
171
+ const filled = isActive || isCompleted;
172
+
173
+ const circle = (
174
+ <div
175
+ className={cn(
176
+ "relative z-10 flex size-8 shrink-0 items-center justify-center rounded-full text-sm font-medium transition-colors",
177
+ !filled &&
178
+ !error &&
179
+ "border-2 border-border bg-background text-muted-foreground",
180
+ isActive &&
181
+ !error &&
182
+ "border-2 border-primary bg-primary text-primary-foreground",
183
+ isCompleted && !error && "bg-primary text-primary-foreground",
184
+ error &&
185
+ "border-2 border-destructive bg-destructive text-destructive-foreground",
186
+ )}
187
+ >
188
+ {isCompleted && !error ? (
189
+ <Check className="size-4" strokeWidth={2.5} />
190
+ ) : (
191
+ <span>{index + 1}</span>
192
+ )}
193
+ </div>
194
+ );
195
+
196
+ if (orientation === "horizontal") {
197
+ return (
198
+ <div
199
+ data-slot="step-indicator"
200
+ className={cn("flex w-full items-center", className)}
201
+ >
202
+ <div
203
+ className={cn(
204
+ "h-[2px] flex-1 transition-colors",
205
+ isFirst
206
+ ? "bg-transparent"
207
+ : isActive || isCompleted
208
+ ? "bg-primary"
209
+ : "bg-border",
210
+ )}
211
+ />
212
+ {circle}
213
+ <div
214
+ className={cn(
215
+ "h-[2px] flex-1 transition-colors",
216
+ isLast
217
+ ? "bg-transparent"
218
+ : isCompleted
219
+ ? "bg-primary"
220
+ : "bg-border",
221
+ )}
222
+ />
223
+ </div>
224
+ );
225
+ }
226
+
227
+ return (
228
+ <div
229
+ data-slot="step-indicator"
230
+ className={cn("flex w-8 shrink-0 flex-col items-center", className)}
231
+ >
232
+ {circle}
233
+ {!isLast && (
234
+ <div
235
+ data-slot="step-connector"
236
+ className={cn(
237
+ "mt-1 w-[2px] flex-1 transition-colors",
238
+ isActive || isCompleted ? "bg-primary" : "bg-border",
239
+ )}
240
+ />
241
+ )}
242
+ </div>
243
+ );
244
+ }
245
+
246
+ // ---------------------------------------------------------------------------
247
+ // StepLabel
248
+ // ---------------------------------------------------------------------------
249
+
250
+ export interface StepLabelProps {
251
+ children: React.ReactNode;
252
+ description?: string;
253
+ /** Pass a string to show a custom error message, or `true` to show description in error colour. */
254
+ error?: string | boolean;
255
+ className?: string;
256
+ }
257
+
258
+ function StepLabel({
259
+ children,
260
+ description,
261
+ error,
262
+ className,
263
+ }: StepLabelProps) {
264
+ const { orientation } = React.useContext(StepperContext);
265
+ const { isActive, isCompleted, isLast } = React.useContext(StepContext);
266
+
267
+ const subtext = typeof error === "string" ? error : description;
268
+
269
+ return (
270
+ <div
271
+ data-slot="step-label"
272
+ className={cn(
273
+ "flex flex-col",
274
+ orientation === "horizontal"
275
+ ? "mt-2 items-center text-center"
276
+ : cn("flex-1", !isLast && "pb-5"),
277
+ className,
278
+ )}
279
+ >
280
+ <span
281
+ className={cn(
282
+ "text-sm font-medium leading-tight transition-colors",
283
+ (isActive || isCompleted) && !error && "text-foreground",
284
+ !isActive && !isCompleted && !error && "text-muted-foreground",
285
+ error && "text-destructive",
286
+ )}
287
+ >
288
+ {children}
289
+ </span>
290
+ {subtext && (
291
+ <span
292
+ className={cn(
293
+ "mt-0.5 text-xs",
294
+ error ? "text-destructive" : "text-muted-foreground",
295
+ )}
296
+ >
297
+ {subtext}
298
+ </span>
299
+ )}
300
+ </div>
301
+ );
302
+ }
303
+
304
+ // ---------------------------------------------------------------------------
305
+ // StepContent — visible only on the active step (vertical orientation)
306
+ // ---------------------------------------------------------------------------
307
+
308
+ export interface StepContentProps {
309
+ className?: string;
310
+ children: React.ReactNode;
311
+ }
312
+
313
+ function StepContent({ className, children }: StepContentProps) {
314
+ const { isActive } = React.useContext(StepContext);
315
+
316
+ if (!isActive) return null;
317
+
318
+ return (
319
+ <div data-slot="step-content" className={cn("pb-4 pt-2", className)}>
320
+ {children}
321
+ </div>
322
+ );
323
+ }
324
+
325
+ // ---------------------------------------------------------------------------
326
+ // StepItem — convenience wrapper for horizontal steppers
327
+ // Renders StepIndicator + StepLabel in a single element.
328
+ // ---------------------------------------------------------------------------
329
+
330
+ export interface StepItemProps {
331
+ label: string;
332
+ description?: string;
333
+ error?: string | boolean;
334
+ }
335
+
336
+ function StepItem({ label, description, error }: StepItemProps) {
337
+ return (
338
+ <>
339
+ <StepIndicator error={!!error} />
340
+ <StepLabel description={description} error={error}>
341
+ {label}
342
+ </StepLabel>
343
+ </>
344
+ );
345
+ }
346
+
347
+ export { Stepper, Step, StepIndicator, StepLabel, StepContent, StepItem };
@@ -15,7 +15,7 @@ import { cn } from "@/lib/utils";
15
15
  * works correctly inside ToggleGroup where border-l is collapsed for connected items)
16
16
  */
17
17
  const toggleVariants = cva(
18
- "inline-flex items-center justify-center gap-2 text-label-medium whitespace-nowrap transition-[color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-pressed:bg-primary/10 data-pressed:inset-ring data-pressed:inset-ring-primary data-pressed:text-foreground data-pressed:hover:bg-primary/10 data-pressed:hover:text-foreground dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
18
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap transition-[color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-pressed:bg-primary/10 data-pressed:inset-ring data-pressed:inset-ring-primary data-pressed:text-foreground data-pressed:hover:bg-primary/10 data-pressed:hover:text-foreground dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
19
19
  {
20
20
  variants: {
21
21
  variant: {
@@ -24,9 +24,9 @@ const toggleVariants = cva(
24
24
  "border border-input bg-transparent hover:bg-accent hover:text-accent-foreground",
25
25
  },
26
26
  size: {
27
- default: "h-9 min-w-9 px-2",
28
- sm: "h-8 min-w-8 px-1.5 text-caption",
29
- lg: "h-10 min-w-10 px-2.5",
27
+ default: "h-9 min-w-9 px-2 text-label-large",
28
+ sm: "h-8 min-w-8 px-1.5 text-label-medium",
29
+ lg: "h-10 min-w-10 px-2.5 text-h5",
30
30
  },
31
31
  },
32
32
  defaultVariants: {
@@ -129,7 +129,9 @@ export function TransactionsExpenseCategoriesDoughnutChart({
129
129
  style={{ maxWidth: width, fontFamily }}
130
130
  >
131
131
  <CardHeader className="px-3 sm:px-6">
132
- <CardTitle className="text-sm sm:text-base">{title}</CardTitle>
132
+ <CardTitle className="text-xs font-semibold uppercase tracking-wide">
133
+ {title}
134
+ </CardTitle>
133
135
  </CardHeader>
134
136
 
135
137
  <CardContent className="px-3 sm:px-6">
@@ -16,13 +16,20 @@ import { Card, CardContent, CardHeader, CardTitle } from "./card";
16
16
  import { Empty, EmptyDescription } from "./empty";
17
17
  import { Spinner } from "./spinner";
18
18
  import { cn } from "@/lib/utils";
19
+ import { formatCurrency } from "@/lib/format-currency";
19
20
  import {
20
21
  FALLBACK_TICK,
21
22
  FALLBACK_PRIMARY,
22
23
  FALLBACK_SECONDARY,
23
24
  } from "./chart-shared";
24
25
 
25
- ChartJS.register(CategoryScale, LinearScale, BarController, BarElement, Tooltip);
26
+ ChartJS.register(
27
+ CategoryScale,
28
+ LinearScale,
29
+ BarController,
30
+ BarElement,
31
+ Tooltip,
32
+ );
26
33
 
27
34
  // ---------------------------------------------------------------------------
28
35
  // Constants
@@ -32,12 +39,6 @@ ChartJS.register(CategoryScale, LinearScale, BarController, BarElement, Tooltip)
32
39
  // Helpers
33
40
  // ---------------------------------------------------------------------------
34
41
 
35
- function formatDollar(value: number): string {
36
- return `$${value.toLocaleString(undefined, {
37
- minimumFractionDigits: 2,
38
- maximumFractionDigits: 2,
39
- })}`;
40
- }
41
42
 
42
43
  // ---------------------------------------------------------------------------
43
44
  // Types
@@ -110,7 +111,7 @@ export function TransactionsIncomeExpenseBarChart({
110
111
  color: FALLBACK_SECONDARY,
111
112
  textAlign: "left",
112
113
  // Returns array for multi-line: dollar value on line 1, blank on line 2
113
- formatter: (v: number) => [formatDollar(v), ""],
114
+ formatter: (v: number) => [formatCurrency(v, { decimals: 2 }), ""],
114
115
  },
115
116
  name: {
116
117
  anchor: "end",
@@ -173,7 +174,9 @@ export function TransactionsIncomeExpenseBarChart({
173
174
  style={{ maxWidth: width, fontFamily }}
174
175
  >
175
176
  <CardHeader className="px-3 sm:px-6">
176
- <CardTitle className="text-sm sm:text-base">{title}</CardTitle>
177
+ <CardTitle className="text-xs font-semibold uppercase tracking-wide">
178
+ {title}
179
+ </CardTitle>
177
180
  </CardHeader>
178
181
 
179
182
  <CardContent className="px-3 sm:px-6">
@@ -129,7 +129,9 @@ export function TransactionsLiabilitiesBreakdownChart({
129
129
  style={{ maxWidth: width, fontFamily }}
130
130
  >
131
131
  <CardHeader className="px-3 sm:px-6">
132
- <CardTitle className="text-sm sm:text-base">{title}</CardTitle>
132
+ <CardTitle className="text-xs font-semibold uppercase tracking-wide">
133
+ {title}
134
+ </CardTitle>
133
135
  </CardHeader>
134
136
 
135
137
  <CardContent className="px-3 sm:px-6">
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Currency formatting utilities for the WealthX design system.
3
+ * All formatters default to AUD (en-AU locale).
4
+ */
5
+
6
+ /**
7
+ * Format a number as AUD currency using Intl.NumberFormat.
8
+ *
9
+ * @param value The numeric value to format.
10
+ * @param options Optional formatting overrides.
11
+ * @param options.decimals Decimal places (default: 0).
12
+ * @param options.showSign Prefix with +/− for positive/negative (default: false).
13
+ */
14
+ export function formatCurrency(
15
+ value: number,
16
+ options?: { decimals?: number; showSign?: boolean },
17
+ ): string {
18
+ const { decimals = 0, showSign = false } = options ?? {};
19
+ const abs = Math.abs(value);
20
+ const formatted = new Intl.NumberFormat("en-AU", {
21
+ style: "currency",
22
+ currency: "AUD",
23
+ minimumFractionDigits: decimals,
24
+ maximumFractionDigits: decimals,
25
+ }).format(abs);
26
+ if (!showSign) return value < 0 ? `-${formatted}` : formatted;
27
+ if (value > 0) return `+${formatted}`;
28
+ if (value < 0) return `-${formatted}`;
29
+ return formatted;
30
+ }
31
+
32
+ /**
33
+ * Abbreviated currency: $1.2B, $3.5M, $580K, $42.
34
+ * Used primarily for chart axis ticks.
35
+ */
36
+ export function formatCurrencyAbbrev(value: number): string {
37
+ const abs = Math.abs(value);
38
+ const sign = value < 0 ? "-" : "";
39
+ if (abs >= 1_000_000_000)
40
+ return `${sign}$${(abs / 1_000_000_000).toFixed(1)}B`;
41
+ if (abs >= 1_000_000) return `${sign}$${(abs / 1_000_000).toFixed(1)}M`;
42
+ if (abs >= 1_000) return `${sign}$${(abs / 1_000).toFixed(0)}K`;
43
+ return `${sign}$${abs.toFixed(0)}`;
44
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Date formatting utilities for the WealthX design system.
3
+ * Uses date-fns for consistent, locale-independent formatting.
4
+ */
5
+ import { format, parseISO } from "date-fns";
6
+
7
+ /**
8
+ * Parse an ISO date string safely, handling both full ISO and date-only strings.
9
+ * Date-only strings ("2026-01-15") are parsed via parseISO to avoid timezone shifts.
10
+ */
11
+ function safeParse(iso: string): Date {
12
+ return parseISO(iso);
13
+ }
14
+
15
+ /**
16
+ * Format an ISO date string as "15 Jan 2026".
17
+ * Returns "—" for falsy input, raw string on parse failure.
18
+ */
19
+ export function formatDateShort(iso?: string): string {
20
+ if (!iso) return "—";
21
+ try {
22
+ return format(safeParse(iso), "dd MMM yyyy");
23
+ } catch {
24
+ return iso;
25
+ }
26
+ }
27
+
28
+ /**
29
+ * Format an ISO date string as "15 Jan" (day + month only).
30
+ * Returns raw string on parse failure.
31
+ */
32
+ export function formatDateDayMonth(iso: string): string {
33
+ try {
34
+ return format(safeParse(iso), "dd MMM");
35
+ } catch {
36
+ return iso;
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Format an ISO date string with weekday: "Wed, 15 Jan 2026".
42
+ * Returns raw string on parse failure.
43
+ */
44
+ export function formatDateWithWeekday(iso: string): string {
45
+ try {
46
+ return format(safeParse(iso), "EEE, dd MMM yyyy");
47
+ } catch {
48
+ return iso;
49
+ }
50
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Shared constants for the opportunity / loan pipeline domain.
3
+ */
4
+
5
+ /** Asset types that represent real property (map to PropertyCard). */
6
+ export const PROPERTY_ASSET_TYPES = new Set([
7
+ "Primary Residence",
8
+ "Investment Property",
9
+ "Holiday Home",
10
+ "Commercial Property",
11
+ "Rural Property",
12
+ ]);
@@ -44,12 +44,12 @@ export const TYPOGRAPHY_DISPLAY = {
44
44
 
45
45
  /** Headings H1–H6 */
46
46
  export const TYPOGRAPHY_HEADING = {
47
- h1: style(700, "2rem", "2.5rem"),
47
+ h1: style(700, "1.875rem", "2.25rem"),
48
48
  h2: style(600, "1.75rem", "2.25rem"),
49
49
  h3: style(600, "1.5rem", "2rem"),
50
- h4: style(600, "1.3125rem", "1.75rem"),
50
+ h4: style(600, "1.25rem", "1.625rem"),
51
51
  h5: style(600, "1.125rem", "1.5rem"),
52
- h6: style(600, "1rem", "1.375rem"),
52
+ h6: style(600, "0.9375rem", "1.25rem"),
53
53
  } as const;
54
54
 
55
55
  /** Body text */
@@ -62,20 +62,20 @@ export const TYPOGRAPHY_BODY = {
62
62
  /** Labels — buttons, form labels, badges */
63
63
  export const TYPOGRAPHY_LABEL = {
64
64
  large: style(600, "1rem", "1.25rem"),
65
- medium: style(600, "0.875rem", "1.125rem"),
66
- small: style(600, "0.75rem", "1rem", { letterSpacing: "0.03125rem" }),
65
+ medium: style(600, "0.9375rem", "1.25rem"),
66
+ small: style(600, "0.8125rem", "1.125rem", { letterSpacing: "0.03125rem" }),
67
67
  } as const;
68
68
 
69
- /** Button — interactive control text (weight 500 / Medium per Figma) */
69
+ /** Button — interactive control text (weight 600 / SemiBold) */
70
70
  export const TYPOGRAPHY_BUTTON = {
71
- default: style(500, "0.875rem", "1.25rem"),
72
- xs: style(500, "0.75rem", "1.25rem"),
71
+ default: style(600, "0.875rem", "1.25rem"),
72
+ xs: style(600, "0.75rem", "1.25rem"),
73
73
  } as const;
74
74
 
75
75
  /** Utility — caption, overline, code */
76
76
  export const TYPOGRAPHY_UTILITY = {
77
- caption: style(400, "0.75rem", "1rem"),
78
- overline: style(600, "0.625rem", "0.875rem", {
77
+ caption: style(500, "0.8125rem", "1.125rem"),
78
+ overline: style(600, "0.75rem", "1rem", {
79
79
  letterSpacing: "0.09375rem",
80
80
  textTransform: "uppercase",
81
81
  }),
@@ -105,7 +105,7 @@ export const TYPOGRAPHY_RESPONSIVE = {
105
105
  letterSpacing: "-0.03125rem",
106
106
  }),
107
107
  "display-medium": style(700, "2rem", "2.334rem"),
108
- h1: style(700, "1.75rem", "2.1875rem"),
108
+ h1: style(700, "1.625rem", "2rem"),
109
109
  h2: style(600, "1.5rem", "1.93rem"),
110
110
  },
111
111
  } as const;