@wealthx/shadcn 1.2.2 → 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 (229) hide show
  1. package/.turbo/turbo-build.log +200 -156
  2. package/CHANGELOG.md +22 -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-VGSESELX.mjs → chunk-5FQIKDKP.mjs} +5 -5
  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-WAZD7NFU.mjs → chunk-BKNFWEH2.mjs} +6 -6
  20. package/dist/{chunk-CLIN5525.mjs → chunk-C7CQJNMR.mjs} +1 -1
  21. package/dist/{chunk-D4ILTPOG.mjs → chunk-CFMQP5QS.mjs} +5 -4
  22. package/dist/{chunk-VPBN3WOO.mjs → chunk-DGHAXJBN.mjs} +9 -7
  23. package/dist/chunk-DOEO3CDL.mjs +27 -0
  24. package/dist/{chunk-5MEWU56Z.mjs → chunk-DUJTAXMH.mjs} +11 -6
  25. package/dist/{chunk-GGM2UYGG.mjs → chunk-EBXQWIYG.mjs} +10 -4
  26. package/dist/chunk-EWRB4PAD.mjs +468 -0
  27. package/dist/{chunk-ZSHYDDRB.mjs → chunk-FAKPBKLT.mjs} +6 -2
  28. package/dist/chunk-FNQXOAYJ.mjs +169 -0
  29. package/dist/{chunk-A6AAWBPF.mjs → chunk-GHC7LLUX.mjs} +13 -4
  30. package/dist/chunk-HBZLGDIN.mjs +507 -0
  31. package/dist/{chunk-SIZMLSRU.mjs → chunk-HISNT2MG.mjs} +8 -6
  32. package/dist/{chunk-CGH4DRNG.mjs → chunk-HVY6KCCF.mjs} +10 -7
  33. package/dist/chunk-I3RZS7V2.mjs +136 -0
  34. package/dist/chunk-IAE3F7DR.mjs +1962 -0
  35. package/dist/{chunk-UT4KJR7V.mjs → chunk-IHMFS7NZ.mjs} +35 -74
  36. package/dist/{chunk-PCPLO5HT.mjs → chunk-IOJRDS6V.mjs} +96 -14
  37. package/dist/{chunk-LHYCMLVA.mjs → chunk-JKGDCQTZ.mjs} +11 -4
  38. package/dist/{chunk-H45TKD34.mjs → chunk-JMHR3YGZ.mjs} +1 -1
  39. package/dist/{chunk-4MN6UQHG.mjs → chunk-K5A5L6T2.mjs} +17 -39
  40. package/dist/chunk-LV35NGVG.mjs +272 -0
  41. package/dist/{chunk-FZIXGLMV.mjs → chunk-M3FV7LOK.mjs} +5 -12
  42. package/dist/{chunk-FMAXJ2SI.mjs → chunk-MBON7YRJ.mjs} +1 -1
  43. package/dist/chunk-MIZQHHUO.mjs +441 -0
  44. package/dist/chunk-MN5NYQCL.mjs +29 -0
  45. package/dist/chunk-NL3ZO62D.mjs +31 -0
  46. package/dist/{chunk-Q76O3RIQ.mjs → chunk-NMOI6CQD.mjs} +1 -1
  47. package/dist/{chunk-P6AM5V7O.mjs → chunk-OODBHKG7.mjs} +1 -1
  48. package/dist/chunk-PBL4OQV2.mjs +283 -0
  49. package/dist/{chunk-Y4QFWRNR.mjs → chunk-PU4YZQXV.mjs} +17 -18
  50. package/dist/chunk-QMY3AZJH.mjs +80 -0
  51. package/dist/{chunk-BL3DXM2X.mjs → chunk-QZ4RE6NA.mjs} +11 -4
  52. package/dist/{chunk-VACKZOMY.mjs → chunk-R3VSPKNP.mjs} +3 -3
  53. package/dist/{chunk-OPNQAVVH.mjs → chunk-RJI6GKVF.mjs} +8 -6
  54. package/dist/{chunk-WG6JGJXB.mjs → chunk-T4BJLT57.mjs} +1 -1
  55. package/dist/chunk-UMTOX62O.mjs +415 -0
  56. package/dist/{chunk-7MMXNK3C.mjs → chunk-VLARHE5V.mjs} +8 -6
  57. package/dist/{chunk-2I5S2AMY.mjs → chunk-XREGSKX3.mjs} +2 -2
  58. package/dist/{chunk-JNQORUPP.mjs → chunk-YJG55G2H.mjs} +14 -11
  59. package/dist/{chunk-ZRSDX6OW.mjs → chunk-ZC45IGZO.mjs} +33 -30
  60. package/dist/components/ui/add-column-modal.js +42 -14
  61. package/dist/components/ui/add-column-modal.mjs +5 -5
  62. package/dist/components/ui/add-lead-modal.js +42 -11
  63. package/dist/components/ui/add-lead-modal.mjs +3 -3
  64. package/dist/components/ui/advisor-card.js +497 -0
  65. package/dist/components/ui/advisor-card.mjs +13 -0
  66. package/dist/components/ui/ai-assistant-drawer.js +11 -10
  67. package/dist/components/ui/ai-assistant-drawer.mjs +3 -3
  68. package/dist/components/ui/alert-dialog.js +2 -2
  69. package/dist/components/ui/alert-dialog.mjs +2 -2
  70. package/dist/components/ui/appointment-action-dialogs.js +1160 -0
  71. package/dist/components/ui/appointment-action-dialogs.mjs +23 -0
  72. package/dist/components/ui/appointment-availability-settings.js +1590 -0
  73. package/dist/components/ui/appointment-availability-settings.mjs +23 -0
  74. package/dist/components/ui/appointment-book-dialog.js +1744 -0
  75. package/dist/components/ui/appointment-book-dialog.mjs +27 -0
  76. package/dist/components/ui/appointment-calendar-view.js +833 -0
  77. package/dist/components/ui/appointment-calendar-view.mjs +14 -0
  78. package/dist/components/ui/appointment-detail-sheet.js +1517 -0
  79. package/dist/components/ui/appointment-detail-sheet.mjs +24 -0
  80. package/dist/components/ui/appointment-gmail-connect.js +467 -0
  81. package/dist/components/ui/appointment-gmail-connect.mjs +14 -0
  82. package/dist/components/ui/appointment-mini-card.js +345 -0
  83. package/dist/components/ui/appointment-mini-card.mjs +11 -0
  84. package/dist/components/ui/appointment-time-slot-picker.js +311 -0
  85. package/dist/components/ui/appointment-time-slot-picker.mjs +13 -0
  86. package/dist/components/ui/appointment-upcoming-card.js +1268 -0
  87. package/dist/components/ui/appointment-upcoming-card.mjs +21 -0
  88. package/dist/components/ui/backoffice-alert-history-chart.js +11 -5
  89. package/dist/components/ui/backoffice-alert-history-chart.mjs +5 -4
  90. package/dist/components/ui/backoffice-alerts-chart.js +786 -0
  91. package/dist/components/ui/backoffice-alerts-chart.mjs +19 -0
  92. package/dist/components/ui/backoffice-connections-chart.js +817 -0
  93. package/dist/components/ui/backoffice-connections-chart.mjs +19 -0
  94. package/dist/components/ui/backoffice-contact-history-chart.js +11 -5
  95. package/dist/components/ui/backoffice-contact-history-chart.mjs +5 -4
  96. package/dist/components/ui/badge.js +6 -6
  97. package/dist/components/ui/badge.mjs +1 -1
  98. package/dist/components/ui/borrowing-capacity-line-chart.js +30 -21
  99. package/dist/components/ui/borrowing-capacity-line-chart.mjs +5 -4
  100. package/dist/components/ui/button.js +2 -2
  101. package/dist/components/ui/button.mjs +1 -1
  102. package/dist/components/ui/calendar.js +2 -2
  103. package/dist/components/ui/calendar.mjs +2 -2
  104. package/dist/components/ui/card.js +1 -1
  105. package/dist/components/ui/card.mjs +1 -1
  106. package/dist/components/ui/cash-balance-line-chart.js +31 -23
  107. package/dist/components/ui/cash-balance-line-chart.mjs +5 -4
  108. package/dist/components/ui/cashflow-bar-chart.js +12 -5
  109. package/dist/components/ui/cashflow-bar-chart.mjs +5 -4
  110. package/dist/components/ui/chip.js +97 -18
  111. package/dist/components/ui/chip.mjs +3 -2
  112. package/dist/components/ui/color-picker.js +158 -28
  113. package/dist/components/ui/color-picker.mjs +3 -1
  114. package/dist/components/ui/data-table.js +140 -119
  115. package/dist/components/ui/data-table.mjs +3 -2
  116. package/dist/components/ui/date-picker.js +48 -27
  117. package/dist/components/ui/date-picker.mjs +4 -3
  118. package/dist/components/ui/dialog.js +37 -9
  119. package/dist/components/ui/dialog.mjs +2 -2
  120. package/dist/components/ui/expense-bar-chart.js +12 -5
  121. package/dist/components/ui/expense-bar-chart.mjs +5 -4
  122. package/dist/components/ui/field.mjs +2 -2
  123. package/dist/components/ui/financial-cards.js +322 -155
  124. package/dist/components/ui/financial-cards.mjs +5 -3
  125. package/dist/components/ui/financial-drawers.js +2 -2
  126. package/dist/components/ui/financial-drawers.mjs +3 -3
  127. package/dist/components/ui/financial-sections.js +14 -10
  128. package/dist/components/ui/financial-sections.mjs +6 -5
  129. package/dist/components/ui/income-bar-chart.js +12 -5
  130. package/dist/components/ui/income-bar-chart.mjs +5 -4
  131. package/dist/components/ui/input-group.js +2 -2
  132. package/dist/components/ui/input-group.mjs +2 -2
  133. package/dist/components/ui/kanban-column.js +52 -44
  134. package/dist/components/ui/kanban-column.mjs +7 -5
  135. package/dist/components/ui/opportunity-card.js +52 -44
  136. package/dist/components/ui/opportunity-card.mjs +6 -4
  137. package/dist/components/ui/opportunity-edit-modals.js +1367 -1263
  138. package/dist/components/ui/opportunity-edit-modals.mjs +8 -8
  139. package/dist/components/ui/opportunity-summary-tab.js +2744 -2157
  140. package/dist/components/ui/opportunity-summary-tab.mjs +14 -14
  141. package/dist/components/ui/page-header.js +92 -0
  142. package/dist/components/ui/page-header.mjs +8 -0
  143. package/dist/components/ui/page-top-bar.js +88 -0
  144. package/dist/components/ui/page-top-bar.mjs +8 -0
  145. package/dist/components/ui/pagination.js +303 -19
  146. package/dist/components/ui/pagination.mjs +11 -4
  147. package/dist/components/ui/pipeline-board.js +205 -191
  148. package/dist/components/ui/pipeline-board.mjs +9 -7
  149. package/dist/components/ui/pipeline-dialogs.js +114 -65
  150. package/dist/components/ui/pipeline-dialogs.mjs +7 -6
  151. package/dist/components/ui/pipeline-primitives.js +6 -6
  152. package/dist/components/ui/pipeline-primitives.mjs +2 -2
  153. package/dist/components/ui/property-cashflow-doughnut-chart.js +14 -12
  154. package/dist/components/ui/property-cashflow-doughnut-chart.mjs +5 -4
  155. package/dist/components/ui/property-debt-equity-doughnut-chart.js +14 -12
  156. package/dist/components/ui/property-debt-equity-doughnut-chart.mjs +5 -4
  157. package/dist/components/ui/property-mobile-estimate-line-chart.js +16 -14
  158. package/dist/components/ui/property-mobile-estimate-line-chart.mjs +5 -4
  159. package/dist/components/ui/sidebar-nav.js +234 -95
  160. package/dist/components/ui/sidebar-nav.mjs +4 -1
  161. package/dist/components/ui/stage-timeline.js +6 -6
  162. package/dist/components/ui/stage-timeline.mjs +3 -3
  163. package/dist/components/ui/transactions-expense-categories-doughnut-chart.js +18 -16
  164. package/dist/components/ui/transactions-expense-categories-doughnut-chart.mjs +5 -4
  165. package/dist/components/ui/transactions-income-expense-bar-chart.js +28 -12
  166. package/dist/components/ui/transactions-income-expense-bar-chart.mjs +5 -4
  167. package/dist/components/ui/transactions-liabilities-breakdown-doughnut-chart.js +18 -16
  168. package/dist/components/ui/transactions-liabilities-breakdown-doughnut-chart.mjs +5 -4
  169. package/dist/index.js +12899 -9343
  170. package/dist/index.mjs +256 -190
  171. package/dist/styles.css +1 -1
  172. package/package.json +71 -1
  173. package/src/components/index.tsx +114 -9
  174. package/src/components/ui/add-column-modal.tsx +7 -7
  175. package/src/components/ui/add-lead-modal.tsx +6 -3
  176. package/src/components/ui/advisor-card.tsx +227 -0
  177. package/src/components/ui/ai-assistant-drawer.tsx +4 -3
  178. package/src/components/ui/appointment-action-dialogs.tsx +297 -0
  179. package/src/components/ui/appointment-availability-settings.tsx +645 -0
  180. package/src/components/ui/appointment-book-dialog.tsx +618 -0
  181. package/src/components/ui/appointment-calendar-view.tsx +510 -0
  182. package/src/components/ui/appointment-detail-sheet.tsx +415 -0
  183. package/src/components/ui/appointment-gmail-connect.tsx +188 -0
  184. package/src/components/ui/appointment-mini-card.tsx +104 -0
  185. package/src/components/ui/appointment-time-slot-picker.tsx +123 -0
  186. package/src/components/ui/appointment-upcoming-card.tsx +635 -0
  187. package/src/components/ui/backoffice-alert-history-chart.tsx +10 -2
  188. package/src/components/ui/backoffice-alerts-chart.tsx +312 -0
  189. package/src/components/ui/backoffice-connections-chart.tsx +339 -0
  190. package/src/components/ui/backoffice-contact-history-chart.tsx +10 -2
  191. package/src/components/ui/badge.tsx +12 -6
  192. package/src/components/ui/borrowing-capacity-line-chart.tsx +4 -11
  193. package/src/components/ui/button.tsx +2 -2
  194. package/src/components/ui/card.tsx +1 -1
  195. package/src/components/ui/cash-balance-line-chart.tsx +4 -23
  196. package/src/components/ui/cashflow-bar-chart.tsx +9 -2
  197. package/src/components/ui/chart-shared.tsx +4 -11
  198. package/src/components/ui/chip.tsx +23 -19
  199. package/src/components/ui/color-picker.tsx +4 -2
  200. package/src/components/ui/data-table.tsx +28 -74
  201. package/src/components/ui/date-picker.tsx +42 -37
  202. package/src/components/ui/dialog.tsx +72 -6
  203. package/src/components/ui/expense-bar-chart.tsx +11 -2
  204. package/src/components/ui/financial-cards.tsx +99 -10
  205. package/src/components/ui/income-bar-chart.tsx +11 -2
  206. package/src/components/ui/opportunity-card.tsx +10 -39
  207. package/src/components/ui/opportunity-edit-modals.tsx +98 -36
  208. package/src/components/ui/opportunity-summary-tab.tsx +548 -232
  209. package/src/components/ui/page-header.tsx +57 -0
  210. package/src/components/ui/page-top-bar.tsx +48 -0
  211. package/src/components/ui/pagination.tsx +171 -22
  212. package/src/components/ui/pipeline-board.tsx +12 -5
  213. package/src/components/ui/property-cashflow-doughnut-chart.tsx +3 -1
  214. package/src/components/ui/property-debt-equity-doughnut-chart.tsx +3 -1
  215. package/src/components/ui/property-mobile-estimate-line-chart.tsx +3 -1
  216. package/src/components/ui/sidebar-nav.tsx +36 -37
  217. package/src/components/ui/transactions-expense-categories-doughnut-chart.tsx +3 -1
  218. package/src/components/ui/transactions-income-expense-bar-chart.tsx +12 -9
  219. package/src/components/ui/transactions-liabilities-breakdown-doughnut-chart.tsx +3 -1
  220. package/src/lib/format-currency.ts +44 -0
  221. package/src/lib/format-date.ts +50 -0
  222. package/src/lib/opportunity-constants.ts +12 -0
  223. package/src/styles/globals.css +17 -15
  224. package/src/styles/styles-css.ts +1 -1
  225. package/tsup.config.ts +14 -0
  226. package/dist/chunk-S4QRUQNW.mjs +0 -475
  227. package/dist/chunk-URGMJAE3.mjs +0 -1885
  228. package/dist/chunk-WNGWBVLV.mjs +0 -148
  229. package/dist/{chunk-LLVQKSU3.mjs → chunk-GD4BJDJR.mjs} +3 -3
@@ -25,6 +25,7 @@ import {
25
25
  ChartLegendItem,
26
26
  formatAbbrev,
27
27
  } from "./chart-shared";
28
+ import { formatCurrency } from "@/lib/format-currency";
28
29
 
29
30
  ChartJS.register(
30
31
  CategoryScale,
@@ -123,14 +124,6 @@ const DASH_PATTERN: number[] = [6, 4];
123
124
  // Helpers
124
125
  // ---------------------------------------------------------------------------
125
126
 
126
- function formatCurrencyFull(dollars: number): string {
127
- return new Intl.NumberFormat("en-AU", {
128
- style: "currency",
129
- currency: "AUD",
130
- maximumFractionDigits: 0,
131
- }).format(dollars);
132
- }
133
-
134
127
  /** "2025-01" or "2025-01-15" → "Jan '25" for x-axis ticks */
135
128
  function formatDateLabel(iso: string): string {
136
129
  const d = new Date(`${iso.slice(0, 7)}-01T00:00:00`);
@@ -246,7 +239,7 @@ export function BorrowingCapacityLineChart({
246
239
  },
247
240
  label: (ctx) => {
248
241
  const dollars = ctx.parsed.y ?? 0;
249
- return ` ${ctx.dataset.label}: ${formatCurrencyFull(dollars)}`;
242
+ return ` ${ctx.dataset.label}: ${formatCurrency(dollars)}`;
250
243
  },
251
244
  },
252
245
  },
@@ -319,12 +312,12 @@ export function BorrowingCapacityLineChart({
319
312
  >
320
313
  <CardHeader className="px-3 sm:px-6">
321
314
  <div className="flex items-start justify-between gap-4">
322
- <CardTitle className="text-sm sm:text-base uppercase tracking-wider">
315
+ <CardTitle className="text-xs font-semibold uppercase tracking-wide">
323
316
  {title}
324
317
  </CardTitle>
325
318
  {kpiValue != null && (
326
319
  <span className="shrink-0 text-xl font-bold tabular-nums">
327
- {formatCurrencyFull(kpiValue / 100)}
320
+ {formatCurrency(kpiValue / 100)}
328
321
  </span>
329
322
  )}
330
323
  </div>
@@ -23,13 +23,13 @@ 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 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: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
30
  "border border-brand-secondary text-brand-secondary bg-transparent shadow-xs hover:bg-brand-secondary/10 focus-visible:ring-brand-secondary/30",
31
31
  ghost:
32
- "hover:bg-accent hover:text-accent-foreground hover:shadow-xs dark:hover:bg-accent/50",
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",
34
34
  },
35
35
  size: {
@@ -23,7 +23,7 @@ function CardHeader({ className, ...props }: CardHeaderProps): ReactElement {
23
23
  return (
24
24
  <div
25
25
  className={cn(
26
- "@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
26
+ "@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-center gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
27
27
  className,
28
28
  )}
29
29
  data-slot="card-header"
@@ -21,7 +21,9 @@ import {
21
21
  FALLBACK_PRIMARY,
22
22
  FALLBACK_BG,
23
23
  ChartLegendItem,
24
+ formatAbbrev,
24
25
  } from "./chart-shared";
26
+ import { formatCurrency } from "@/lib/format-currency";
25
27
 
26
28
  ChartJS.register(
27
29
  CategoryScale,
@@ -36,27 +38,6 @@ ChartJS.register(
36
38
  // Helpers
37
39
  // ---------------------------------------------------------------------------
38
40
 
39
- /** Abbreviate a dollar value: $1.2K, $4.5M, $1.1B */
40
- function formatAbbrev(value: number): string {
41
- const abs = Math.abs(value);
42
- const sign = value < 0 ? "-" : "";
43
- if (abs >= 1_000_000_000)
44
- return `${sign}$${(abs / 1_000_000_000).toFixed(1)}B`;
45
- if (abs >= 1_000_000) return `${sign}$${(abs / 1_000_000).toFixed(1)}M`;
46
- if (abs >= 1_000) return `${sign}$${(abs / 1_000).toFixed(1)}K`;
47
- return `${sign}$${abs.toFixed(0)}`;
48
- }
49
-
50
- /** Format a balance value for prominent display: $12,345.67 */
51
- function formatBalanceFull(value: number): string {
52
- const abs = Math.abs(value);
53
- const sign = value < 0 ? "-" : "";
54
- return `${sign}$${abs.toLocaleString(undefined, {
55
- minimumFractionDigits: 2,
56
- maximumFractionDigits: 2,
57
- })}`;
58
- }
59
-
60
41
  /** Format ISO date string to "MMM dd" label e.g. "Jan 15" */
61
42
  function formatDateLabel(iso: string): string {
62
43
  const d = new Date(iso);
@@ -244,12 +225,12 @@ export function CashBalanceLineChart({
244
225
  >
245
226
  <CardHeader className="px-3 sm:px-6">
246
227
  <div className="flex flex-col gap-0.5">
247
- <CardTitle className="text-sm sm:text-base uppercase tracking-wider">
228
+ <CardTitle className="text-xs font-semibold uppercase tracking-wide">
248
229
  {title}
249
230
  </CardTitle>
250
231
  {showBalanceValue && latestValue !== null && (
251
232
  <p className="text-2xl font-bold tabular-nums leading-tight">
252
- {formatBalanceFull(latestValue)}
233
+ {formatCurrency(latestValue, { decimals: 2 })}
253
234
  </p>
254
235
  )}
255
236
  </div>
@@ -25,7 +25,14 @@ import {
25
25
  ChartPeriodButton,
26
26
  } from "./chart-shared";
27
27
 
28
- ChartJS.register(CategoryScale, LinearScale, BarController, BarElement, Tooltip, Legend);
28
+ ChartJS.register(
29
+ CategoryScale,
30
+ LinearScale,
31
+ BarController,
32
+ BarElement,
33
+ Tooltip,
34
+ Legend,
35
+ );
29
36
 
30
37
  // ---------------------------------------------------------------------------
31
38
  // Types
@@ -313,7 +320,7 @@ export function CashflowBarChart({
313
320
  style={{ maxWidth: width, fontFamily }}
314
321
  >
315
322
  <CardHeader className="px-3 sm:px-6">
316
- <CardTitle className="text-sm sm:text-base uppercase tracking-wider">
323
+ <CardTitle className="text-xs font-semibold uppercase tracking-wide">
317
324
  {title}
318
325
  </CardTitle>
319
326
  {showPeriodSelector && (
@@ -90,18 +90,11 @@ export const SEVERITY_COLORS = {
90
90
  } as const;
91
91
 
92
92
  /**
93
- * Format a dollar amount as an abbreviated string for chart axis ticks.
94
- * e.g. 1_200_000 "$1.2M", 580_000 "$580K", -250_000 "-$250K"
93
+ * Re-export abbreviated currency formatter from shared lib.
94
+ * Kept as `formatAbbrev` for backward compatibility with existing chart consumers.
95
95
  */
96
- export function formatAbbrev(value: number): string {
97
- const abs = Math.abs(value);
98
- const sign = value < 0 ? "-" : "";
99
- if (abs >= 1_000_000_000)
100
- return `${sign}$${(abs / 1_000_000_000).toFixed(1)}B`;
101
- if (abs >= 1_000_000) return `${sign}$${(abs / 1_000_000).toFixed(1)}M`;
102
- if (abs >= 1_000) return `${sign}$${(abs / 1_000).toFixed(0)}K`;
103
- return `${sign}$${abs.toFixed(0)}`;
104
- }
96
+ import { formatCurrencyAbbrev as formatAbbrev } from "@/lib/format-currency";
97
+ export { formatAbbrev };
105
98
 
106
99
  // ---------------------------------------------------------------------------
107
100
  // Tooltip date format
@@ -7,19 +7,19 @@
7
7
  // <Chip>Label</Chip>
8
8
  // <Chip onRemove={() => handleRemove(id)}>Label</Chip>
9
9
  // <Chip variant="outline" disabled onRemove={...}>Label</Chip>
10
- import { type ReactElement } from "react"
11
- import * as React from "react"
12
- import { X } from "lucide-react"
13
- import type { VariantProps } from "class-variance-authority"
14
- import { cn } from "@/lib/utils"
15
- import { badgeVariants } from "@/components/ui/badge"
10
+ import { type ReactElement } from "react";
11
+ import * as React from "react";
12
+ import { X } from "lucide-react";
13
+ import { Button } from "./button";
14
+ import type { VariantProps } from "class-variance-authority";
15
+ import { cn } from "@/lib/utils";
16
+ import { badgeVariants } from "@/components/ui/badge";
16
17
 
17
18
  export interface ChipProps
18
- extends React.ComponentProps<"span">,
19
- VariantProps<typeof badgeVariants> {
19
+ extends React.ComponentProps<"span">, VariantProps<typeof badgeVariants> {
20
20
  /** When provided, renders a dismiss (×) button inside the chip. */
21
- onRemove?: () => void
22
- disabled?: boolean
21
+ onRemove?: () => void;
22
+ disabled?: boolean;
23
23
  }
24
24
 
25
25
  function Chip({
@@ -38,28 +38,32 @@ function Chip({
38
38
  // extra right padding only when dismiss button is present
39
39
  onRemove && "pr-1",
40
40
  disabled && "pointer-events-none opacity-50",
41
- className
41
+ className,
42
42
  )}
43
43
  data-slot="chip"
44
44
  data-variant={variant}
45
45
  {...props}
46
46
  >
47
47
  {children}
48
- {onRemove ? <button
48
+ {onRemove ? (
49
+ <Button
50
+ type="button"
51
+ variant="ghost"
52
+ size="icon"
49
53
  aria-label="Remove"
50
- className="ml-0.5 inline-flex shrink-0 items-center justify-center rounded-full p-0.5 opacity-60 hover:opacity-100 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none"
54
+ className="ml-0.5 size-4 shrink-0 rounded-full p-0.5 opacity-60 hover:opacity-100 disabled:pointer-events-none"
51
55
  data-slot="chip-remove"
52
56
  disabled={disabled}
53
57
  onClick={(e) => {
54
- e.stopPropagation()
55
- onRemove()
58
+ e.stopPropagation();
59
+ onRemove();
56
60
  }}
57
- type="button"
58
61
  >
59
62
  <X className="size-3" />
60
- </button> : null}
63
+ </Button>
64
+ ) : null}
61
65
  </span>
62
- )
66
+ );
63
67
  }
64
68
 
65
- export { Chip }
69
+ export { Chip };
@@ -6,6 +6,7 @@ import {
6
6
  PopoverTrigger,
7
7
  } from "@/components/ui/popover";
8
8
  import { Input } from "@/components/ui/input";
9
+ import { Button } from "@/components/ui/button";
9
10
 
10
11
  /**
11
12
  * ColorPicker — WealthX Design System
@@ -93,14 +94,15 @@ function ColorSwatch({
93
94
  className,
94
95
  }: ColorSwatchProps) {
95
96
  return (
96
- <button
97
+ <Button
97
98
  type="button"
99
+ variant="ghost"
98
100
  title={color}
99
101
  aria-label={`Select color ${color}`}
100
102
  aria-pressed={selected}
101
103
  onClick={() => onClick?.(color)}
102
104
  className={cn(
103
- "relative shrink-0 transition-all outline-none shadow-[inset_0_0_0_1px_rgba(0,0,0,0.12)]",
105
+ "relative shrink-0 p-0 transition-all outline-none shadow-[inset_0_0_0_1px_rgba(0,0,0,0.12)]",
104
106
  "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1",
105
107
  size === "md" ? "size-7" : "size-5",
106
108
  selected &&
@@ -35,10 +35,6 @@ import {
35
35
  ArrowUpDown,
36
36
  ArrowUp,
37
37
  ArrowDown,
38
- ChevronLeftIcon,
39
- ChevronRightIcon,
40
- ChevronsLeftIcon,
41
- ChevronsRightIcon,
42
38
  SlidersHorizontal,
43
39
  } from "lucide-react";
44
40
  import { cn } from "@/lib/utils";
@@ -60,6 +56,7 @@ import {
60
56
  SelectTrigger,
61
57
  SelectValue,
62
58
  } from "@/components/ui/select";
59
+ import { PaginationNavButtons } from "@/components/ui/pagination";
63
60
  import {
64
61
  DropdownMenu,
65
62
  DropdownMenuCheckboxItem,
@@ -291,32 +288,30 @@ function DataTablePagination<TData>({
291
288
  }): ReactElement {
292
289
  return (
293
290
  <div
294
- className="flex items-center justify-between gap-4 py-4"
291
+ className="flex items-center gap-4 py-4"
295
292
  data-slot="data-table-pagination"
296
293
  >
297
- {/* Selected rows info */}
298
- <p className="text-body-small text-muted-foreground">
299
- {table.getFilteredSelectedRowModel().rows.length > 0 && (
300
- <>
301
- {table.getFilteredSelectedRowModel().rows.length} of{" "}
302
- {table.getFilteredRowModel().rows.length} row(s) selected.
303
- </>
304
- )}
305
- </p>
294
+ {table.getFilteredSelectedRowModel().rows.length > 0 && (
295
+ <span className="text-body-small text-muted-foreground">
296
+ {table.getFilteredSelectedRowModel().rows.length} of{" "}
297
+ {table.getFilteredRowModel().rows.length} row(s) selected.
298
+ </span>
299
+ )}
306
300
 
307
- <div className="flex items-center gap-6">
308
- {/* Page size selector */}
301
+ <div className="ml-auto flex items-center gap-6">
309
302
  <div className="flex items-center gap-2">
310
- <p className="text-body-small text-muted-foreground whitespace-nowrap">
303
+ <span className="text-body-small text-muted-foreground whitespace-nowrap">
311
304
  Rows per page
312
- </p>
305
+ </span>
313
306
  <Select
314
- onValueChange={(value) => {
315
- table.setPageSize(Number(value));
316
- }}
307
+ onValueChange={(value) => table.setPageSize(Number(value))}
317
308
  value={`${table.getState().pagination.pageSize}`}
318
309
  >
319
- <SelectTrigger className="w-[70px]" size="sm">
310
+ <SelectTrigger
311
+ aria-label="Rows per page"
312
+ className="w-[70px]"
313
+ size="sm"
314
+ >
320
315
  <SelectValue
321
316
  placeholder={`${table.getState().pagination.pageSize}`}
322
317
  />
@@ -331,59 +326,19 @@ function DataTablePagination<TData>({
331
326
  </Select>
332
327
  </div>
333
328
 
334
- {/* Page info */}
335
- <p className="text-body-small text-muted-foreground whitespace-nowrap">
329
+ <span className="text-body-small text-muted-foreground whitespace-nowrap">
336
330
  Page {table.getState().pagination.pageIndex + 1} of{" "}
337
331
  {table.getPageCount()}
338
- </p>
332
+ </span>
339
333
 
340
- {/* Page navigation */}
341
- <div className="flex items-center gap-1">
342
- <Button
343
- aria-label="Go to first page"
344
- disabled={!table.getCanPreviousPage()}
345
- onClick={() => {
346
- table.setPageIndex(0);
347
- }}
348
- size="icon-sm"
349
- variant="outline"
350
- >
351
- <ChevronsLeftIcon className="size-4" />
352
- </Button>
353
- <Button
354
- aria-label="Go to previous page"
355
- disabled={!table.getCanPreviousPage()}
356
- onClick={() => {
357
- table.previousPage();
358
- }}
359
- size="icon-sm"
360
- variant="outline"
361
- >
362
- <ChevronLeftIcon className="size-4" />
363
- </Button>
364
- <Button
365
- aria-label="Go to next page"
366
- disabled={!table.getCanNextPage()}
367
- onClick={() => {
368
- table.nextPage();
369
- }}
370
- size="icon-sm"
371
- variant="outline"
372
- >
373
- <ChevronRightIcon className="size-4" />
374
- </Button>
375
- <Button
376
- aria-label="Go to last page"
377
- disabled={!table.getCanNextPage()}
378
- onClick={() => {
379
- table.setPageIndex(table.getPageCount() - 1);
380
- }}
381
- size="icon-sm"
382
- variant="outline"
383
- >
384
- <ChevronsRightIcon className="size-4" />
385
- </Button>
386
- </div>
334
+ <PaginationNavButtons
335
+ hasNext={table.getCanNextPage()}
336
+ hasPrev={table.getCanPreviousPage()}
337
+ onFirst={() => table.setPageIndex(0)}
338
+ onLast={() => table.setPageIndex(table.getPageCount() - 1)}
339
+ onNext={() => table.nextPage()}
340
+ onPrev={() => table.previousPage()}
341
+ />
387
342
  </div>
388
343
  </div>
389
344
  );
@@ -494,8 +449,7 @@ function DataTable<TData, TValue>({
494
449
  onColumnFiltersChange: (updater) => {
495
450
  const next =
496
451
  typeof updater === "function" ? updater(columnFilters) : updater;
497
- if (controlledColumnFilters === undefined)
498
- setInternalColumnFilters(next);
452
+ if (controlledColumnFilters === undefined) setInternalColumnFilters(next);
499
453
  onColumnFiltersChangeProp?.(next);
500
454
  },
501
455
  onPaginationChange: (updater) => {
@@ -1,15 +1,16 @@
1
- import { type ReactElement } from "react"
2
- import * as React from "react"
3
- import { format } from "date-fns"
4
- import { CalendarIcon } from "lucide-react"
5
- import { cn } from "@/lib/utils"
6
- import { Button } from "@/components/ui/button"
7
- import { Calendar } from "@/components/ui/calendar"
1
+ import { type ReactElement } from "react";
2
+ import * as React from "react";
3
+ import { format } from "date-fns";
4
+ import { CalendarIcon } from "lucide-react";
5
+ import { cn } from "@/lib/utils";
6
+ import { Button } from "@/components/ui/button";
7
+ import { Input } from "@/components/ui/input";
8
+ import { Calendar } from "@/components/ui/calendar";
8
9
  import {
9
10
  Popover,
10
11
  PopoverContent,
11
12
  PopoverTrigger,
12
- } from "@/components/ui/popover"
13
+ } from "@/components/ui/popover";
13
14
 
14
15
  /**
15
16
  * DatePicker — WealthX Design System
@@ -22,18 +23,18 @@ import {
22
23
  */
23
24
 
24
25
  export interface DatePickerProps {
25
- value?: Date
26
- onChange?: (date: Date | undefined) => void
27
- placeholder?: string
26
+ value?: Date;
27
+ onChange?: (date: Date | undefined) => void;
28
+ placeholder?: string;
28
29
  /** Show a time input below the calendar */
29
- showTimePicker?: boolean
30
- disabled?: boolean
31
- className?: string
30
+ showTimePicker?: boolean;
31
+ disabled?: boolean;
32
+ className?: string;
32
33
  /** Passed through to Calendar (e.g. fromYear, toYear, captionLayout) */
33
34
  calendarProps?: Omit<
34
35
  React.ComponentProps<typeof Calendar>,
35
36
  "mode" | "selected" | "onSelect"
36
- >
37
+ >;
37
38
  }
38
39
 
39
40
  function DatePicker({
@@ -45,37 +46,37 @@ function DatePicker({
45
46
  className,
46
47
  calendarProps,
47
48
  }: DatePickerProps): ReactElement {
48
- const [open, setOpen] = React.useState(false)
49
+ const [open, setOpen] = React.useState(false);
49
50
 
50
51
  function handleDaySelect(day: Date | undefined): void {
51
52
  if (!day) {
52
- onChange?.(undefined)
53
- return
53
+ onChange?.(undefined);
54
+ return;
54
55
  }
55
56
  // Preserve existing time when selecting a new day
56
57
  if (showTimePicker && value) {
57
- day.setHours(value.getHours(), value.getMinutes())
58
+ day.setHours(value.getHours(), value.getMinutes());
58
59
  }
59
- onChange?.(day)
60
- if (!showTimePicker) setOpen(false)
60
+ onChange?.(day);
61
+ if (!showTimePicker) setOpen(false);
61
62
  }
62
63
 
63
64
  function handleTimeChange(e: React.ChangeEvent<HTMLInputElement>): void {
64
- const [hours, minutes] = e.target.value.split(":").map(Number)
65
- const next = value ? new Date(value) : new Date()
66
- next.setHours(hours, minutes, 0, 0)
67
- onChange?.(next)
65
+ const [hours, minutes] = e.target.value.split(":").map(Number);
66
+ const next = value ? new Date(value) : new Date();
67
+ next.setHours(hours, minutes, 0, 0);
68
+ onChange?.(next);
68
69
  }
69
70
 
70
71
  const timeValue = value
71
72
  ? `${String(value.getHours()).padStart(2, "0")}:${String(value.getMinutes()).padStart(2, "0")}`
72
- : ""
73
+ : "";
73
74
 
74
- let displayValue: string | undefined
75
+ let displayValue: string | undefined;
75
76
  if (value && showTimePicker) {
76
- displayValue = format(value, "dd/MM/yyyy HH:mm")
77
+ displayValue = format(value, "dd/MM/yyyy HH:mm");
77
78
  } else if (value) {
78
- displayValue = format(value, "dd/MM/yyyy")
79
+ displayValue = format(value, "dd/MM/yyyy");
79
80
  }
80
81
 
81
82
  return (
@@ -85,7 +86,9 @@ function DatePicker({
85
86
  <Button
86
87
  className={cn(
87
88
  "w-full justify-start rounded-none font-normal data-[empty=true]:text-muted-foreground",
88
- className
89
+ // Show open/focus state when the popover is expanded
90
+ "aria-expanded:border-ring aria-expanded:ring-2 aria-expanded:ring-ring/20",
91
+ className,
89
92
  )}
90
93
  data-empty={!value}
91
94
  data-slot="date-picker-trigger"
@@ -109,23 +112,25 @@ function DatePicker({
109
112
  {...calendarProps}
110
113
  className={cn(
111
114
  "rounded-none border-0 shadow-none",
112
- calendarProps?.className
115
+ calendarProps?.className,
113
116
  )}
114
117
  />
115
- {showTimePicker ? <div className="border-t border-border px-3 pb-3 pt-2">
118
+ {showTimePicker ? (
119
+ <div className="border-t border-border px-3 pb-3 pt-2">
116
120
  <label className="mb-1.5 block text-xs font-medium text-muted-foreground">
117
121
  Time
118
- <input
119
- className="mt-1.5 h-8 w-full rounded-none border border-input bg-transparent px-2 text-sm font-sans outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50"
122
+ <Input
123
+ className="mt-1.5 h-8"
120
124
  onChange={handleTimeChange}
121
125
  type="time"
122
126
  value={timeValue}
123
127
  />
124
128
  </label>
125
- </div> : null}
129
+ </div>
130
+ ) : null}
126
131
  </PopoverContent>
127
132
  </Popover>
128
- )
133
+ );
129
134
  }
130
135
 
131
- export { DatePicker }
136
+ export { DatePicker };