@wealthx/shadcn 1.1.0 → 1.2.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.
- package/.turbo/turbo-build.log +235 -154
- package/CHANGELOG.md +12 -0
- package/dist/{chunk-6OJF6XRN.mjs → chunk-24FUO7TD.mjs} +4 -8
- package/dist/{chunk-4AJ5HWHD.mjs → chunk-2I5S2AMY.mjs} +3 -3
- package/dist/{chunk-GPRJQ24C.mjs → chunk-34NWQURD.mjs} +2 -2
- package/dist/{chunk-MQ72DIBH.mjs → chunk-3GF7OVTP.mjs} +14 -5
- package/dist/chunk-3WMX6KWS.mjs +245 -0
- package/dist/{chunk-PMKODV6M.mjs → chunk-462HMNO4.mjs} +6 -10
- package/dist/chunk-4CX4SBRO.mjs +153 -0
- package/dist/chunk-4MN6UQHG.mjs +443 -0
- package/dist/chunk-4Y6R4WEC.mjs +250 -0
- package/dist/{chunk-BGP2N52Z.mjs → chunk-66MI7Q4B.mjs} +5 -5
- package/dist/{chunk-CGOKTPXU.mjs → chunk-6JQFUE5I.mjs} +20 -23
- package/dist/{chunk-Z3MK2KKZ.mjs → chunk-7DHU4VGG.mjs} +7 -3
- package/dist/chunk-7MMXNK3C.mjs +317 -0
- package/dist/{chunk-VZ2NR7L3.mjs → chunk-7PYJD5JI.mjs} +35 -27
- package/dist/{chunk-JU2RUWHF.mjs → chunk-7XJHLGUV.mjs} +1 -1
- package/dist/{chunk-BMFN37JH.mjs → chunk-7YAU5CY6.mjs} +1 -1
- package/dist/chunk-A56YQQHG.mjs +402 -0
- package/dist/{chunk-GLW2UO6O.mjs → chunk-BL3DXM2X.mjs} +84 -62
- package/dist/{chunk-SLWCCURD.mjs → chunk-CLIN5525.mjs} +8 -4
- package/dist/{chunk-3VQNJ235.mjs → chunk-CSDO6VBW.mjs} +7 -0
- package/dist/chunk-D4ILTPOG.mjs +293 -0
- package/dist/{chunk-HS7TFG7V.mjs → chunk-D6ID6M4V.mjs} +1 -1
- package/dist/chunk-DOH3EHX7.mjs +378 -0
- package/dist/{chunk-MJIEMGRD.mjs → chunk-EFRENWEJ.mjs} +9 -17
- package/dist/{chunk-YBXCIF5Q.mjs → chunk-ERGGHC2V.mjs} +36 -49
- package/dist/{chunk-OXQQNQZI.mjs → chunk-FEZKMUCF.mjs} +10 -1
- package/dist/{chunk-55CEW76V.mjs → chunk-FH6QVUVZ.mjs} +1 -1
- package/dist/chunk-FMAXJ2SI.mjs +71 -0
- package/dist/chunk-FZIXGLMV.mjs +173 -0
- package/dist/chunk-GGM2UYGG.mjs +273 -0
- package/dist/{chunk-DS2AMHN2.mjs → chunk-GYMYRIZP.mjs} +2 -2
- package/dist/{chunk-KQDD5MU3.mjs → chunk-H45TKD34.mjs} +5 -5
- package/dist/{chunk-BBJBJSXQ.mjs → chunk-J5UICVJS.mjs} +1 -1
- package/dist/{chunk-RL772EH7.mjs → chunk-JHJHG4GO.mjs} +4 -12
- package/dist/{chunk-RN67642N.mjs → chunk-JNQORUPP.mjs} +49 -42
- package/dist/{chunk-5JGQAAQV.mjs → chunk-K3JYD4IU.mjs} +86 -63
- package/dist/{chunk-FHNT55I5.mjs → chunk-KUDCQ4FI.mjs} +4 -4
- package/dist/{chunk-UEL4RD5P.mjs → chunk-LHYCMLVA.mjs} +82 -68
- package/dist/{chunk-NLLKTU4B.mjs → chunk-LLVQKSU3.mjs} +21 -17
- package/dist/{chunk-KKHTJNMM.mjs → chunk-MARPPFOJ.mjs} +8 -4
- package/dist/{chunk-6AFMNC42.mjs → chunk-N2PT566P.mjs} +15 -11
- package/dist/{chunk-YN5SYTOO.mjs → chunk-NQPOYKAQ.mjs} +9 -5
- package/dist/{chunk-ZZV5JVNW.mjs → chunk-NSLMILBT.mjs} +3 -7
- package/dist/chunk-OGOYQ7BG.mjs +150 -0
- package/dist/chunk-OPNQAVVH.mjs +162 -0
- package/dist/{chunk-3NQGYJEZ.mjs → chunk-P6AM5V7O.mjs} +10 -18
- package/dist/{chunk-CZ3BW5GL.mjs → chunk-P76HMUI6.mjs} +5 -11
- package/dist/chunk-PCPLO5HT.mjs +671 -0
- package/dist/chunk-PG6K5XEC.mjs +475 -0
- package/dist/{chunk-DDPA2XXS.mjs → chunk-PMB3A7V3.mjs} +2 -2
- package/dist/chunk-PR6V5XKM.mjs +209 -0
- package/dist/{chunk-46OFHMQA.mjs → chunk-Q76O3RIQ.mjs} +10 -6
- package/dist/chunk-RGU7HOEC.mjs +140 -0
- package/dist/{chunk-JF4PHPD5.mjs → chunk-RGVKLTLH.mjs} +4 -4
- package/dist/{chunk-VG6UF6UT.mjs → chunk-RP3SQYA3.mjs} +2 -2
- package/dist/chunk-RYCLWMZ7.mjs +162 -0
- package/dist/chunk-SIZMLSRU.mjs +162 -0
- package/dist/chunk-SPJ5KXW7.mjs +199 -0
- package/dist/chunk-SWGT756Z.mjs +210 -0
- package/dist/chunk-SYOD63OZ.mjs +225 -0
- package/dist/chunk-TS2ZX2VS.mjs +270 -0
- package/dist/chunk-UFYSFDER.mjs +42 -0
- package/dist/chunk-VACKZOMY.mjs +190 -0
- package/dist/chunk-VLQZANBF.mjs +42 -0
- package/dist/chunk-VPBN3WOO.mjs +164 -0
- package/dist/chunk-WA6O6EUR.mjs +1885 -0
- package/dist/{chunk-E3K6O4FZ.mjs → chunk-WAZD7NFU.mjs} +5 -2
- package/dist/chunk-WG6JGJXB.mjs +165 -0
- package/dist/{chunk-I64K754C.mjs → chunk-WNGWBVLV.mjs} +2 -2
- package/dist/{chunk-3U7SD3MS.mjs → chunk-WOEHFRGB.mjs} +3 -3
- package/dist/{chunk-DKZRJOMF.mjs → chunk-XIRTEFKH.mjs} +12 -12
- package/dist/chunk-Y6DWJSKZ.mjs +79 -0
- package/dist/{chunk-CJ46PDXE.mjs → chunk-ZRO5JO3H.mjs} +106 -66
- package/dist/{chunk-VYMHBV6D.mjs → chunk-ZU4NV6RG.mjs} +5 -3
- package/dist/components/ui/accordion.js +40 -4
- package/dist/components/ui/accordion.mjs +2 -2
- package/dist/components/ui/add-column-modal.js +789 -0
- package/dist/components/ui/add-column-modal.mjs +17 -0
- package/dist/components/ui/add-lead-modal.js +647 -0
- package/dist/components/ui/add-lead-modal.mjs +16 -0
- package/dist/components/ui/ai-assistant-drawer.js +686 -0
- package/dist/components/ui/ai-assistant-drawer.mjs +16 -0
- package/dist/components/ui/alert-dialog.js +37 -5
- package/dist/components/ui/alert-dialog.mjs +4 -4
- package/dist/components/ui/alert.js +37 -11
- package/dist/components/ui/alert.mjs +2 -2
- package/dist/components/ui/avatar.js +36 -8
- package/dist/components/ui/avatar.mjs +2 -2
- package/dist/components/ui/backoffice-alert-history-chart.js +624 -0
- package/dist/components/ui/backoffice-alert-history-chart.mjs +16 -0
- package/dist/components/ui/backoffice-contact-history-chart.js +687 -0
- package/dist/components/ui/backoffice-contact-history-chart.mjs +16 -0
- package/dist/components/ui/badge.js +37 -2
- package/dist/components/ui/badge.mjs +2 -2
- package/dist/components/ui/borrowing-capacity-line-chart.js +640 -0
- package/dist/components/ui/borrowing-capacity-line-chart.mjs +16 -0
- package/dist/components/ui/button.js +35 -3
- package/dist/components/ui/button.mjs +2 -2
- package/dist/components/ui/calendar.js +43 -19
- package/dist/components/ui/calendar.mjs +3 -3
- package/dist/components/ui/card.js +40 -4
- package/dist/components/ui/card.mjs +2 -2
- package/dist/components/ui/cash-balance-line-chart.js +628 -0
- package/dist/components/ui/cash-balance-line-chart.mjs +16 -0
- package/dist/components/ui/cashflow-bar-chart.js +124 -70
- package/dist/components/ui/cashflow-bar-chart.mjs +8 -8
- package/dist/components/ui/checkbox.js +36 -5
- package/dist/components/ui/checkbox.mjs +2 -3
- package/dist/components/ui/chip.js +37 -2
- package/dist/components/ui/chip.mjs +3 -3
- package/dist/components/ui/combobox.js +68 -49
- package/dist/components/ui/combobox.mjs +2 -2
- package/dist/components/ui/data-table.js +160 -88
- package/dist/components/ui/data-table.mjs +10 -11
- package/dist/components/ui/date-picker.js +44 -20
- package/dist/components/ui/date-picker.mjs +6 -7
- package/dist/components/ui/dialog.js +44 -12
- package/dist/components/ui/dialog.mjs +4 -4
- package/dist/components/ui/drawer.js +46 -10
- package/dist/components/ui/drawer.mjs +3 -3
- package/dist/components/ui/dropdown-menu.js +40 -16
- package/dist/components/ui/dropdown-menu.mjs +3 -3
- package/dist/components/ui/empty.js +41 -5
- package/dist/components/ui/empty.mjs +2 -2
- package/dist/components/ui/expense-bar-chart.js +166 -67
- package/dist/components/ui/expense-bar-chart.mjs +8 -8
- package/dist/components/ui/field.js +53 -21
- package/dist/components/ui/field.mjs +4 -4
- package/dist/components/ui/financial-cards.js +1002 -0
- package/dist/components/ui/financial-cards.mjs +24 -0
- package/dist/components/ui/financial-drawers.js +637 -0
- package/dist/components/ui/financial-drawers.mjs +17 -0
- package/dist/components/ui/financial-primitives.js +218 -0
- package/dist/components/ui/financial-primitives.mjs +22 -0
- package/dist/components/ui/financial-sections.js +1422 -0
- package/dist/components/ui/financial-sections.mjs +30 -0
- package/dist/components/ui/form-primitives.js +682 -0
- package/dist/components/ui/form-primitives.mjs +19 -0
- package/dist/components/ui/income-bar-chart.js +164 -66
- package/dist/components/ui/income-bar-chart.mjs +8 -8
- package/dist/components/ui/input-group.js +43 -7
- package/dist/components/ui/input-group.mjs +5 -5
- package/dist/components/ui/input-otp.js +39 -3
- package/dist/components/ui/input-otp.mjs +2 -2
- package/dist/components/ui/input.js +34 -2
- package/dist/components/ui/input.mjs +2 -2
- package/dist/components/ui/kanban-column.js +1143 -0
- package/dist/components/ui/kanban-column.mjs +20 -0
- package/dist/components/ui/label.js +35 -7
- package/dist/components/ui/label.mjs +2 -2
- package/dist/components/ui/opportunity-card.js +960 -0
- package/dist/components/ui/opportunity-card.mjs +20 -0
- package/dist/components/ui/opportunity-edit-modals.js +3360 -0
- package/dist/components/ui/opportunity-edit-modals.mjs +37 -0
- package/dist/components/ui/opportunity-summary-tab.js +4365 -0
- package/dist/components/ui/opportunity-summary-tab.mjs +34 -0
- package/dist/components/ui/pagination.js +35 -3
- package/dist/components/ui/pagination.mjs +3 -3
- package/dist/components/ui/pipeline-alerts.js +103 -0
- package/dist/components/ui/pipeline-alerts.mjs +8 -0
- package/dist/components/ui/pipeline-board.js +1408 -0
- package/dist/components/ui/pipeline-board.mjs +24 -0
- package/dist/components/ui/pipeline-chart.js +216 -0
- package/dist/components/ui/pipeline-chart.mjs +10 -0
- package/dist/components/ui/pipeline-dialogs.js +1183 -0
- package/dist/components/ui/pipeline-dialogs.mjs +23 -0
- package/dist/components/ui/pipeline-primitives.js +300 -0
- package/dist/components/ui/pipeline-primitives.mjs +11 -0
- package/dist/components/ui/popover.js +45 -4
- package/dist/components/ui/popover.mjs +3 -3
- package/dist/components/ui/progress.js +33 -1
- package/dist/components/ui/progress.mjs +2 -2
- package/dist/components/ui/property-cashflow-doughnut-chart.js +523 -0
- package/dist/components/ui/property-cashflow-doughnut-chart.mjs +16 -0
- package/dist/components/ui/property-debt-equity-doughnut-chart.js +521 -0
- package/dist/components/ui/property-debt-equity-doughnut-chart.mjs +16 -0
- package/dist/components/ui/property-mobile-estimate-line-chart.js +683 -0
- package/dist/components/ui/property-mobile-estimate-line-chart.mjs +16 -0
- package/dist/components/ui/radio-group.js +33 -1
- package/dist/components/ui/radio-group.mjs +2 -2
- package/dist/components/ui/select.js +66 -26
- package/dist/components/ui/select.mjs +3 -3
- package/dist/components/ui/separator.js +33 -1
- package/dist/components/ui/separator.mjs +2 -2
- package/dist/components/ui/sheet.js +37 -9
- package/dist/components/ui/sheet.mjs +3 -3
- package/dist/components/ui/skeleton.js +33 -1
- package/dist/components/ui/skeleton.mjs +2 -2
- package/dist/components/ui/slider.js +86 -102
- package/dist/components/ui/slider.mjs +2 -2
- package/dist/components/ui/spinner.js +33 -1
- package/dist/components/ui/spinner.mjs +2 -2
- package/dist/components/ui/stage-timeline.js +579 -0
- package/dist/components/ui/stage-timeline.mjs +15 -0
- package/dist/components/ui/switch.js +37 -4
- package/dist/components/ui/switch.mjs +2 -3
- package/dist/components/ui/table.js +37 -5
- package/dist/components/ui/table.mjs +2 -2
- package/dist/components/ui/tabs.js +36 -12
- package/dist/components/ui/tabs.mjs +2 -2
- package/dist/components/ui/textarea.js +34 -2
- package/dist/components/ui/textarea.mjs +2 -2
- package/dist/components/ui/toggle-group.js +35 -4
- package/dist/components/ui/toggle-group.mjs +3 -4
- package/dist/components/ui/toggle.js +35 -4
- package/dist/components/ui/toggle.mjs +2 -3
- package/dist/components/ui/tooltip.js +51 -22
- package/dist/components/ui/tooltip.mjs +3 -3
- package/dist/components/ui/transactions-expense-categories-doughnut-chart.js +528 -0
- package/dist/components/ui/transactions-expense-categories-doughnut-chart.mjs +16 -0
- package/dist/components/ui/transactions-income-expense-bar-chart.js +77 -39
- package/dist/components/ui/transactions-income-expense-bar-chart.mjs +8 -8
- package/dist/components/ui/transactions-liabilities-breakdown-doughnut-chart.js +528 -0
- package/dist/components/ui/transactions-liabilities-breakdown-doughnut-chart.mjs +16 -0
- package/dist/index.js +11620 -3832
- package/dist/index.mjs +333 -161
- package/dist/lib/theme-provider.js +10 -1
- package/dist/lib/theme-provider.mjs +1 -1
- package/dist/lib/typography.js +8 -0
- package/dist/lib/typography.mjs +3 -1
- package/dist/lib/utils.js +33 -1
- package/dist/lib/utils.mjs +1 -1
- package/dist/styles.css +1 -1
- package/package.json +140 -5
- package/src/components/index.tsx +296 -42
- package/src/components/ui/accordion.tsx +6 -3
- package/src/components/ui/add-column-modal.tsx +339 -0
- package/src/components/ui/add-lead-modal.tsx +290 -0
- package/src/components/ui/ai-assistant-drawer.tsx +408 -0
- package/src/components/ui/alert-dialog.tsx +80 -54
- package/src/components/ui/alert.tsx +28 -28
- package/src/components/ui/avatar.tsx +30 -29
- package/src/components/ui/backoffice-alert-history-chart.tsx +261 -0
- package/src/components/ui/backoffice-contact-history-chart.tsx +326 -0
- package/src/components/ui/badge.tsx +17 -15
- package/src/components/ui/borrowing-capacity-line-chart.tsx +359 -0
- package/src/components/ui/button.tsx +30 -27
- package/src/components/ui/calendar.tsx +53 -67
- package/src/components/ui/card.tsx +27 -24
- package/src/components/ui/cash-balance-line-chart.tsx +304 -0
- package/src/components/ui/cashflow-bar-chart.tsx +106 -78
- package/src/components/ui/chart-shared.tsx +176 -15
- package/src/components/ui/checkbox.tsx +30 -26
- package/src/components/ui/combobox.tsx +78 -72
- package/src/components/ui/data-table.tsx +160 -99
- package/src/components/ui/date-picker.tsx +0 -2
- package/src/components/ui/dialog.tsx +70 -60
- package/src/components/ui/drawer.tsx +57 -48
- package/src/components/ui/dropdown-menu.tsx +90 -82
- package/src/components/ui/empty.tsx +31 -27
- package/src/components/ui/expense-bar-chart.tsx +85 -66
- package/src/components/ui/field.tsx +70 -62
- package/src/components/ui/financial-cards.tsx +830 -0
- package/src/components/ui/financial-drawers.tsx +339 -0
- package/src/components/ui/financial-primitives.tsx +331 -0
- package/src/components/ui/financial-sections.tsx +672 -0
- package/src/components/ui/form-primitives.tsx +536 -0
- package/src/components/ui/income-bar-chart.tsx +81 -61
- package/src/components/ui/input-group.tsx +41 -34
- package/src/components/ui/input-otp.tsx +29 -24
- package/src/components/ui/input.tsx +8 -8
- package/src/components/ui/kanban-column.tsx +333 -0
- package/src/components/ui/label.tsx +9 -12
- package/src/components/ui/opportunity-card.tsx +616 -0
- package/src/components/ui/opportunity-edit-modals.tsx +2528 -0
- package/src/components/ui/opportunity-summary-tab.tsx +579 -0
- package/src/components/ui/pipeline-alerts.tsx +74 -0
- package/src/components/ui/pipeline-board.tsx +268 -0
- package/src/components/ui/pipeline-chart.tsx +173 -0
- package/src/components/ui/pipeline-dialogs.tsx +303 -0
- package/src/components/ui/pipeline-primitives.tsx +108 -0
- package/src/components/ui/popover.tsx +41 -36
- package/src/components/ui/property-cashflow-doughnut-chart.tsx +189 -0
- package/src/components/ui/property-debt-equity-doughnut-chart.tsx +186 -0
- package/src/components/ui/property-mobile-estimate-line-chart.tsx +395 -0
- package/src/components/ui/select.tsx +65 -52
- package/src/components/ui/sheet.tsx +55 -52
- package/src/components/ui/slider.tsx +54 -77
- package/src/components/ui/stage-timeline.tsx +205 -0
- package/src/components/ui/switch.tsx +42 -29
- package/src/components/ui/table.tsx +28 -28
- package/src/components/ui/tabs.tsx +22 -28
- package/src/components/ui/textarea.tsx +8 -8
- package/src/components/ui/toggle-group.tsx +0 -2
- package/src/components/ui/toggle.tsx +13 -15
- package/src/components/ui/tooltip.tsx +30 -28
- package/src/components/ui/transactions-expense-categories-doughnut-chart.tsx +192 -0
- package/src/components/ui/transactions-income-expense-bar-chart.tsx +47 -39
- package/src/components/ui/transactions-liabilities-breakdown-doughnut-chart.tsx +192 -0
- package/src/lib/theme-provider.tsx +10 -0
- package/src/lib/typography.ts +9 -0
- package/src/lib/utils.ts +41 -3
- package/src/styles/globals.css +371 -124
- package/src/styles/styles-css.ts +1 -1
- package/tsup.config.ts +27 -0
- package/dist/chunk-3EQP72AW.mjs +0 -58
- package/dist/chunk-K74JRTJR.mjs +0 -105
- package/dist/chunk-V7CNWJT3.mjs +0 -10
|
@@ -0,0 +1,830 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import {
|
|
3
|
+
Baby,
|
|
4
|
+
Building2,
|
|
5
|
+
Car,
|
|
6
|
+
ChevronDown,
|
|
7
|
+
CreditCard,
|
|
8
|
+
Dumbbell,
|
|
9
|
+
GraduationCap,
|
|
10
|
+
HeartPulse,
|
|
11
|
+
Landmark,
|
|
12
|
+
Receipt,
|
|
13
|
+
RefreshCw,
|
|
14
|
+
Shield,
|
|
15
|
+
ShoppingCart,
|
|
16
|
+
Shirt,
|
|
17
|
+
Tv,
|
|
18
|
+
UtensilsCrossed,
|
|
19
|
+
Zap,
|
|
20
|
+
} from "lucide-react";
|
|
21
|
+
import type { LucideIcon } from "lucide-react";
|
|
22
|
+
import { cn } from "@/lib/utils";
|
|
23
|
+
import { Badge } from "./badge";
|
|
24
|
+
import { RadioGroup, RadioGroupItem } from "./radio-group";
|
|
25
|
+
import {
|
|
26
|
+
FinancialDetailField,
|
|
27
|
+
FinancialLineItem,
|
|
28
|
+
FinancialLvrBar,
|
|
29
|
+
FinancialSubsectionTitle,
|
|
30
|
+
FinancialSubtotalBlock,
|
|
31
|
+
FinancialSubtotalFrame,
|
|
32
|
+
} from "./financial-primitives";
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Financial card molecules — WealthX DS (Level 3)
|
|
36
|
+
*
|
|
37
|
+
* Composed from financial primitives (Level 2) + shadcn atoms.
|
|
38
|
+
* Used inside summary report drawers, opportunity detail panels,
|
|
39
|
+
* and any financial data section in the backoffice.
|
|
40
|
+
*
|
|
41
|
+
* Component inventory:
|
|
42
|
+
* PropertyCard — property holding with optional bank link + expandable loan detail
|
|
43
|
+
* DebtCard — mortgage / investment-loan breakdown
|
|
44
|
+
* OtherLiabilityCard — credit card or personal loan card (discriminated by `type`)
|
|
45
|
+
* AlertCard — single alert item with severity indicator + dismiss/snooze actions
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
// PropertyCard
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
|
|
52
|
+
export interface PropertyCardProps {
|
|
53
|
+
/** Street address or property name */
|
|
54
|
+
address: string;
|
|
55
|
+
/** Badge label: "Owner Occupier", "Investment", etc. */
|
|
56
|
+
type?: string;
|
|
57
|
+
/** Estimated property value e.g. "$1,200,000" */
|
|
58
|
+
estimated: string;
|
|
59
|
+
/**
|
|
60
|
+
* When true removes the outer card border.
|
|
61
|
+
* Use inside `FinancialViewSection` where the card is already inside a bordered container.
|
|
62
|
+
*/
|
|
63
|
+
borderless?: boolean;
|
|
64
|
+
|
|
65
|
+
/** Whether a bank mortgage account is linked to this property */
|
|
66
|
+
isLinkedToBank?: boolean;
|
|
67
|
+
/** Outstanding loan amount — only shown when isLinkedToBank */
|
|
68
|
+
loanAmount?: string;
|
|
69
|
+
/** Equity = estimated − loan — only shown when isLinkedToBank */
|
|
70
|
+
equity?: string;
|
|
71
|
+
/** LVR label e.g. "56% — Good" — only shown when isLinkedToBank */
|
|
72
|
+
lvr?: string;
|
|
73
|
+
/** LVR numeric percentage (drives color thresholds) */
|
|
74
|
+
lvrPercent?: number;
|
|
75
|
+
|
|
76
|
+
// — Expandable loan detail (visible only when isLinkedToBank && expanded) —
|
|
77
|
+
lenderName?: string;
|
|
78
|
+
interestRate?: string;
|
|
79
|
+
yearsRemaining?: string;
|
|
80
|
+
minRepayments?: string;
|
|
81
|
+
averageRepayments?: string;
|
|
82
|
+
redrawAmount?: string;
|
|
83
|
+
offsetAccount?: string;
|
|
84
|
+
|
|
85
|
+
// — SubtotalFrame footer —
|
|
86
|
+
totalMinRepayments?: string;
|
|
87
|
+
totalExtraRepayments?: string;
|
|
88
|
+
interestCharged?: string;
|
|
89
|
+
principlePaidOff?: string;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function PropertyCard({
|
|
93
|
+
address,
|
|
94
|
+
type,
|
|
95
|
+
estimated,
|
|
96
|
+
isLinkedToBank = false,
|
|
97
|
+
borderless = false,
|
|
98
|
+
loanAmount,
|
|
99
|
+
equity,
|
|
100
|
+
lvr,
|
|
101
|
+
lvrPercent = 0,
|
|
102
|
+
lenderName,
|
|
103
|
+
interestRate,
|
|
104
|
+
yearsRemaining,
|
|
105
|
+
minRepayments,
|
|
106
|
+
averageRepayments,
|
|
107
|
+
redrawAmount,
|
|
108
|
+
offsetAccount,
|
|
109
|
+
totalMinRepayments,
|
|
110
|
+
totalExtraRepayments,
|
|
111
|
+
interestCharged,
|
|
112
|
+
principlePaidOff,
|
|
113
|
+
}: PropertyCardProps) {
|
|
114
|
+
const [expanded, setExpanded] = React.useState(false);
|
|
115
|
+
|
|
116
|
+
return (
|
|
117
|
+
<div
|
|
118
|
+
className={cn(
|
|
119
|
+
"overflow-hidden flex flex-col",
|
|
120
|
+
!borderless && "border border-border",
|
|
121
|
+
)}
|
|
122
|
+
>
|
|
123
|
+
{/* Header row */}
|
|
124
|
+
{isLinkedToBank ? (
|
|
125
|
+
<button
|
|
126
|
+
type="button"
|
|
127
|
+
aria-expanded={expanded}
|
|
128
|
+
onClick={() => setExpanded((prev) => !prev)}
|
|
129
|
+
className="flex items-center gap-1.5 px-5 py-3 text-left cursor-pointer hover:opacity-85"
|
|
130
|
+
>
|
|
131
|
+
<span className="text-label-medium text-foreground">{address}</span>
|
|
132
|
+
{type && <Badge variant="outline">{type}</Badge>}
|
|
133
|
+
<span className="flex-1" />
|
|
134
|
+
<ChevronDown
|
|
135
|
+
className={cn(
|
|
136
|
+
"h-5 w-5 shrink-0 text-muted-foreground transition-transform duration-200",
|
|
137
|
+
expanded && "rotate-180",
|
|
138
|
+
)}
|
|
139
|
+
/>
|
|
140
|
+
</button>
|
|
141
|
+
) : (
|
|
142
|
+
<div className="flex items-center gap-1.5 px-5 py-3">
|
|
143
|
+
<span className="text-label-medium text-foreground">{address}</span>
|
|
144
|
+
{type && <Badge variant="outline">{type}</Badge>}
|
|
145
|
+
</div>
|
|
146
|
+
)}
|
|
147
|
+
|
|
148
|
+
{!isLinkedToBank && (
|
|
149
|
+
<p className="px-5 pb-2 text-xs italic text-muted-foreground">
|
|
150
|
+
No mortgage account linked to this property
|
|
151
|
+
</p>
|
|
152
|
+
)}
|
|
153
|
+
|
|
154
|
+
{/* Static fields — always visible */}
|
|
155
|
+
<div
|
|
156
|
+
className={cn(
|
|
157
|
+
"grid gap-5 px-5 py-[15px]",
|
|
158
|
+
isLinkedToBank ? "grid-cols-3" : "grid-cols-1",
|
|
159
|
+
)}
|
|
160
|
+
>
|
|
161
|
+
<FinancialDetailField label="Estimated Value" value={estimated} />
|
|
162
|
+
{isLinkedToBank && (
|
|
163
|
+
<>
|
|
164
|
+
<FinancialDetailField label="Loan Amount" value={loanAmount} />
|
|
165
|
+
<FinancialDetailField label="Equity" value={equity} />
|
|
166
|
+
</>
|
|
167
|
+
)}
|
|
168
|
+
</div>
|
|
169
|
+
|
|
170
|
+
{isLinkedToBank && (
|
|
171
|
+
<div className="px-5 pb-[15px]">
|
|
172
|
+
<FinancialLvrBar
|
|
173
|
+
percent={lvrPercent}
|
|
174
|
+
label={lvr ?? `${lvrPercent}%`}
|
|
175
|
+
/>
|
|
176
|
+
</div>
|
|
177
|
+
)}
|
|
178
|
+
|
|
179
|
+
{/* Expandable loan detail — CSS grid transition for smooth open/close */}
|
|
180
|
+
{isLinkedToBank && (
|
|
181
|
+
<div
|
|
182
|
+
className={cn(
|
|
183
|
+
"grid transition-[grid-template-rows] duration-200 ease-in-out",
|
|
184
|
+
expanded ? "grid-rows-[1fr]" : "grid-rows-[0fr]",
|
|
185
|
+
)}
|
|
186
|
+
>
|
|
187
|
+
<div className="overflow-hidden">
|
|
188
|
+
<div className="flex flex-col gap-5 border-t border-border px-5 py-[15px]">
|
|
189
|
+
<FinancialSubsectionTitle>Loan</FinancialSubsectionTitle>
|
|
190
|
+
<div className="grid grid-cols-4 gap-x-4 gap-y-4">
|
|
191
|
+
<FinancialDetailField
|
|
192
|
+
label="Name of Lender"
|
|
193
|
+
value={lenderName}
|
|
194
|
+
/>
|
|
195
|
+
<FinancialDetailField
|
|
196
|
+
label="Current Loan Amount"
|
|
197
|
+
value={loanAmount}
|
|
198
|
+
/>
|
|
199
|
+
<FinancialDetailField
|
|
200
|
+
label="Interest Rate"
|
|
201
|
+
value={interestRate}
|
|
202
|
+
/>
|
|
203
|
+
<FinancialDetailField
|
|
204
|
+
label="Years Remaining"
|
|
205
|
+
value={yearsRemaining}
|
|
206
|
+
/>
|
|
207
|
+
</div>
|
|
208
|
+
<div className="grid grid-cols-4 gap-x-4 gap-y-4">
|
|
209
|
+
<FinancialDetailField
|
|
210
|
+
label="Min Repayments"
|
|
211
|
+
value={minRepayments}
|
|
212
|
+
/>
|
|
213
|
+
<FinancialDetailField
|
|
214
|
+
label="Average Repayments"
|
|
215
|
+
value={averageRepayments}
|
|
216
|
+
/>
|
|
217
|
+
<FinancialDetailField
|
|
218
|
+
label="Redraw Amount"
|
|
219
|
+
value={redrawAmount}
|
|
220
|
+
/>
|
|
221
|
+
<FinancialDetailField
|
|
222
|
+
label="Offset Account"
|
|
223
|
+
value={offsetAccount}
|
|
224
|
+
/>
|
|
225
|
+
</div>
|
|
226
|
+
</div>
|
|
227
|
+
<FinancialSubtotalFrame>
|
|
228
|
+
<div className="grid grid-cols-4 gap-x-4 gap-y-1">
|
|
229
|
+
<FinancialDetailField
|
|
230
|
+
variant="footer"
|
|
231
|
+
label="Total Min Repayments"
|
|
232
|
+
value={totalMinRepayments}
|
|
233
|
+
/>
|
|
234
|
+
<FinancialDetailField
|
|
235
|
+
variant="footer"
|
|
236
|
+
label="Total Extra Repayments"
|
|
237
|
+
value={totalExtraRepayments}
|
|
238
|
+
/>
|
|
239
|
+
<FinancialDetailField
|
|
240
|
+
variant="footer"
|
|
241
|
+
label="Interest Charge"
|
|
242
|
+
value={interestCharged}
|
|
243
|
+
/>
|
|
244
|
+
<FinancialDetailField
|
|
245
|
+
variant="footer"
|
|
246
|
+
label="Principle Paid Off"
|
|
247
|
+
value={principlePaidOff}
|
|
248
|
+
/>
|
|
249
|
+
</div>
|
|
250
|
+
</FinancialSubtotalFrame>
|
|
251
|
+
</div>
|
|
252
|
+
</div>
|
|
253
|
+
)}
|
|
254
|
+
</div>
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// ---------------------------------------------------------------------------
|
|
259
|
+
// DebtCard
|
|
260
|
+
// ---------------------------------------------------------------------------
|
|
261
|
+
|
|
262
|
+
export interface DebtCardProps {
|
|
263
|
+
// — Header row —
|
|
264
|
+
lenderName?: string;
|
|
265
|
+
currentLoanAmount?: string;
|
|
266
|
+
/** Current annual interest rate e.g. `"6.04% p.a."` */
|
|
267
|
+
interestRate?: string;
|
|
268
|
+
// — Loan Stats section —
|
|
269
|
+
originalLoanAmount?: string;
|
|
270
|
+
/** Original term at drawdown e.g. `"30 years"` */
|
|
271
|
+
originalLoanTerm?: string;
|
|
272
|
+
/** Minimum scheduled monthly repayment */
|
|
273
|
+
monthlyRepayments?: string;
|
|
274
|
+
/** Available redraw balance */
|
|
275
|
+
redrawAmount?: string;
|
|
276
|
+
/** Linked offset account balance */
|
|
277
|
+
offsetAmount?: string;
|
|
278
|
+
/** Additional voluntary repayments per month */
|
|
279
|
+
extraLoanRepayments?: string;
|
|
280
|
+
// — SubtotalFrame footer —
|
|
281
|
+
/** Cumulative repayments made within the selected date range */
|
|
282
|
+
totalRepaymentsMade?: string;
|
|
283
|
+
/** Cumulative interest paid within the selected date range */
|
|
284
|
+
totalInterestPaid?: string;
|
|
285
|
+
/** Remaining loan term e.g. `"25 years"` */
|
|
286
|
+
yearsRemaining?: string;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
export function DebtCard({
|
|
290
|
+
lenderName,
|
|
291
|
+
currentLoanAmount,
|
|
292
|
+
interestRate,
|
|
293
|
+
originalLoanAmount,
|
|
294
|
+
originalLoanTerm,
|
|
295
|
+
monthlyRepayments,
|
|
296
|
+
redrawAmount,
|
|
297
|
+
offsetAmount,
|
|
298
|
+
extraLoanRepayments,
|
|
299
|
+
totalRepaymentsMade,
|
|
300
|
+
totalInterestPaid,
|
|
301
|
+
yearsRemaining,
|
|
302
|
+
}: DebtCardProps) {
|
|
303
|
+
return (
|
|
304
|
+
<div className="border border-border overflow-hidden flex flex-col h-full">
|
|
305
|
+
{/* Header row — fixed min-h so two cards in a grid row have aligned headers */}
|
|
306
|
+
<div className="grid grid-cols-3 gap-5 px-5 py-[15px] border-b border-border min-h-[7rem] items-start">
|
|
307
|
+
<FinancialDetailField label="Name of Lender" value={lenderName} />
|
|
308
|
+
<FinancialDetailField
|
|
309
|
+
label="Current Loan Amount"
|
|
310
|
+
value={currentLoanAmount}
|
|
311
|
+
/>
|
|
312
|
+
<FinancialDetailField
|
|
313
|
+
label="Current Interest Rate"
|
|
314
|
+
value={interestRate}
|
|
315
|
+
/>
|
|
316
|
+
</div>
|
|
317
|
+
|
|
318
|
+
{/* Loan Stats section */}
|
|
319
|
+
<div className="px-5 py-[15px] flex flex-col gap-5 flex-1">
|
|
320
|
+
<FinancialSubsectionTitle>Loan Stats</FinancialSubsectionTitle>
|
|
321
|
+
<div className="grid grid-cols-3 gap-5">
|
|
322
|
+
<FinancialDetailField
|
|
323
|
+
label="Original Loan Amount"
|
|
324
|
+
value={originalLoanAmount}
|
|
325
|
+
/>
|
|
326
|
+
<FinancialDetailField
|
|
327
|
+
label="Original Loan Term"
|
|
328
|
+
value={originalLoanTerm}
|
|
329
|
+
/>
|
|
330
|
+
<FinancialDetailField
|
|
331
|
+
label="Monthly Repayments"
|
|
332
|
+
value={monthlyRepayments}
|
|
333
|
+
/>
|
|
334
|
+
</div>
|
|
335
|
+
<div className="grid grid-cols-3 gap-5">
|
|
336
|
+
<FinancialDetailField label="Redraw Amount" value={redrawAmount} />
|
|
337
|
+
<FinancialDetailField label="Offset Account" value={offsetAmount} />
|
|
338
|
+
<FinancialDetailField
|
|
339
|
+
label="Extra Loan Repayments"
|
|
340
|
+
value={extraLoanRepayments}
|
|
341
|
+
/>
|
|
342
|
+
</div>
|
|
343
|
+
</div>
|
|
344
|
+
|
|
345
|
+
{/* Footer */}
|
|
346
|
+
<FinancialSubtotalFrame>
|
|
347
|
+
<div className="grid grid-cols-3 gap-x-4 gap-y-1">
|
|
348
|
+
<FinancialDetailField
|
|
349
|
+
variant="footer"
|
|
350
|
+
label="Total Repayments Made"
|
|
351
|
+
value={totalRepaymentsMade}
|
|
352
|
+
/>
|
|
353
|
+
<FinancialDetailField
|
|
354
|
+
variant="footer"
|
|
355
|
+
label="Total Interest Paid"
|
|
356
|
+
value={totalInterestPaid}
|
|
357
|
+
/>
|
|
358
|
+
<FinancialDetailField
|
|
359
|
+
variant="footer"
|
|
360
|
+
label="Years Remaining"
|
|
361
|
+
value={yearsRemaining}
|
|
362
|
+
/>
|
|
363
|
+
</div>
|
|
364
|
+
</FinancialSubtotalFrame>
|
|
365
|
+
</div>
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// ---------------------------------------------------------------------------
|
|
370
|
+
// OtherLiabilityCard
|
|
371
|
+
// ---------------------------------------------------------------------------
|
|
372
|
+
|
|
373
|
+
export type OtherLiabilityType = "credit_card" | "personal_loan";
|
|
374
|
+
|
|
375
|
+
export interface OtherLiabilityCardProps {
|
|
376
|
+
/**
|
|
377
|
+
* Discriminates the field set and footer layout:
|
|
378
|
+
* - `"credit_card"` → Card Stats (Credit Limit, Min Payment, Annual Fee)
|
|
379
|
+
* - `"personal_loan"` → Loan Stats (Original Amount, Term, Monthly Repayments)
|
|
380
|
+
*/
|
|
381
|
+
type: OtherLiabilityType;
|
|
382
|
+
lenderName?: string;
|
|
383
|
+
/** Annual interest rate e.g. `"19.99% p.a."` */
|
|
384
|
+
interestRate?: string;
|
|
385
|
+
// ── Credit card fields ──
|
|
386
|
+
/** Current outstanding balance on the card */
|
|
387
|
+
currentBalance?: string;
|
|
388
|
+
/** Approved credit limit */
|
|
389
|
+
creditLimit?: string;
|
|
390
|
+
/** Minimum required monthly payment */
|
|
391
|
+
minMonthlyPayment?: string;
|
|
392
|
+
annualFee?: string;
|
|
393
|
+
/** Pre-computed available credit: `creditLimit − currentBalance` */
|
|
394
|
+
availableCredit?: string;
|
|
395
|
+
// ── Personal loan fields ──
|
|
396
|
+
/** Current outstanding loan balance */
|
|
397
|
+
currentLoanAmount?: string;
|
|
398
|
+
originalLoanAmount?: string;
|
|
399
|
+
/** Original term at drawdown e.g. `"5 years"` */
|
|
400
|
+
originalLoanTerm?: string;
|
|
401
|
+
/** Minimum scheduled monthly repayment */
|
|
402
|
+
monthlyRepayments?: string;
|
|
403
|
+
/** Remaining term e.g. `"2.5 years"` */
|
|
404
|
+
currentLoanTermLeft?: string;
|
|
405
|
+
// ── Shared footer ──
|
|
406
|
+
/** Cumulative repayments made within the selected date range */
|
|
407
|
+
totalRepaymentsMade?: string;
|
|
408
|
+
/** Cumulative interest paid within the selected date range */
|
|
409
|
+
totalInterestPaid?: string;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
export function OtherLiabilityCard({
|
|
413
|
+
type,
|
|
414
|
+
lenderName,
|
|
415
|
+
interestRate,
|
|
416
|
+
currentBalance,
|
|
417
|
+
creditLimit,
|
|
418
|
+
minMonthlyPayment,
|
|
419
|
+
annualFee,
|
|
420
|
+
availableCredit,
|
|
421
|
+
currentLoanAmount,
|
|
422
|
+
originalLoanAmount,
|
|
423
|
+
originalLoanTerm,
|
|
424
|
+
monthlyRepayments,
|
|
425
|
+
currentLoanTermLeft,
|
|
426
|
+
totalRepaymentsMade,
|
|
427
|
+
totalInterestPaid,
|
|
428
|
+
}: OtherLiabilityCardProps) {
|
|
429
|
+
const isCreditCard = type === "credit_card";
|
|
430
|
+
|
|
431
|
+
return (
|
|
432
|
+
<div className="border border-border overflow-hidden flex flex-col h-full">
|
|
433
|
+
{/* Header row — fixed min-h so two cards in a grid row have aligned headers */}
|
|
434
|
+
<div className="grid grid-cols-3 gap-5 px-4 pt-5 pb-4 min-h-[7rem] items-start">
|
|
435
|
+
<FinancialDetailField label="Lender" value={lenderName} />
|
|
436
|
+
{isCreditCard ? (
|
|
437
|
+
<FinancialDetailField
|
|
438
|
+
label="Current Balance"
|
|
439
|
+
value={currentBalance}
|
|
440
|
+
/>
|
|
441
|
+
) : (
|
|
442
|
+
<FinancialDetailField
|
|
443
|
+
label="Current Loan Amount"
|
|
444
|
+
value={currentLoanAmount}
|
|
445
|
+
/>
|
|
446
|
+
)}
|
|
447
|
+
<FinancialDetailField label="Interest Rate" value={interestRate} />
|
|
448
|
+
</div>
|
|
449
|
+
|
|
450
|
+
{/* Stats section */}
|
|
451
|
+
<div className="px-4 pt-4 pb-5 flex flex-col gap-5 border-t border-border flex-1">
|
|
452
|
+
<FinancialSubsectionTitle>
|
|
453
|
+
{isCreditCard ? "Card Stats" : "Loan Stats"}
|
|
454
|
+
</FinancialSubsectionTitle>
|
|
455
|
+
{isCreditCard ? (
|
|
456
|
+
<div className="grid grid-cols-3 gap-5">
|
|
457
|
+
<FinancialDetailField label="Credit Limit" value={creditLimit} />
|
|
458
|
+
<FinancialDetailField
|
|
459
|
+
label="Min Monthly Payment"
|
|
460
|
+
value={minMonthlyPayment}
|
|
461
|
+
/>
|
|
462
|
+
<FinancialDetailField label="Annual Fee" value={annualFee} />
|
|
463
|
+
</div>
|
|
464
|
+
) : (
|
|
465
|
+
<div className="grid grid-cols-3 gap-5">
|
|
466
|
+
<FinancialDetailField
|
|
467
|
+
label="Original Loan Amount"
|
|
468
|
+
value={originalLoanAmount}
|
|
469
|
+
/>
|
|
470
|
+
<FinancialDetailField
|
|
471
|
+
label="Original Loan Term"
|
|
472
|
+
value={originalLoanTerm}
|
|
473
|
+
/>
|
|
474
|
+
<FinancialDetailField
|
|
475
|
+
label="Monthly Repayments"
|
|
476
|
+
value={monthlyRepayments}
|
|
477
|
+
/>
|
|
478
|
+
</div>
|
|
479
|
+
)}
|
|
480
|
+
</div>
|
|
481
|
+
|
|
482
|
+
{/* Footer */}
|
|
483
|
+
<FinancialSubtotalFrame>
|
|
484
|
+
{isCreditCard ? (
|
|
485
|
+
<div className="grid grid-cols-2 gap-x-4 gap-y-1">
|
|
486
|
+
<FinancialDetailField
|
|
487
|
+
variant="footer"
|
|
488
|
+
label="Total Interest Paid"
|
|
489
|
+
value={totalInterestPaid}
|
|
490
|
+
/>
|
|
491
|
+
<FinancialDetailField
|
|
492
|
+
variant="footer"
|
|
493
|
+
label="Available Credit"
|
|
494
|
+
value={availableCredit}
|
|
495
|
+
/>
|
|
496
|
+
</div>
|
|
497
|
+
) : (
|
|
498
|
+
<div className="grid grid-cols-3 gap-x-4 gap-y-1">
|
|
499
|
+
<FinancialDetailField
|
|
500
|
+
variant="footer"
|
|
501
|
+
label="Total Repayments Made"
|
|
502
|
+
value={totalRepaymentsMade}
|
|
503
|
+
/>
|
|
504
|
+
<FinancialDetailField
|
|
505
|
+
variant="footer"
|
|
506
|
+
label="Total Interest Paid"
|
|
507
|
+
value={totalInterestPaid}
|
|
508
|
+
/>
|
|
509
|
+
<FinancialDetailField
|
|
510
|
+
variant="footer"
|
|
511
|
+
label="Years Remaining"
|
|
512
|
+
value={currentLoanTermLeft}
|
|
513
|
+
/>
|
|
514
|
+
</div>
|
|
515
|
+
)}
|
|
516
|
+
</FinancialSubtotalFrame>
|
|
517
|
+
</div>
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// ---------------------------------------------------------------------------
|
|
522
|
+
// AlertCard
|
|
523
|
+
// ---------------------------------------------------------------------------
|
|
524
|
+
|
|
525
|
+
export type AlertSeverity = "NEED_ACTION" | "WATCH" | "INSIGHT";
|
|
526
|
+
export type AlertActionType = "DISMISS" | "SNOOZE";
|
|
527
|
+
|
|
528
|
+
const SEVERITY_CLASSES: Record<AlertSeverity, { dot: string; border: string }> =
|
|
529
|
+
{
|
|
530
|
+
NEED_ACTION: { dot: "bg-destructive", border: "border-destructive" },
|
|
531
|
+
WATCH: { dot: "bg-warning", border: "border-warning" },
|
|
532
|
+
INSIGHT: { dot: "bg-success", border: "border-success" },
|
|
533
|
+
};
|
|
534
|
+
|
|
535
|
+
export interface AlertCardProps {
|
|
536
|
+
id: string;
|
|
537
|
+
name: string;
|
|
538
|
+
severityCode: AlertSeverity;
|
|
539
|
+
/** Whether this alert has an active ignore period */
|
|
540
|
+
ignored?: boolean;
|
|
541
|
+
/** Human-readable date string e.g. "31/12/2025" */
|
|
542
|
+
ignoredUntil?: string;
|
|
543
|
+
/** Currently selected action, controlled externally */
|
|
544
|
+
selectedAction?: AlertActionType | null;
|
|
545
|
+
onActionChange?: (action: AlertActionType) => void;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
export function AlertCard({
|
|
549
|
+
id,
|
|
550
|
+
name,
|
|
551
|
+
severityCode,
|
|
552
|
+
ignored = false,
|
|
553
|
+
ignoredUntil,
|
|
554
|
+
selectedAction = null,
|
|
555
|
+
onActionChange,
|
|
556
|
+
}: AlertCardProps) {
|
|
557
|
+
const { dot, border } = SEVERITY_CLASSES[severityCode];
|
|
558
|
+
|
|
559
|
+
return (
|
|
560
|
+
<div className={cn("flex flex-col gap-1.5 border p-2.5 px-3", border)}>
|
|
561
|
+
{/* Alert name row */}
|
|
562
|
+
<div className="flex items-center gap-1.5">
|
|
563
|
+
<span className={cn("h-[9px] w-[9px] shrink-0 rounded-full", dot)} />
|
|
564
|
+
<span
|
|
565
|
+
className={cn(
|
|
566
|
+
"text-sm font-medium leading-tight",
|
|
567
|
+
ignored ? "text-muted-foreground line-through" : "text-foreground",
|
|
568
|
+
)}
|
|
569
|
+
>
|
|
570
|
+
{name}
|
|
571
|
+
</span>
|
|
572
|
+
</div>
|
|
573
|
+
|
|
574
|
+
{ignored ? (
|
|
575
|
+
<span className="ml-[15px] text-xs italic text-muted-foreground/60">
|
|
576
|
+
Ignored until {ignoredUntil}
|
|
577
|
+
</span>
|
|
578
|
+
) : (
|
|
579
|
+
<RadioGroup
|
|
580
|
+
value={selectedAction ?? ""}
|
|
581
|
+
onValueChange={(val) => onActionChange?.(val as AlertActionType)}
|
|
582
|
+
className="ml-[15px] flex flex-col gap-1"
|
|
583
|
+
>
|
|
584
|
+
<div className="flex items-center gap-2">
|
|
585
|
+
<RadioGroupItem value="DISMISS" id={`dismiss-${id}`} />
|
|
586
|
+
<label
|
|
587
|
+
htmlFor={`dismiss-${id}`}
|
|
588
|
+
className="cursor-pointer select-none text-sm leading-none text-muted-foreground"
|
|
589
|
+
>
|
|
590
|
+
Mark as done (will reset in 6 months)
|
|
591
|
+
</label>
|
|
592
|
+
</div>
|
|
593
|
+
<div className="flex items-center gap-2">
|
|
594
|
+
<RadioGroupItem value="SNOOZE" id={`snooze-${id}`} />
|
|
595
|
+
<label
|
|
596
|
+
htmlFor={`snooze-${id}`}
|
|
597
|
+
className="cursor-pointer select-none text-sm leading-none text-muted-foreground"
|
|
598
|
+
>
|
|
599
|
+
Snooze for 30 days
|
|
600
|
+
</label>
|
|
601
|
+
</div>
|
|
602
|
+
</RadioGroup>
|
|
603
|
+
)}
|
|
604
|
+
</div>
|
|
605
|
+
);
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
// ---------------------------------------------------------------------------
|
|
609
|
+
// AboutCard
|
|
610
|
+
// ---------------------------------------------------------------------------
|
|
611
|
+
|
|
612
|
+
export interface AboutCardProps {
|
|
613
|
+
title?: string;
|
|
614
|
+
firstName?: string;
|
|
615
|
+
lastName?: string;
|
|
616
|
+
phone?: string;
|
|
617
|
+
email?: string;
|
|
618
|
+
gender?: string;
|
|
619
|
+
maritalStatus?: string;
|
|
620
|
+
citizenStatus?: string;
|
|
621
|
+
propertyInTrust?: string;
|
|
622
|
+
companyOwnership?: string;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
/**
|
|
626
|
+
* Display card for applicant personal / contact information.
|
|
627
|
+
* Used inside the Summary tab applicant sub-tabs.
|
|
628
|
+
*/
|
|
629
|
+
export function AboutCard({
|
|
630
|
+
title,
|
|
631
|
+
firstName,
|
|
632
|
+
lastName,
|
|
633
|
+
phone,
|
|
634
|
+
email,
|
|
635
|
+
gender,
|
|
636
|
+
maritalStatus,
|
|
637
|
+
citizenStatus,
|
|
638
|
+
propertyInTrust,
|
|
639
|
+
companyOwnership,
|
|
640
|
+
}: AboutCardProps) {
|
|
641
|
+
const fullName =
|
|
642
|
+
[title, firstName, lastName].filter(Boolean).join(" ") || "—";
|
|
643
|
+
|
|
644
|
+
return (
|
|
645
|
+
<div className="border border-border overflow-hidden">
|
|
646
|
+
<div className="grid grid-cols-2 gap-x-8 gap-y-4 px-5 py-4">
|
|
647
|
+
<FinancialDetailField label="Full Name" value={fullName} />
|
|
648
|
+
<FinancialDetailField label="Gender" value={gender || "—"} />
|
|
649
|
+
<FinancialDetailField label="Phone" value={phone || "—"} />
|
|
650
|
+
<FinancialDetailField label="Email" value={email || "—"} />
|
|
651
|
+
<FinancialDetailField
|
|
652
|
+
label="Marital Status"
|
|
653
|
+
value={maritalStatus || "—"}
|
|
654
|
+
/>
|
|
655
|
+
<FinancialDetailField
|
|
656
|
+
label="Citizenship"
|
|
657
|
+
value={citizenStatus || "—"}
|
|
658
|
+
/>
|
|
659
|
+
<FinancialDetailField
|
|
660
|
+
label="Property in Trust"
|
|
661
|
+
value={propertyInTrust || "—"}
|
|
662
|
+
/>
|
|
663
|
+
<FinancialDetailField
|
|
664
|
+
label="Company Ownership"
|
|
665
|
+
value={companyOwnership || "—"}
|
|
666
|
+
/>
|
|
667
|
+
</div>
|
|
668
|
+
</div>
|
|
669
|
+
);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
// ---------------------------------------------------------------------------
|
|
673
|
+
// IncomeCard
|
|
674
|
+
// ---------------------------------------------------------------------------
|
|
675
|
+
|
|
676
|
+
export interface IncomeCardItem {
|
|
677
|
+
incomeType: string;
|
|
678
|
+
jobTitle?: string;
|
|
679
|
+
companyName?: string;
|
|
680
|
+
/** Pre-formatted amount + frequency e.g. "$9,500 / Monthly" */
|
|
681
|
+
amountLabel: string;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
export interface IncomeCardProps {
|
|
685
|
+
items: IncomeCardItem[];
|
|
686
|
+
/** Pre-formatted total monthly income e.g. "$12,300" */
|
|
687
|
+
totalMonthly?: string;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
/**
|
|
691
|
+
* Display card for applicant income items.
|
|
692
|
+
* Each income source renders as a bordered row with type, company, and amount.
|
|
693
|
+
*/
|
|
694
|
+
export function IncomeCard({ items, totalMonthly }: IncomeCardProps) {
|
|
695
|
+
if (items.length === 0) {
|
|
696
|
+
return (
|
|
697
|
+
<div className="border border-border px-5 py-4">
|
|
698
|
+
<p className="text-sm text-muted-foreground">No income recorded.</p>
|
|
699
|
+
</div>
|
|
700
|
+
);
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
return (
|
|
704
|
+
<div className="border border-border overflow-hidden flex flex-col">
|
|
705
|
+
{items.map((item, i) => (
|
|
706
|
+
<div
|
|
707
|
+
key={i}
|
|
708
|
+
className={cn(
|
|
709
|
+
"grid grid-cols-3 gap-x-6 px-5 py-[15px]",
|
|
710
|
+
i < items.length - 1 && "border-b border-border",
|
|
711
|
+
)}
|
|
712
|
+
>
|
|
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} />
|
|
719
|
+
</div>
|
|
720
|
+
))}
|
|
721
|
+
{totalMonthly && (
|
|
722
|
+
<FinancialSubtotalFrame>
|
|
723
|
+
<FinancialSubtotalBlock
|
|
724
|
+
monthlyAverage={totalMonthly}
|
|
725
|
+
label="Total Monthly Income"
|
|
726
|
+
/>
|
|
727
|
+
</FinancialSubtotalFrame>
|
|
728
|
+
)}
|
|
729
|
+
</div>
|
|
730
|
+
);
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
// ---------------------------------------------------------------------------
|
|
734
|
+
// ExpensesCard
|
|
735
|
+
// ---------------------------------------------------------------------------
|
|
736
|
+
|
|
737
|
+
const EXPENSE_ICON_MAP: Record<string, LucideIcon> = {
|
|
738
|
+
groceries: ShoppingCart,
|
|
739
|
+
"dining out": UtensilsCrossed,
|
|
740
|
+
dining: UtensilsCrossed,
|
|
741
|
+
restaurants: UtensilsCrossed,
|
|
742
|
+
transport: Car,
|
|
743
|
+
transportation: Car,
|
|
744
|
+
vehicle: Car,
|
|
745
|
+
utilities: Zap,
|
|
746
|
+
electricity: Zap,
|
|
747
|
+
insurance: Shield,
|
|
748
|
+
"council rates": Landmark,
|
|
749
|
+
council: Landmark,
|
|
750
|
+
rates: Landmark,
|
|
751
|
+
medical: HeartPulse,
|
|
752
|
+
health: HeartPulse,
|
|
753
|
+
subscriptions: RefreshCw,
|
|
754
|
+
subscription: RefreshCw,
|
|
755
|
+
"credit card": CreditCard,
|
|
756
|
+
education: GraduationCap,
|
|
757
|
+
childcare: Baby,
|
|
758
|
+
entertainment: Tv,
|
|
759
|
+
gym: Dumbbell,
|
|
760
|
+
fitness: Dumbbell,
|
|
761
|
+
clothing: Shirt,
|
|
762
|
+
rent: Building2,
|
|
763
|
+
};
|
|
764
|
+
|
|
765
|
+
function getExpenseIcon(expenseType: string): LucideIcon {
|
|
766
|
+
return EXPENSE_ICON_MAP[expenseType.toLowerCase()] ?? Receipt;
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
export interface ExpensesCardItem {
|
|
770
|
+
expenseType: string;
|
|
771
|
+
/** Pre-formatted amount + frequency e.g. "$800 / Monthly" */
|
|
772
|
+
amountLabel: string;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
export interface ExpensesCardProps {
|
|
776
|
+
items: ExpensesCardItem[];
|
|
777
|
+
/** Pre-formatted total monthly expenses e.g. "$2,100" */
|
|
778
|
+
totalMonthly?: string;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
/**
|
|
782
|
+
* Display card for applicant expense items.
|
|
783
|
+
* Each expense renders as an icon-labelled row with a destructive amount value.
|
|
784
|
+
*/
|
|
785
|
+
export function ExpensesCard({ items, totalMonthly }: ExpensesCardProps) {
|
|
786
|
+
if (items.length === 0) {
|
|
787
|
+
return (
|
|
788
|
+
<div className="border border-border px-5 py-4">
|
|
789
|
+
<p className="text-sm text-muted-foreground">No expenses recorded.</p>
|
|
790
|
+
</div>
|
|
791
|
+
);
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
return (
|
|
795
|
+
<div className="border border-border overflow-hidden flex flex-col">
|
|
796
|
+
<div className="flex flex-col">
|
|
797
|
+
{items.map((item, i) => {
|
|
798
|
+
const Icon = getExpenseIcon(item.expenseType);
|
|
799
|
+
return (
|
|
800
|
+
<div
|
|
801
|
+
key={item.expenseType}
|
|
802
|
+
className={cn(
|
|
803
|
+
"flex items-center justify-between gap-4 px-5 py-2.5",
|
|
804
|
+
i < items.length - 1 && "border-b border-border",
|
|
805
|
+
)}
|
|
806
|
+
>
|
|
807
|
+
<div className="flex items-center gap-2.5">
|
|
808
|
+
<Icon className="size-3.5 shrink-0 text-muted-foreground" />
|
|
809
|
+
<span className="text-body-small text-foreground">
|
|
810
|
+
{item.expenseType}
|
|
811
|
+
</span>
|
|
812
|
+
</div>
|
|
813
|
+
<span className="text-label-medium whitespace-nowrap text-destructive">
|
|
814
|
+
{item.amountLabel}
|
|
815
|
+
</span>
|
|
816
|
+
</div>
|
|
817
|
+
);
|
|
818
|
+
})}
|
|
819
|
+
</div>
|
|
820
|
+
{totalMonthly && (
|
|
821
|
+
<FinancialSubtotalFrame>
|
|
822
|
+
<FinancialSubtotalBlock
|
|
823
|
+
monthlyAverage={totalMonthly}
|
|
824
|
+
label="Total Monthly Expenses"
|
|
825
|
+
/>
|
|
826
|
+
</FinancialSubtotalFrame>
|
|
827
|
+
)}
|
|
828
|
+
</div>
|
|
829
|
+
);
|
|
830
|
+
}
|