@wealthx/shadcn 1.2.2 → 1.3.1

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 (230) hide show
  1. package/.turbo/turbo-build.log +193 -149
  2. package/CHANGELOG.md +28 -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-A6AAWBPF.mjs → chunk-GHC7LLUX.mjs} +13 -4
  29. package/dist/chunk-HBZLGDIN.mjs +507 -0
  30. package/dist/{chunk-SIZMLSRU.mjs → chunk-HISNT2MG.mjs} +8 -6
  31. package/dist/{chunk-CGH4DRNG.mjs → chunk-HVY6KCCF.mjs} +10 -7
  32. package/dist/chunk-I3RZS7V2.mjs +136 -0
  33. package/dist/chunk-IAE3F7DR.mjs +1962 -0
  34. package/dist/{chunk-UT4KJR7V.mjs → chunk-IHMFS7NZ.mjs} +35 -74
  35. package/dist/{chunk-PCPLO5HT.mjs → chunk-IOJRDS6V.mjs} +96 -14
  36. package/dist/{chunk-LHYCMLVA.mjs → chunk-JKGDCQTZ.mjs} +11 -4
  37. package/dist/{chunk-H45TKD34.mjs → chunk-JMHR3YGZ.mjs} +1 -1
  38. package/dist/{chunk-4MN6UQHG.mjs → chunk-K5A5L6T2.mjs} +17 -39
  39. package/dist/chunk-LV35NGVG.mjs +272 -0
  40. package/dist/{chunk-FZIXGLMV.mjs → chunk-M3FV7LOK.mjs} +5 -12
  41. package/dist/{chunk-FMAXJ2SI.mjs → chunk-MBON7YRJ.mjs} +1 -1
  42. package/dist/chunk-MIZQHHUO.mjs +441 -0
  43. package/dist/chunk-MLNEWRWV.mjs +449 -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-Q2BGOAMG.mjs +202 -0
  51. package/dist/chunk-QMY3AZJH.mjs +80 -0
  52. package/dist/{chunk-BL3DXM2X.mjs → chunk-QZ4RE6NA.mjs} +11 -4
  53. package/dist/{chunk-VACKZOMY.mjs → chunk-R3VSPKNP.mjs} +3 -3
  54. package/dist/{chunk-OPNQAVVH.mjs → chunk-RJI6GKVF.mjs} +8 -6
  55. package/dist/{chunk-WG6JGJXB.mjs → chunk-T4BJLT57.mjs} +1 -1
  56. package/dist/chunk-UMTOX62O.mjs +415 -0
  57. package/dist/{chunk-7MMXNK3C.mjs → chunk-VLARHE5V.mjs} +8 -6
  58. package/dist/{chunk-2I5S2AMY.mjs → chunk-XREGSKX3.mjs} +2 -2
  59. package/dist/{chunk-JNQORUPP.mjs → chunk-YJG55G2H.mjs} +14 -11
  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 +530 -0
  65. package/dist/components/ui/advisor-card.mjs +15 -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 +426 -191
  160. package/dist/components/ui/sidebar-nav.mjs +5 -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 +12258 -8611
  170. package/dist/index.mjs +258 -190
  171. package/dist/styles.css +1 -1
  172. package/package.json +71 -1
  173. package/src/components/index.tsx +115 -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 +284 -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 +213 -157
  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-ZRSDX6OW.mjs +0 -385
  230. package/dist/{chunk-LLVQKSU3.mjs → chunk-GD4BJDJR.mjs} +3 -3
@@ -68,10 +68,55 @@ function DialogOverlay({
68
68
  );
69
69
  }
70
70
 
71
+ /**
72
+ * Maps the `size` prop to an exact CSS max-width value.
73
+ * Applied via inline style — bypasses Tailwind v4 class scanning entirely.
74
+ */
75
+ const DIALOG_MAX_WIDTHS: Record<string, string> = {
76
+ sm: "24rem",
77
+ md: "28rem",
78
+ lg: "32rem",
79
+ xl: "36rem",
80
+ "2xl": "42rem",
81
+ "3xl": "48rem",
82
+ "4xl": "56rem",
83
+ full: "100%",
84
+ };
85
+
86
+ /**
87
+ * Default minimum width for size="auto" dialogs.
88
+ * Prevents the dialog from collapsing too narrow when hugging content.
89
+ * Override per-instance via the `minWidth` prop.
90
+ */
91
+ const DIALOG_AUTO_MIN_WIDTH = "20rem"; // 320px
92
+
71
93
  export type DialogContentProps = React.ComponentProps<
72
94
  typeof DialogPrimitive.Popup
73
95
  > & {
74
96
  showCloseButton?: boolean;
97
+ /**
98
+ * Optional container element to render the Dialog portal into.
99
+ * Pass a ref'd element to scope the overlay inside a parent (e.g. a Sheet drawer)
100
+ * instead of appending to document.body.
101
+ */
102
+ container?: HTMLElement | null;
103
+ /** Vertical alignment of the dialog. "top" anchors to top-4; "center" (default) vertically centers. */
104
+ align?: "center" | "top";
105
+ /**
106
+ * Width behaviour of the dialog panel.
107
+ * - Fixed sizes ("sm" | "md" | "lg" | "xl" | "2xl" | "3xl" | "4xl" | "full"):
108
+ * dialog fills width up to that maximum (applied via inline style — reliable in Tailwind v4).
109
+ * - "auto": dialog shrinks to fit its content (hug-content). Has a built-in minimum
110
+ * width of `DIALOG_AUTO_MIN_WIDTH` (20rem / 320px) to avoid collapsing too narrow.
111
+ * Defaults to "lg".
112
+ */
113
+ size?: "sm" | "md" | "lg" | "xl" | "2xl" | "3xl" | "4xl" | "full" | "auto";
114
+ /**
115
+ * Override the minimum width for `size="auto"` dialogs.
116
+ * Accepts any valid CSS length value (e.g. "24rem", "400px").
117
+ * Ignored when using a fixed size.
118
+ */
119
+ minWidth?: string;
75
120
  };
76
121
 
77
122
  function DialogContent({
@@ -79,28 +124,47 @@ function DialogContent({
79
124
  children,
80
125
  showCloseButton = true,
81
126
  style,
127
+ container,
128
+ align = "center",
129
+ size = "lg",
130
+ minWidth,
82
131
  ...props
83
132
  }: DialogContentProps): ReactElement {
84
133
  const themeVars = useThemeVars();
134
+ const isAuto = size === "auto";
135
+
136
+ const sizeStyle = isAuto
137
+ ? { minWidth: minWidth ?? DIALOG_AUTO_MIN_WIDTH }
138
+ : { maxWidth: DIALOG_MAX_WIDTHS[size] };
139
+
85
140
  return (
86
- <DialogPortal>
141
+ <DialogPortal container={container ?? undefined}>
87
142
  <DialogOverlay style={themeVars as React.CSSProperties} />
88
143
  <DialogPrimitive.Popup
89
144
  className={cn(
90
- // WealthX: removed rounded-lg (sharp corners), shadow-lg (flat panels), foreground/50 scrim via DialogOverlay
91
- "fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 duration-200 outline-none data-ending-style:animate-out data-ending-style:fade-out-0 data-ending-style:zoom-out-95 data-ending-style:fill-mode-forwards data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 sm:max-w-lg",
145
+ // WealthX: removed rounded-lg (sharp corners), shadow-lg (flat panels)
146
+ // max-w-[calc(100%-2rem)] acts as a viewport-edge guard on all sizes.
147
+ // Fixed max-width is applied via inline style (sizeStyle) to avoid
148
+ // Tailwind v4 class-scanning gaps with dynamic class lookups.
149
+ "fixed left-[50%] z-50 grid max-w-[calc(100%-2rem)] translate-x-[-50%] gap-4 border bg-background p-6 duration-200 outline-none data-ending-style:animate-out data-ending-style:fade-out-0 data-ending-style:zoom-out-95 data-ending-style:fill-mode-forwards data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95",
150
+ // "auto" → hug content (with min-width floor); fixed sizes → fill to max-width
151
+ isAuto ? "w-auto" : "w-full",
152
+ align === "center"
153
+ ? "top-[50%] translate-y-[-50%]"
154
+ : "top-4 translate-y-0",
92
155
  className,
93
156
  )}
94
157
  data-slot="dialog-content"
95
- style={{ ...themeVars, ...style } as React.CSSProperties}
158
+ style={{ ...themeVars, ...sizeStyle, ...style } as React.CSSProperties}
96
159
  {...props}
97
160
  >
98
161
  {children}
99
162
  {showCloseButton ? (
100
163
  <DialogPrimitive.Close
101
- className="absolute top-4 right-4 transition-colors hover:bg-foreground/5 focus:outline-hidden focus:ring-2 focus:ring-border focus:ring-offset-0 disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
164
+ className="absolute top-4 right-4 inline-flex size-7 items-center justify-center transition-colors hover:bg-foreground/5 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-border focus-visible:ring-offset-0 disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
102
165
  // WealthX: removed rounded-xs (sharp), replaced opacity fade with hover:bg-foreground/5,
103
- // focus ring uses border color (subtle, matches Figma DialogClose focus state)
166
+ // focus ring uses border color (subtle, matches Figma DialogClose focus state).
167
+ // size-7 = 28px hit area (icon is size-4=16px, centered via inline-flex).
104
168
  data-slot="dialog-close"
105
169
  >
106
170
  <XIcon />
@@ -152,6 +216,8 @@ function DialogFooter({
152
216
  {showCloseButton ? (
153
217
  <DialogPrimitive.Close
154
218
  className={cn(buttonVariants({ variant: "outline" }))}
219
+ // type="button" prevents accidental form submission inside Dialog forms
220
+ type="button"
155
221
  >
156
222
  Close
157
223
  </DialogPrimitive.Close>
@@ -30,7 +30,14 @@ import {
30
30
  type ChartGranularity,
31
31
  } from "./chart-shared";
32
32
 
33
- ChartJS.register(CategoryScale, LinearScale, BarController, BarElement, Tooltip, Legend);
33
+ ChartJS.register(
34
+ CategoryScale,
35
+ LinearScale,
36
+ BarController,
37
+ BarElement,
38
+ Tooltip,
39
+ Legend,
40
+ );
34
41
 
35
42
  // ---------------------------------------------------------------------------
36
43
  // Types
@@ -234,7 +241,9 @@ export function ExpenseBarChart({
234
241
  style={{ maxWidth: width, fontFamily }}
235
242
  >
236
243
  <CardHeader className="px-3 sm:px-6">
237
- <CardTitle className="text-sm sm:text-base">{title}</CardTitle>
244
+ <CardTitle className="text-xs font-semibold uppercase tracking-wide">
245
+ {title}
246
+ </CardTitle>
238
247
  <CardAction>
239
248
  <div className="flex gap-0.5 sm:gap-1">
240
249
  {periods.map((p) => (
@@ -20,6 +20,7 @@ import {
20
20
  } from "lucide-react";
21
21
  import type { LucideIcon } from "lucide-react";
22
22
  import { cn } from "@/lib/utils";
23
+ import { formatDateShort } from "@/lib/format-date";
23
24
  import { Badge } from "./badge";
24
25
  import { RadioGroup, RadioGroupItem } from "./radio-group";
25
26
  import {
@@ -30,6 +31,7 @@ import {
30
31
  FinancialSubtotalBlock,
31
32
  FinancialSubtotalFrame,
32
33
  } from "./financial-primitives";
34
+ import { Button } from "./button";
33
35
 
34
36
  /**
35
37
  * Financial card molecules — WealthX DS (Level 3)
@@ -122,11 +124,12 @@ export function PropertyCard({
122
124
  >
123
125
  {/* Header row */}
124
126
  {isLinkedToBank ? (
125
- <button
127
+ <Button
126
128
  type="button"
129
+ variant="ghost"
127
130
  aria-expanded={expanded}
128
131
  onClick={() => setExpanded((prev) => !prev)}
129
- className="flex items-center gap-1.5 px-5 py-3 text-left cursor-pointer hover:opacity-85"
132
+ className="h-auto w-full justify-start gap-1.5 px-5 py-3 text-left"
130
133
  >
131
134
  <span className="text-label-medium text-foreground">{address}</span>
132
135
  {type && <Badge variant="outline">{type}</Badge>}
@@ -137,7 +140,7 @@ export function PropertyCard({
137
140
  expanded && "rotate-180",
138
141
  )}
139
142
  />
140
- </button>
143
+ </Button>
141
144
  ) : (
142
145
  <div className="flex items-center gap-1.5 px-5 py-3">
143
146
  <span className="text-label-medium text-foreground">{address}</span>
@@ -615,9 +618,17 @@ export interface AboutCardProps {
615
618
  lastName?: string;
616
619
  phone?: string;
617
620
  email?: string;
621
+ dob?: string;
618
622
  gender?: string;
619
623
  maritalStatus?: string;
624
+ numDependants?: string;
620
625
  citizenStatus?: string;
626
+ residentialAddress?: string;
627
+ residentialStatus?: string;
628
+ timeAtAddressYears?: string;
629
+ timeAtAddressMonths?: string;
630
+ driversLicence?: string;
631
+ passport?: string;
621
632
  propertyInTrust?: string;
622
633
  companyOwnership?: string;
623
634
  }
@@ -632,19 +643,38 @@ export function AboutCard({
632
643
  lastName,
633
644
  phone,
634
645
  email,
646
+ dob,
635
647
  gender,
636
648
  maritalStatus,
649
+ numDependants,
637
650
  citizenStatus,
651
+ residentialAddress,
652
+ residentialStatus,
653
+ timeAtAddressYears,
654
+ timeAtAddressMonths,
655
+ driversLicence,
656
+ passport,
638
657
  propertyInTrust,
639
658
  companyOwnership,
640
659
  }: AboutCardProps) {
641
660
  const fullName =
642
661
  [title, firstName, lastName].filter(Boolean).join(" ") || "—";
643
662
 
663
+ const timeAtAddress =
664
+ timeAtAddressYears || timeAtAddressMonths
665
+ ? [
666
+ timeAtAddressYears && `${timeAtAddressYears}yr`,
667
+ timeAtAddressMonths && `${timeAtAddressMonths}mo`,
668
+ ]
669
+ .filter(Boolean)
670
+ .join(" ")
671
+ : undefined;
672
+
644
673
  return (
645
674
  <div className="border border-border overflow-hidden">
646
675
  <div className="grid grid-cols-2 gap-x-8 gap-y-4 px-5 py-4">
647
676
  <FinancialDetailField label="Full Name" value={fullName} />
677
+ <FinancialDetailField label="Date of Birth" value={dob || "—"} />
648
678
  <FinancialDetailField label="Gender" value={gender || "—"} />
649
679
  <FinancialDetailField label="Phone" value={phone || "—"} />
650
680
  <FinancialDetailField label="Email" value={email || "—"} />
@@ -652,10 +682,30 @@ export function AboutCard({
652
682
  label="Marital Status"
653
683
  value={maritalStatus || "—"}
654
684
  />
685
+ <FinancialDetailField
686
+ label="No. of Dependants"
687
+ value={numDependants ?? "—"}
688
+ />
655
689
  <FinancialDetailField
656
690
  label="Citizenship"
657
691
  value={citizenStatus || "—"}
658
692
  />
693
+ <FinancialDetailField
694
+ label="Residential Address"
695
+ value={residentialAddress || "—"}
696
+ />
697
+ <FinancialDetailField
698
+ label="Residential Status"
699
+ value={residentialStatus || "—"}
700
+ />
701
+ {timeAtAddress && (
702
+ <FinancialDetailField label="Time at Address" value={timeAtAddress} />
703
+ )}
704
+ <FinancialDetailField
705
+ label="Driver's Licence"
706
+ value={driversLicence || "—"}
707
+ />
708
+ <FinancialDetailField label="Passport" value={passport || "—"} />
659
709
  <FinancialDetailField
660
710
  label="Property in Trust"
661
711
  value={propertyInTrust || "—"}
@@ -677,6 +727,11 @@ export interface IncomeCardItem {
677
727
  incomeType: string;
678
728
  jobTitle?: string;
679
729
  companyName?: string;
730
+ companyAddress?: string;
731
+ startDate?: string;
732
+ stillInPosition?: boolean;
733
+ endDate?: string;
734
+ companyType?: string;
680
735
  /** Pre-formatted amount + frequency e.g. "$9,500 / Monthly" */
681
736
  amountLabel: string;
682
737
  }
@@ -687,6 +742,7 @@ export interface IncomeCardProps {
687
742
  totalMonthly?: string;
688
743
  }
689
744
 
745
+
690
746
  /**
691
747
  * Display card for applicant income items.
692
748
  * Each income source renders as a bordered row with type, company, and amount.
@@ -706,16 +762,49 @@ export function IncomeCard({ items, totalMonthly }: IncomeCardProps) {
706
762
  <div
707
763
  key={i}
708
764
  className={cn(
709
- "grid grid-cols-3 gap-x-6 px-5 py-[15px]",
765
+ "flex flex-col gap-3 px-5 py-[15px]",
710
766
  i < items.length - 1 && "border-b border-border",
711
767
  )}
712
768
  >
713
- <FinancialDetailField label="Type" value={item.incomeType || "—"} />
714
- <FinancialDetailField
715
- label="Employer"
716
- value={item.companyName || item.jobTitle || "—"}
717
- />
718
- <FinancialDetailField label="Amount" value={item.amountLabel} />
769
+ {/* Primary row: Type | Job Title | Amount */}
770
+ <div className="grid grid-cols-3 gap-x-6">
771
+ <FinancialDetailField label="Type" value={item.incomeType || "—"} />
772
+ <FinancialDetailField
773
+ label="Job Title"
774
+ value={item.jobTitle || "—"}
775
+ />
776
+ <FinancialDetailField label="Amount" value={item.amountLabel} />
777
+ </div>
778
+ {/* Secondary row: Company | Start Date | Status */}
779
+ <div className="grid grid-cols-3 gap-x-6">
780
+ <FinancialDetailField
781
+ label="Company"
782
+ value={item.companyName || "—"}
783
+ />
784
+ <FinancialDetailField
785
+ label="Start Date"
786
+ value={formatDateShort(item.startDate)}
787
+ />
788
+ <FinancialDetailField
789
+ label="Status"
790
+ value={
791
+ item.stillInPosition === undefined
792
+ ? "—"
793
+ : item.stillInPosition
794
+ ? "Still in position"
795
+ : item.endDate
796
+ ? `Ended ${formatDateShort(item.endDate)}`
797
+ : "No longer in position"
798
+ }
799
+ />
800
+ </div>
801
+ {/* Company address — full width if present */}
802
+ {item.companyAddress && (
803
+ <FinancialDetailField
804
+ label="Company Address"
805
+ value={item.companyAddress}
806
+ />
807
+ )}
719
808
  </div>
720
809
  ))}
721
810
  {totalMonthly && (
@@ -30,7 +30,14 @@ import {
30
30
  type ChartGranularity,
31
31
  } from "./chart-shared";
32
32
 
33
- ChartJS.register(CategoryScale, LinearScale, BarController, BarElement, Tooltip, Legend);
33
+ ChartJS.register(
34
+ CategoryScale,
35
+ LinearScale,
36
+ BarController,
37
+ BarElement,
38
+ Tooltip,
39
+ Legend,
40
+ );
34
41
 
35
42
  // ---------------------------------------------------------------------------
36
43
  // Types
@@ -235,7 +242,9 @@ export function IncomeBarChart({
235
242
  style={{ maxWidth: width, fontFamily }}
236
243
  >
237
244
  <CardHeader className="px-3 sm:px-6">
238
- <CardTitle className="text-sm sm:text-base">{title}</CardTitle>
245
+ <CardTitle className="text-xs font-semibold uppercase tracking-wide">
246
+ {title}
247
+ </CardTitle>
239
248
  <CardAction>
240
249
  <div className="flex gap-0.5 sm:gap-1">
241
250
  {periods.map((p) => (
@@ -10,6 +10,8 @@ import {
10
10
  Bot,
11
11
  } from "lucide-react";
12
12
  import { cn } from "@/lib/utils";
13
+ import { formatCurrency } from "@/lib/format-currency";
14
+ import { formatDateShort, formatDateDayMonth } from "@/lib/format-date";
13
15
  import { Badge } from "@/components/ui/badge";
14
16
  import { Button, buttonVariants } from "@/components/ui/button";
15
17
  import { Separator } from "@/components/ui/separator";
@@ -149,36 +151,6 @@ function resolvePriority(
149
151
  return priority;
150
152
  }
151
153
 
152
- function formatAmount(amount: number): string {
153
- return new Intl.NumberFormat("en-AU", {
154
- style: "currency",
155
- currency: "AUD",
156
- maximumFractionDigits: 0,
157
- }).format(amount);
158
- }
159
-
160
- function formatDate(iso: string): string {
161
- try {
162
- return new Date(iso).toLocaleDateString("en-AU", {
163
- day: "2-digit",
164
- month: "short",
165
- year: "numeric",
166
- });
167
- } catch {
168
- return iso;
169
- }
170
- }
171
-
172
- function formatHoldDate(iso: string): string {
173
- try {
174
- return new Date(iso).toLocaleDateString("en-AU", {
175
- day: "2-digit",
176
- month: "short",
177
- });
178
- } catch {
179
- return iso;
180
- }
181
- }
182
154
 
183
155
  function formatLoanType(type: string): string {
184
156
  return type
@@ -249,7 +221,7 @@ export function OpportunityCard({
249
221
  style={{ color: "var(--color-warning-text)" }}
250
222
  >
251
223
  <Clock className="size-3 shrink-0" />
252
- On hold until {formatHoldDate(onHoldTo)}
224
+ On hold until {formatDateDayMonth(onHoldTo)}
253
225
  </div>
254
226
  )}
255
227
 
@@ -272,24 +244,23 @@ export function OpportunityCard({
272
244
  </Badge>
273
245
  )}
274
246
  <span className="text-base font-bold tabular-nums text-foreground">
275
- {formatAmount(amount)}
247
+ {formatCurrency(amount)}
276
248
  </span>
277
249
  </div>
278
250
 
279
251
  <div className="flex items-center gap-1 -mr-1 -mt-1">
280
252
  {onLaunchAssistant && (
281
- <button
253
+ <Button
282
254
  type="button"
283
- className={cn(
284
- buttonVariants({ variant: "ghost", size: "icon" }),
285
- "size-7 shrink-0",
286
- )}
255
+ variant="ghost"
256
+ size="icon"
257
+ className="size-7 shrink-0"
287
258
  onClick={onLaunchAssistant}
288
259
  aria-label="Launch AI Assistant"
289
260
  title="Launch AI Assistant"
290
261
  >
291
262
  <Bot className="size-4" />
292
- </button>
263
+ </Button>
293
264
  )}
294
265
  {hasMenu && (
295
266
  <DropdownMenu>
@@ -372,7 +343,7 @@ export function OpportunityCard({
372
343
  <div className="flex items-center justify-between">
373
344
  <span className="flex items-center gap-1.5 text-xs text-muted-foreground">
374
345
  <Calendar className="size-3 shrink-0" aria-hidden="true" />
375
- {formatDate(date)}
346
+ {formatDateShort(date)}
376
347
  </span>
377
348
 
378
349
  <span className="flex items-center gap-1.5">