@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
@@ -1,6 +1,7 @@
1
1
  import * as React from "react";
2
2
  import { ChevronDownIcon, Plus, Trash2 } from "lucide-react";
3
3
  import { cn } from "@/lib/utils";
4
+ import { PROPERTY_ASSET_TYPES } from "@/lib/opportunity-constants";
4
5
  import {
5
6
  Dialog,
6
7
  DialogContent,
@@ -86,14 +87,6 @@ const ASSET_TYPES = [
86
87
  "Other",
87
88
  ];
88
89
 
89
- const PROPERTY_ASSET_TYPES = new Set([
90
- "Primary Residence",
91
- "Investment Property",
92
- "Holiday Home",
93
- "Commercial Property",
94
- "Rural Property",
95
- ]);
96
-
97
90
  const PROPERTY_SUBTYPES = [
98
91
  "House",
99
92
  "Unit / Apartment",
@@ -447,6 +440,8 @@ export interface EditLoanScenarioModalProps {
447
440
  onSave: (data: LoanScenarioFormData) => void;
448
441
  mainApplicantName?: string;
449
442
  coApplicantName?: string;
443
+ /** Scope the dialog portal into this element (e.g. a Sheet drawer) instead of document.body. */
444
+ container?: HTMLElement | null;
450
445
  className?: string;
451
446
  }
452
447
 
@@ -457,6 +452,7 @@ export interface EditAssetsModalProps {
457
452
  onSave: (items: AssetLineItem[]) => void;
458
453
  mainApplicantName?: string;
459
454
  coApplicantName?: string;
455
+ container?: HTMLElement | null;
460
456
  className?: string;
461
457
  }
462
458
 
@@ -467,6 +463,7 @@ export interface EditDebtsModalProps {
467
463
  onSave: (items: DebtLineItem[]) => void;
468
464
  mainApplicantName?: string;
469
465
  coApplicantName?: string;
466
+ container?: HTMLElement | null;
470
467
  className?: string;
471
468
  }
472
469
 
@@ -476,6 +473,7 @@ export interface EditAboutApplicantModalProps {
476
473
  applicantLabel?: string;
477
474
  initialData?: Partial<AboutApplicantFormData>;
478
475
  onSave: (data: AboutApplicantFormData) => void;
476
+ container?: HTMLElement | null;
479
477
  className?: string;
480
478
  }
481
479
 
@@ -485,6 +483,7 @@ export interface EditIncomeModalProps {
485
483
  applicantLabel?: string;
486
484
  initialData?: IncomeFormData;
487
485
  onSave: (data: IncomeFormData) => void;
486
+ container?: HTMLElement | null;
488
487
  className?: string;
489
488
  }
490
489
 
@@ -494,6 +493,7 @@ export interface EditExpensesModalProps {
494
493
  applicantLabel?: string;
495
494
  initialData?: ExpensesFormData;
496
495
  onSave: (data: ExpensesFormData) => void;
496
+ container?: HTMLElement | null;
497
497
  className?: string;
498
498
  }
499
499
 
@@ -577,14 +577,16 @@ function AccordionItemHeader({
577
577
  {label}
578
578
  <ChevronDownIcon className="pointer-events-none size-4 shrink-0 text-muted-foreground transition-transform duration-200" />
579
579
  </AccordionPrimitive.Trigger>
580
- <button
580
+ <Button
581
581
  type="button"
582
+ variant="ghost"
583
+ size="icon"
582
584
  onClick={onRemove}
583
- className="p-2 hover:bg-foreground/5 transition-colors"
585
+ className="size-8 shrink-0"
584
586
  aria-label={removeLabel}
585
587
  >
586
- <Trash2 className="h-3.5 w-3.5 text-destructive" />
587
- </button>
588
+ <Trash2 className="size-3.5 text-destructive" />
589
+ </Button>
588
590
  </AccordionPrimitive.Header>
589
591
  );
590
592
  }
@@ -698,6 +700,7 @@ export function EditLoanScenarioModal({
698
700
  onOpenChange,
699
701
  initialData,
700
702
  onSave,
703
+ container,
701
704
  className,
702
705
  }: EditLoanScenarioModalProps) {
703
706
  const [form, setForm] = React.useState<LoanScenarioFormData>({
@@ -705,12 +708,18 @@ export function EditLoanScenarioModal({
705
708
  ...initialData,
706
709
  });
707
710
 
711
+ const initialSnapshot = React.useRef<string>("");
712
+
708
713
  React.useEffect(() => {
709
714
  if (open) {
710
- setForm({ ...LOAN_SCENARIO_DEFAULTS, ...initialData });
715
+ const data = { ...LOAN_SCENARIO_DEFAULTS, ...initialData };
716
+ setForm(data);
717
+ initialSnapshot.current = JSON.stringify(data);
711
718
  }
712
719
  }, [open]); // eslint-disable-line react-hooks/exhaustive-deps
713
720
 
721
+ const isDirty = JSON.stringify(form) !== initialSnapshot.current;
722
+
714
723
  const set = <K extends keyof LoanScenarioFormData>(
715
724
  key: K,
716
725
  val: LoanScenarioFormData[K],
@@ -762,7 +771,10 @@ export function EditLoanScenarioModal({
762
771
 
763
772
  return (
764
773
  <Dialog open={open} onOpenChange={onOpenChange}>
765
- <DialogContent className={cn("max-w-lg", className)}>
774
+ <DialogContent
775
+ className={cn("max-w-lg", className)}
776
+ container={container}
777
+ >
766
778
  <DialogHeader>
767
779
  <DialogTitle>Edit Loan Scenario</DialogTitle>
768
780
  </DialogHeader>
@@ -965,6 +977,7 @@ export function EditLoanScenarioModal({
965
977
  Cancel
966
978
  </Button>
967
979
  <Button
980
+ disabled={!isDirty}
968
981
  onClick={() => {
969
982
  onSave(form);
970
983
  onOpenChange(false);
@@ -989,20 +1002,26 @@ export function EditAssetsModal({
989
1002
  onSave,
990
1003
  mainApplicantName = "Main Applicant",
991
1004
  coApplicantName = "Co-Applicant",
1005
+ container,
992
1006
  className,
993
1007
  }: EditAssetsModalProps) {
994
1008
  const [items, setItems] = React.useState<AssetLineItem[]>(
995
1009
  initialItems.length > 0 ? initialItems : [makeDefaultAssetItem()],
996
1010
  );
997
1011
 
1012
+ const initialSnapshot = React.useRef<string>("");
1013
+
998
1014
  React.useEffect(() => {
999
1015
  if (open) {
1000
- setItems(
1001
- initialItems.length > 0 ? initialItems : [makeDefaultAssetItem()],
1002
- );
1016
+ const data =
1017
+ initialItems.length > 0 ? initialItems : [makeDefaultAssetItem()];
1018
+ setItems(data);
1019
+ initialSnapshot.current = JSON.stringify(data);
1003
1020
  }
1004
1021
  }, [open]); // eslint-disable-line react-hooks/exhaustive-deps
1005
1022
 
1023
+ const isDirty = JSON.stringify(items) !== initialSnapshot.current;
1024
+
1006
1025
  const updateItem = <K extends keyof AssetLineItem>(
1007
1026
  id: string,
1008
1027
  key: K,
@@ -1021,7 +1040,10 @@ export function EditAssetsModal({
1021
1040
 
1022
1041
  return (
1023
1042
  <Dialog open={open} onOpenChange={onOpenChange}>
1024
- <DialogContent className={cn("max-w-lg", className)}>
1043
+ <DialogContent
1044
+ className={cn("max-w-lg", className)}
1045
+ container={container}
1046
+ >
1025
1047
  <DialogHeader>
1026
1048
  <DialogTitle>Edit Assets</DialogTitle>
1027
1049
  </DialogHeader>
@@ -1329,6 +1351,7 @@ export function EditAssetsModal({
1329
1351
  Cancel
1330
1352
  </Button>
1331
1353
  <Button
1354
+ disabled={!isDirty}
1332
1355
  onClick={() => {
1333
1356
  onSave(items);
1334
1357
  onOpenChange(false);
@@ -1353,20 +1376,26 @@ export function EditDebtsModal({
1353
1376
  onSave,
1354
1377
  mainApplicantName = "Main Applicant",
1355
1378
  coApplicantName = "Co-Applicant",
1379
+ container,
1356
1380
  className,
1357
1381
  }: EditDebtsModalProps) {
1358
1382
  const [items, setItems] = React.useState<DebtLineItem[]>(
1359
1383
  initialItems.length > 0 ? initialItems : [makeDefaultDebtItem()],
1360
1384
  );
1361
1385
 
1386
+ const initialSnapshot = React.useRef<string>("");
1387
+
1362
1388
  React.useEffect(() => {
1363
1389
  if (open) {
1364
- setItems(
1365
- initialItems.length > 0 ? initialItems : [makeDefaultDebtItem()],
1366
- );
1390
+ const data =
1391
+ initialItems.length > 0 ? initialItems : [makeDefaultDebtItem()];
1392
+ setItems(data);
1393
+ initialSnapshot.current = JSON.stringify(data);
1367
1394
  }
1368
1395
  }, [open]); // eslint-disable-line react-hooks/exhaustive-deps
1369
1396
 
1397
+ const isDirty = JSON.stringify(items) !== initialSnapshot.current;
1398
+
1370
1399
  const updateItem = <K extends keyof DebtLineItem>(
1371
1400
  id: string,
1372
1401
  key: K,
@@ -1385,7 +1414,10 @@ export function EditDebtsModal({
1385
1414
 
1386
1415
  return (
1387
1416
  <Dialog open={open} onOpenChange={onOpenChange}>
1388
- <DialogContent className={cn("max-w-xl", className)}>
1417
+ <DialogContent
1418
+ className={cn("max-w-xl", className)}
1419
+ container={container}
1420
+ >
1389
1421
  <DialogHeader>
1390
1422
  <DialogTitle>Edit Debts</DialogTitle>
1391
1423
  </DialogHeader>
@@ -1722,6 +1754,7 @@ export function EditDebtsModal({
1722
1754
  Cancel
1723
1755
  </Button>
1724
1756
  <Button
1757
+ disabled={!isDirty}
1725
1758
  onClick={() => {
1726
1759
  onSave(items);
1727
1760
  onOpenChange(false);
@@ -1745,6 +1778,7 @@ export function EditAboutApplicantModal({
1745
1778
  applicantLabel = "Applicant",
1746
1779
  initialData,
1747
1780
  onSave,
1781
+ container,
1748
1782
  className,
1749
1783
  }: EditAboutApplicantModalProps) {
1750
1784
  const [form, setForm] = React.useState<AboutApplicantFormData>({
@@ -1752,12 +1786,18 @@ export function EditAboutApplicantModal({
1752
1786
  ...initialData,
1753
1787
  });
1754
1788
 
1789
+ const initialSnapshot = React.useRef<string>("");
1790
+
1755
1791
  React.useEffect(() => {
1756
1792
  if (open) {
1757
- setForm({ ...ABOUT_APPLICANT_DEFAULTS, ...initialData });
1793
+ const data = { ...ABOUT_APPLICANT_DEFAULTS, ...initialData };
1794
+ setForm(data);
1795
+ initialSnapshot.current = JSON.stringify(data);
1758
1796
  }
1759
1797
  }, [open]); // eslint-disable-line react-hooks/exhaustive-deps
1760
1798
 
1799
+ const isDirty = JSON.stringify(form) !== initialSnapshot.current;
1800
+
1761
1801
  const setStr = (key: keyof AboutApplicantFormData) => (val: string) =>
1762
1802
  setForm((prev) => ({ ...prev, [key]: val }));
1763
1803
 
@@ -1781,7 +1821,10 @@ export function EditAboutApplicantModal({
1781
1821
 
1782
1822
  return (
1783
1823
  <Dialog open={open} onOpenChange={onOpenChange}>
1784
- <DialogContent className={cn("max-w-md", className)}>
1824
+ <DialogContent
1825
+ className={cn("max-w-md", className)}
1826
+ container={container}
1827
+ >
1785
1828
  <DialogHeader>
1786
1829
  <DialogTitle>Edit About {applicantLabel}</DialogTitle>
1787
1830
  </DialogHeader>
@@ -2080,6 +2123,7 @@ export function EditAboutApplicantModal({
2080
2123
  Cancel
2081
2124
  </Button>
2082
2125
  <Button
2126
+ disabled={!isDirty}
2083
2127
  onClick={() => {
2084
2128
  onSave(form);
2085
2129
  onOpenChange(false);
@@ -2103,6 +2147,7 @@ export function EditIncomeModal({
2103
2147
  applicantLabel = "Applicant",
2104
2148
  initialData,
2105
2149
  onSave,
2150
+ container,
2106
2151
  className,
2107
2152
  }: EditIncomeModalProps) {
2108
2153
  const defaultItems = React.useMemo(
@@ -2115,16 +2160,20 @@ export function EditIncomeModal({
2115
2160
 
2116
2161
  const [items, setItems] = React.useState<IncomeItem[]>(defaultItems);
2117
2162
 
2163
+ const initialSnapshot = React.useRef<string>("");
2164
+
2118
2165
  React.useEffect(() => {
2119
2166
  if (open) {
2120
- setItems(
2121
- initialData?.items?.length
2122
- ? initialData.items
2123
- : [makeDefaultIncomeItem()],
2124
- );
2167
+ const data = initialData?.items?.length
2168
+ ? initialData.items
2169
+ : [makeDefaultIncomeItem()];
2170
+ setItems(data);
2171
+ initialSnapshot.current = JSON.stringify(data);
2125
2172
  }
2126
2173
  }, [open]); // eslint-disable-line react-hooks/exhaustive-deps
2127
2174
 
2175
+ const isDirty = JSON.stringify(items) !== initialSnapshot.current;
2176
+
2128
2177
  const updateItem = <K extends keyof IncomeItem>(
2129
2178
  id: string,
2130
2179
  key: K,
@@ -2143,7 +2192,10 @@ export function EditIncomeModal({
2143
2192
 
2144
2193
  return (
2145
2194
  <Dialog open={open} onOpenChange={onOpenChange}>
2146
- <DialogContent className={cn("max-w-lg", className)}>
2195
+ <DialogContent
2196
+ className={cn("max-w-lg", className)}
2197
+ container={container}
2198
+ >
2147
2199
  <DialogHeader>
2148
2200
  <DialogTitle>Edit Income {applicantLabel}</DialogTitle>
2149
2201
  </DialogHeader>
@@ -2342,6 +2394,7 @@ export function EditIncomeModal({
2342
2394
  Cancel
2343
2395
  </Button>
2344
2396
  <Button
2397
+ disabled={!isDirty}
2345
2398
  onClick={() => {
2346
2399
  onSave({ items });
2347
2400
  onOpenChange(false);
@@ -2365,6 +2418,7 @@ export function EditExpensesModal({
2365
2418
  applicantLabel = "Applicant",
2366
2419
  initialData,
2367
2420
  onSave,
2421
+ container,
2368
2422
  className,
2369
2423
  }: EditExpensesModalProps) {
2370
2424
  const defaultItems = React.useMemo(
@@ -2377,16 +2431,20 @@ export function EditExpensesModal({
2377
2431
 
2378
2432
  const [items, setItems] = React.useState<ExpenseItem[]>(defaultItems);
2379
2433
 
2434
+ const initialSnapshot = React.useRef<string>("");
2435
+
2380
2436
  React.useEffect(() => {
2381
2437
  if (open) {
2382
- setItems(
2383
- initialData?.items?.length
2384
- ? initialData.items
2385
- : [makeDefaultExpenseItem()],
2386
- );
2438
+ const data = initialData?.items?.length
2439
+ ? initialData.items
2440
+ : [makeDefaultExpenseItem()];
2441
+ setItems(data);
2442
+ initialSnapshot.current = JSON.stringify(data);
2387
2443
  }
2388
2444
  }, [open]); // eslint-disable-line react-hooks/exhaustive-deps
2389
2445
 
2446
+ const isDirty = JSON.stringify(items) !== initialSnapshot.current;
2447
+
2390
2448
  const updateItem = <K extends keyof ExpenseItem>(
2391
2449
  id: string,
2392
2450
  key: K,
@@ -2405,7 +2463,10 @@ export function EditExpensesModal({
2405
2463
 
2406
2464
  return (
2407
2465
  <Dialog open={open} onOpenChange={onOpenChange}>
2408
- <DialogContent className={cn("max-w-lg", className)}>
2466
+ <DialogContent
2467
+ className={cn("max-w-lg", className)}
2468
+ container={container}
2469
+ >
2409
2470
  <DialogHeader>
2410
2471
  <DialogTitle>Edit Expenses {applicantLabel}</DialogTitle>
2411
2472
  </DialogHeader>
@@ -2514,6 +2575,7 @@ export function EditExpensesModal({
2514
2575
  Cancel
2515
2576
  </Button>
2516
2577
  <Button
2578
+ disabled={!isDirty}
2517
2579
  onClick={() => {
2518
2580
  onSave({ items });
2519
2581
  onOpenChange(false);