@wealthx/shadcn 1.5.42 → 1.5.43
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 +215 -210
- package/CHANGELOG.md +6 -0
- package/dist/{chunk-5FHBC6DY.mjs → chunk-33WZ5NCW.mjs} +1 -1
- package/dist/{chunk-C35JMOII.mjs → chunk-3G6JUYRE.mjs} +4 -4
- package/dist/{chunk-LBXIYS34.mjs → chunk-4PVCJ3JD.mjs} +1 -1
- package/dist/{chunk-EHQL64B7.mjs → chunk-4SUXTO2Z.mjs} +4 -4
- package/dist/{chunk-BAONSY54.mjs → chunk-5RYH7SOQ.mjs} +1 -1
- package/dist/{chunk-3C4DZTGA.mjs → chunk-5XD7A7YC.mjs} +1 -1
- package/dist/{chunk-5DAQU3B6.mjs → chunk-66JXT7NY.mjs} +1 -1
- package/dist/{chunk-NGKTJRFN.mjs → chunk-6DO4EGT2.mjs} +2 -2
- package/dist/{chunk-C7ZTZTEW.mjs → chunk-6XJWL2E5.mjs} +1 -1
- package/dist/{chunk-FQUT5XD6.mjs → chunk-A4UP4QFB.mjs} +1 -1
- package/dist/{chunk-USIRKDYQ.mjs → chunk-BFCX7ADE.mjs} +1 -1
- package/dist/{chunk-XGRSPFFC.mjs → chunk-CQHKU24Z.mjs} +1 -1
- package/dist/{chunk-HONTZFLO.mjs → chunk-DP4ER6TJ.mjs} +1 -1
- package/dist/{chunk-VLVEZHFE.mjs → chunk-EFSLAMHI.mjs} +4 -4
- package/dist/{chunk-FYZBGWYR.mjs → chunk-FVSOFXJQ.mjs} +1 -1
- package/dist/{chunk-JUMEIPII.mjs → chunk-G2MOZZPE.mjs} +8 -8
- package/dist/{chunk-D3HKFRQO.mjs → chunk-GQWKBESP.mjs} +8 -5
- package/dist/{chunk-MD66TGX7.mjs → chunk-GXDKWCMV.mjs} +1 -1
- package/dist/{chunk-77L3UPBW.mjs → chunk-H7NOUDU3.mjs} +5 -5
- package/dist/{chunk-4LLTZ45R.mjs → chunk-HOXTEU5K.mjs} +8 -7
- package/dist/{chunk-ZA37ZWZW.mjs → chunk-IXW77PMI.mjs} +7 -7
- package/dist/{chunk-XHZONBL4.mjs → chunk-JLEQU5BO.mjs} +1 -1
- package/dist/{chunk-6UKOJLXO.mjs → chunk-JSFWRD7K.mjs} +4 -4
- package/dist/{chunk-7PTRHNUV.mjs → chunk-JY3FUGNL.mjs} +1 -1
- package/dist/{chunk-3ZU5BH6X.mjs → chunk-KEOAPKJO.mjs} +3 -3
- package/dist/{chunk-4QTHK7ML.mjs → chunk-KWYFJQV6.mjs} +1 -1
- package/dist/{chunk-FGMDBJCF.mjs → chunk-LDPCSE7J.mjs} +4 -4
- package/dist/chunk-LFWNKXZU.mjs +109 -0
- package/dist/{chunk-IRZWYTGV.mjs → chunk-M32YSAWL.mjs} +8 -7
- package/dist/{chunk-LLAGF6BA.mjs → chunk-MUB2G36A.mjs} +1 -1
- package/dist/{chunk-DQNNP6I4.mjs → chunk-NIETQFJQ.mjs} +1 -1
- package/dist/{chunk-RUX3OLVZ.mjs → chunk-OTFG57ZF.mjs} +1 -1
- package/dist/{chunk-OKIWXOJL.mjs → chunk-OWTW5WAJ.mjs} +1 -1
- package/dist/{chunk-WWIWRNBK.mjs → chunk-P7NSCTAW.mjs} +1 -1
- package/dist/{chunk-BZWQU52U.mjs → chunk-QZREZL2F.mjs} +1 -1
- package/dist/{chunk-E432NK23.mjs → chunk-RBQ4BZUV.mjs} +6 -6
- package/dist/{chunk-I2EKKSEF.mjs → chunk-RKBLVNDC.mjs} +4 -7
- package/dist/{chunk-LHQACMZY.mjs → chunk-SPPQFW32.mjs} +106 -50
- package/dist/{chunk-OSSS56CB.mjs → chunk-SUXJWKRI.mjs} +4 -4
- package/dist/{chunk-SCGCGVDN.mjs → chunk-SZXIPE5J.mjs} +1 -1
- package/dist/{chunk-VVURVETY.mjs → chunk-TOQRA2TD.mjs} +1 -1
- package/dist/{chunk-GYWOD2YI.mjs → chunk-TZSDYQFH.mjs} +4 -4
- package/dist/{chunk-S7SBLNX4.mjs → chunk-UB3WG6I4.mjs} +1 -1
- package/dist/{chunk-PGJRZHN7.mjs → chunk-UVZ3JWFG.mjs} +1 -1
- package/dist/{chunk-UD5UF5OC.mjs → chunk-W7OPFKTZ.mjs} +4 -4
- package/dist/{chunk-YEWNFK5S.mjs → chunk-WLXP4OOF.mjs} +5 -5
- package/dist/{chunk-ORMC3TV3.mjs → chunk-XYXYTTNW.mjs} +1 -1
- package/dist/{chunk-CZOGJC76.mjs → chunk-YACFZWRR.mjs} +7 -7
- package/dist/{chunk-UTCW5YUX.mjs → chunk-YPATB6YQ.mjs} +9 -9
- package/dist/{chunk-BZGFW6L7.mjs → chunk-YWJAIPUA.mjs} +1 -1
- package/dist/{chunk-MHBQJVHE.mjs → chunk-Z65BGSHI.mjs} +5 -5
- package/dist/{chunk-PCULNQWA.mjs → chunk-ZGSFRUVI.mjs} +3 -3
- package/dist/{chunk-7NQKFPXE.mjs → chunk-ZRYG6ICN.mjs} +1 -1
- package/dist/{chunk-ZFKAYRFQ.mjs → chunk-ZUHFYW65.mjs} +1 -1
- package/dist/components/ui/about-you-form.mjs +2 -2
- package/dist/components/ui/account-list-carousel.mjs +2 -2
- package/dist/components/ui/add-column-modal.mjs +4 -4
- package/dist/components/ui/add-lead-modal.mjs +4 -4
- package/dist/components/ui/advisor-card.mjs +2 -2
- package/dist/components/ui/ai-assistant-drawer.mjs +2 -2
- package/dist/components/ui/ai-builder/index.mjs +4 -4
- package/dist/components/ui/ai-conversations/index.mjs +4 -4
- package/dist/components/ui/alert-dialog.mjs +3 -3
- package/dist/components/ui/applicant-expenses-section.mjs +1 -1
- package/dist/components/ui/appointment-action-dialogs.mjs +5 -5
- package/dist/components/ui/appointment-availability-settings.mjs +4 -4
- package/dist/components/ui/appointment-book-dialog.mjs +4 -4
- package/dist/components/ui/appointment-detail-sheet.mjs +6 -6
- package/dist/components/ui/appointment-upcoming-card.mjs +4 -4
- package/dist/components/ui/asset-accordion.mjs +7 -7
- package/dist/components/ui/assets-liabilities-side-card.js +19 -66
- package/dist/components/ui/assets-liabilities-side-card.mjs +22 -69
- package/dist/components/ui/backoffice-alert-history-chart.js +1 -1
- package/dist/components/ui/backoffice-alert-history-chart.mjs +5 -5
- package/dist/components/ui/backoffice-alert-matching-chart.js +1 -1
- package/dist/components/ui/backoffice-alert-matching-chart.mjs +5 -5
- package/dist/components/ui/backoffice-alerts-chart.js +1 -1
- package/dist/components/ui/backoffice-alerts-chart.mjs +5 -5
- package/dist/components/ui/backoffice-connections-chart.js +1 -1
- package/dist/components/ui/backoffice-connections-chart.mjs +5 -5
- package/dist/components/ui/backoffice-contact-history-chart.js +1 -1
- package/dist/components/ui/backoffice-contact-history-chart.mjs +5 -5
- package/dist/components/ui/backoffice-contact-matching-chart.js +1 -1
- package/dist/components/ui/backoffice-contact-matching-chart.mjs +5 -5
- package/dist/components/ui/backoffice-signup-steps.mjs +4 -4
- package/dist/components/ui/bank-statement-generate-dialog.mjs +4 -4
- package/dist/components/ui/bank-statement-pdf-viewer.mjs +4 -4
- package/dist/components/ui/borrowing-capacity-atoms.js +3 -6
- package/dist/components/ui/borrowing-capacity-atoms.mjs +2 -2
- package/dist/components/ui/borrowing-capacity-card.js +5 -5
- package/dist/components/ui/borrowing-capacity-card.mjs +6 -6
- package/dist/components/ui/borrowing-capacity-line-chart.js +5 -5
- package/dist/components/ui/borrowing-capacity-line-chart.mjs +5 -5
- package/dist/components/ui/calculator-section.mjs +4 -4
- package/dist/components/ui/cash-balance-line-chart.js +102 -46
- package/dist/components/ui/cash-balance-line-chart.mjs +5 -5
- package/dist/components/ui/cashflow-bar-chart.js +7 -4
- package/dist/components/ui/cashflow-bar-chart.mjs +5 -5
- package/dist/components/ui/category-edit-dialog.mjs +4 -4
- package/dist/components/ui/color-picker.mjs +2 -2
- package/dist/components/ui/contact-alert-dialog/index.mjs +4 -4
- package/dist/components/ui/create-contact-modal.mjs +4 -4
- package/dist/components/ui/csv-import-modal.mjs +4 -4
- package/dist/components/ui/dashboard-expense-categories.js +96 -63
- package/dist/components/ui/dashboard-expense-categories.mjs +101 -66
- package/dist/components/ui/dashboard-transactions-table.js +37 -44
- package/dist/components/ui/dashboard-transactions-table.mjs +45 -52
- package/dist/components/ui/data-table.mjs +2 -2
- package/dist/components/ui/date-picker.mjs +2 -2
- package/dist/components/ui/debt-accordion.mjs +7 -7
- package/dist/components/ui/delete-contact-component.mjs +4 -4
- package/dist/components/ui/dialog.mjs +3 -3
- package/dist/components/ui/document-checklist-template.mjs +2 -2
- package/dist/components/ui/expense-bar-chart.js +8 -7
- package/dist/components/ui/expense-bar-chart.mjs +5 -5
- package/dist/components/ui/expense-categories-bar.js +261 -0
- package/dist/components/ui/expense-categories-bar.mjs +12 -0
- package/dist/components/ui/expense-work-details.js +8 -7
- package/dist/components/ui/expense-work-details.mjs +7 -7
- package/dist/components/ui/file-preview-dialog.mjs +4 -4
- package/dist/components/ui/financial-cards.mjs +2 -2
- package/dist/components/ui/financial-drawers.mjs +2 -2
- package/dist/components/ui/financial-sections.mjs +3 -3
- package/dist/components/ui/frontend-signup-steps.mjs +2 -2
- package/dist/components/ui/income-bar-chart.js +8 -7
- package/dist/components/ui/income-bar-chart.mjs +5 -5
- package/dist/components/ui/income-sources-card.mjs +1 -1
- package/dist/components/ui/income-summary-component.mjs +1 -1
- package/dist/components/ui/income-work-details.js +8 -7
- package/dist/components/ui/income-work-details.mjs +6 -6
- package/dist/components/ui/incoming-outgoings-card.js +2 -2
- package/dist/components/ui/incoming-outgoings-card.mjs +3 -3
- package/dist/components/ui/interest-rate-section.mjs +1 -1
- package/dist/components/ui/kanban-column.mjs +5 -5
- package/dist/components/ui/loan-application-cards.mjs +3 -3
- package/dist/components/ui/loan-financials.mjs +3 -3
- package/dist/components/ui/money-input-with-slider.mjs +2 -2
- package/dist/components/ui/opportunity-card.mjs +4 -4
- package/dist/components/ui/opportunity-edit-modals.mjs +4 -4
- package/dist/components/ui/opportunity-summary-tab.mjs +8 -8
- package/dist/components/ui/pagination.mjs +2 -2
- package/dist/components/ui/pipeline-board.mjs +6 -6
- package/dist/components/ui/pipeline-chart.mjs +2 -2
- package/dist/components/ui/pipeline-dialogs.mjs +4 -4
- package/dist/components/ui/policy-ai/index.mjs +2 -2
- package/dist/components/ui/property-asset-card.mjs +4 -4
- package/dist/components/ui/property-cashflow-doughnut-chart.js +3 -3
- package/dist/components/ui/property-cashflow-doughnut-chart.mjs +5 -5
- package/dist/components/ui/property-debt-equity-doughnut-chart.js +3 -3
- package/dist/components/ui/property-debt-equity-doughnut-chart.mjs +5 -5
- package/dist/components/ui/property-list-carousel.mjs +2 -2
- package/dist/components/ui/property-mobile-estimate-line-chart.js +4 -4
- package/dist/components/ui/property-mobile-estimate-line-chart.mjs +5 -5
- package/dist/components/ui/property-report-dialog.mjs +5 -5
- package/dist/components/ui/resource-center/index.mjs +4 -4
- package/dist/components/ui/review-alerts-dialog.mjs +4 -4
- package/dist/components/ui/savings-goal-modal.mjs +7 -7
- package/dist/components/ui/scenario-drawer.mjs +4 -4
- package/dist/components/ui/scenario-list.js +4 -7
- package/dist/components/ui/scenario-list.mjs +5 -5
- package/dist/components/ui/share-details-dialog.mjs +4 -4
- package/dist/components/ui/sidebar-nav.mjs +4 -4
- package/dist/components/ui/signup-form-primitives.mjs +2 -2
- package/dist/components/ui/stage-timeline.mjs +1 -1
- package/dist/components/ui/support-agent/index.mjs +2 -2
- package/dist/components/ui/top-three-product.mjs +1 -1
- package/dist/components/ui/transactions-expense-categories-doughnut-chart.js +3 -3
- package/dist/components/ui/transactions-expense-categories-doughnut-chart.mjs +5 -5
- package/dist/components/ui/transactions-income-expense-bar-chart.mjs +5 -5
- package/dist/components/ui/transactions-liabilities-breakdown-doughnut-chart.js +4 -4
- package/dist/components/ui/transactions-liabilities-breakdown-doughnut-chart.mjs +5 -5
- package/dist/components/ui/transactions-summary-block.js +13 -0
- package/dist/components/ui/transactions-summary-block.mjs +13 -0
- package/dist/index.js +2092 -1935
- package/dist/index.mjs +138 -134
- package/dist/lib/format-currency.js +54 -0
- package/dist/lib/format-currency.mjs +9 -0
- package/dist/styles.css +1 -1
- package/package.json +6 -1
- package/src/component-descriptions/assets-liabilities-side-card.md +19 -0
- package/src/component-descriptions/pipeline-chart.md +17 -0
- package/src/components/index.tsx +6 -0
- package/src/components/ui/assets-liabilities-side-card.tsx +43 -83
- package/src/components/ui/borrowing-capacity-atoms.tsx +4 -7
- package/src/components/ui/borrowing-capacity-line-chart.tsx +4 -4
- package/src/components/ui/cash-balance-line-chart.tsx +123 -42
- package/src/components/ui/cashflow-bar-chart.tsx +7 -4
- package/src/components/ui/chart-shared.tsx +4 -4
- package/src/components/ui/dashboard-expense-categories.tsx +136 -60
- package/src/components/ui/dashboard-transactions-table.tsx +42 -28
- package/src/components/ui/expense-bar-chart.tsx +32 -19
- package/src/components/ui/expense-categories-bar.tsx +178 -0
- package/src/components/ui/income-bar-chart.tsx +32 -19
- package/src/components/ui/incoming-outgoings-card.tsx +2 -2
- package/src/components/ui/property-mobile-estimate-line-chart.tsx +4 -4
- package/src/components/ui/scenario-list.tsx +2 -2
- package/src/components/ui/transactions-liabilities-breakdown-doughnut-chart.tsx +7 -5
- package/src/components/ui/transactions-summary-block.tsx +39 -6
- package/src/styles/styles-css.ts +1 -1
- package/tsup.config.ts +2 -0
- package/dist/{chunk-CEYEK3TI.mjs → chunk-B4R62ID3.mjs} +3 -3
- package/dist/{chunk-7LN5OGC2.mjs → chunk-E3VAK4EB.mjs} +3 -3
- package/dist/{chunk-EY36WDCF.mjs → chunk-EEZFXE3P.mjs} +3 -3
- package/dist/{chunk-T5FRVEJQ.mjs → chunk-JTMN36BK.mjs} +3 -3
- /package/dist/{chunk-MN5NYQCL.mjs → chunk-XQDTFNVL.mjs} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wealthx/shadcn",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.43",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"module": "./dist/index.mjs",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -364,6 +364,11 @@
|
|
|
364
364
|
"import": "./dist/components/ui/expense-bar-chart.mjs",
|
|
365
365
|
"require": "./dist/components/ui/expense-bar-chart.js"
|
|
366
366
|
},
|
|
367
|
+
"./expense-categories-bar": {
|
|
368
|
+
"types": "./src/components/ui/expense-categories-bar.tsx",
|
|
369
|
+
"import": "./dist/components/ui/expense-categories-bar.mjs",
|
|
370
|
+
"require": "./dist/components/ui/expense-categories-bar.js"
|
|
371
|
+
},
|
|
367
372
|
"./income-bar-chart": {
|
|
368
373
|
"types": "./src/components/ui/income-bar-chart.tsx",
|
|
369
374
|
"import": "./dist/components/ui/income-bar-chart.mjs",
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# AssetsLiabilitiesSideCard
|
|
2
|
+
|
|
3
|
+
## 2026-06-05 — Refactor to 2×2 grid layout matching frontend
|
|
4
|
+
|
|
5
|
+
**Prompted by:** frontend parity review
|
|
6
|
+
|
|
7
|
+
### What changed
|
|
8
|
+
- Replaced vertical list layout (Net Position hero → Total Assets with sub-rows → Total Liabilities) with a 2×2 grid of metric cells
|
|
9
|
+
- Four cells: Total Liabilities, Total Cash Assets, Net Position, Total Property Value — matching the order in the live frontend
|
|
10
|
+
- Each cell now uses `bg-muted p-4` as background (replacing inline card border)
|
|
11
|
+
- Negative values (Total Liabilities) colored with `text-brand-secondary`; positive values with `text-primary`
|
|
12
|
+
- Removed `MetricRow` and `SubRow` helper components (no longer needed)
|
|
13
|
+
- Removed derived `totalAssets` field (Total Assets row was removed from design)
|
|
14
|
+
|
|
15
|
+
### Why
|
|
16
|
+
- Frontend app uses a compact 2×2 grid; Storybook was showing a different list-based layout, causing design drift
|
|
17
|
+
|
|
18
|
+
### Affected tokens / files
|
|
19
|
+
- `packages/shadcn/src/components/ui/assets-liabilities-side-card.tsx`
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# PipelineChart
|
|
2
|
+
|
|
3
|
+
## 2026-05-19 — Single-color segments with gap separators
|
|
4
|
+
|
|
5
|
+
**Prompted by:** Thinh Tu Duc
|
|
6
|
+
|
|
7
|
+
### What changed
|
|
8
|
+
- All segments now share a single primary color instead of 5 cycling opacity shades
|
|
9
|
+
- 1px background-color gaps between segments provide visual separation
|
|
10
|
+
- Legend dots follow the same color logic (primary fallback, or explicit `stage.color`)
|
|
11
|
+
|
|
12
|
+
### Why
|
|
13
|
+
- The previous 5-opacity fallback palette repeated colors when there were more than 5 stages, creating misleading visual groupings
|
|
14
|
+
- A single primary color is tenant-adaptive and works for any number of stages
|
|
15
|
+
|
|
16
|
+
### Affected tokens / files
|
|
17
|
+
- `packages/shadcn/src/components/ui/pipeline-chart.tsx` — replaced `FALLBACK_COLORS` with `SEGMENT_COLOR = "var(--primary)"`, added `gap-px bg-background` to bar container
|
package/src/components/index.tsx
CHANGED
|
@@ -712,6 +712,12 @@ export type {
|
|
|
712
712
|
ExpenseGranularity,
|
|
713
713
|
} from "./ui/expense-bar-chart";
|
|
714
714
|
|
|
715
|
+
export { ExpenseCategoriesBar } from "./ui/expense-categories-bar";
|
|
716
|
+
export type {
|
|
717
|
+
ExpenseCategoriesBarProps,
|
|
718
|
+
ExpenseCategoryPart,
|
|
719
|
+
} from "./ui/expense-categories-bar";
|
|
720
|
+
|
|
715
721
|
export {
|
|
716
722
|
Field,
|
|
717
723
|
FieldLabel,
|
|
@@ -27,36 +27,13 @@ export interface AssetsLiabilitiesSideCardProps {
|
|
|
27
27
|
className?: string;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
// ───
|
|
30
|
+
// ─── Internal types ───────────────────────────────────────────────────────────
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
label,
|
|
34
|
-
value,
|
|
35
|
-
valueClass,
|
|
36
|
-
}: {
|
|
32
|
+
interface MetricCell {
|
|
37
33
|
label: string;
|
|
38
|
-
value:
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
return (
|
|
42
|
-
<div className="flex items-center justify-between gap-2">
|
|
43
|
-
<span className="text-sm font-medium text-foreground">{label}</span>
|
|
44
|
-
<span className={cn("text-base font-semibold shrink-0", valueClass)}>
|
|
45
|
-
{value}
|
|
46
|
-
</span>
|
|
47
|
-
</div>
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// ─── Sub-row ─────────────────────────────────────────────────────────────────
|
|
52
|
-
|
|
53
|
-
function SubRow({ label, value }: { label: string; value: string }) {
|
|
54
|
-
return (
|
|
55
|
-
<div className="flex items-center justify-between gap-2 pl-3 text-sm">
|
|
56
|
-
<span className="text-muted-foreground">{label}</span>
|
|
57
|
-
<span className="font-medium text-foreground">{value}</span>
|
|
58
|
-
</div>
|
|
59
|
-
);
|
|
34
|
+
value: number;
|
|
35
|
+
/** When true, value is prefixed with "-" and coloured with brand-secondary */
|
|
36
|
+
negative?: boolean;
|
|
60
37
|
}
|
|
61
38
|
|
|
62
39
|
// ─── Component ────────────────────────────────────────────────────────────────
|
|
@@ -70,12 +47,16 @@ export function AssetsLiabilitiesSideCard({
|
|
|
70
47
|
tooltipText = "Current snapshot of your financial position. These values are not affected by the date range filter.",
|
|
71
48
|
className,
|
|
72
49
|
}: AssetsLiabilitiesSideCardProps) {
|
|
73
|
-
const
|
|
74
|
-
|
|
50
|
+
const cells: MetricCell[] = [
|
|
51
|
+
{ label: "Total Liabilities", value: liabilities, negative: true },
|
|
52
|
+
{ label: "Total Cash Assets", value: assets },
|
|
53
|
+
{ label: "Net Position", value: netPosition },
|
|
54
|
+
{ label: "Total Property Value", value: propertyValue },
|
|
55
|
+
];
|
|
75
56
|
|
|
76
57
|
return (
|
|
77
58
|
<Card className={cn("flex flex-col", className)}>
|
|
78
|
-
<CardHeader className="pb-
|
|
59
|
+
<CardHeader className="pb-3">
|
|
79
60
|
<div className="flex items-center gap-1">
|
|
80
61
|
<CardTitle className="text-xs font-semibold uppercase tracking-wider text-muted-foreground">
|
|
81
62
|
{title}
|
|
@@ -97,60 +78,39 @@ export function AssetsLiabilitiesSideCard({
|
|
|
97
78
|
</div>
|
|
98
79
|
</CardHeader>
|
|
99
80
|
|
|
100
|
-
<CardContent className="
|
|
101
|
-
{/*
|
|
102
|
-
<div className="
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
<TooltipProvider>
|
|
107
|
-
<Tooltip>
|
|
108
|
-
<TooltipTrigger asChild>
|
|
109
|
-
<span
|
|
110
|
-
className={cn(
|
|
111
|
-
"cursor-default text-xl font-bold leading-tight shrink-0",
|
|
112
|
-
netPositive ? "text-foreground" : "text-destructive",
|
|
113
|
-
)}
|
|
114
|
-
>
|
|
115
|
-
{netPositive ? "" : "-"}
|
|
116
|
-
{formatCurrencyAbbrev(Math.abs(netPosition))}
|
|
117
|
-
</span>
|
|
118
|
-
</TooltipTrigger>
|
|
119
|
-
<TooltipContent>
|
|
120
|
-
{netPositive ? "" : "-"}$
|
|
121
|
-
{Math.abs(netPosition).toLocaleString()}
|
|
122
|
-
</TooltipContent>
|
|
123
|
-
</Tooltip>
|
|
124
|
-
</TooltipProvider>
|
|
125
|
-
</div>
|
|
126
|
-
|
|
127
|
-
{/* Divider */}
|
|
128
|
-
<div className="border-t border-border" />
|
|
81
|
+
<CardContent className="pb-5">
|
|
82
|
+
{/* 2×2 grid — matches frontend layout */}
|
|
83
|
+
<div className="grid grid-cols-2 gap-1.5">
|
|
84
|
+
{cells.map((cell) => {
|
|
85
|
+
const displayValue = `${cell.negative ? "-" : ""}${formatCurrencyAbbrev(Math.abs(cell.value))}`;
|
|
86
|
+
const fullValue = `${cell.negative ? "-" : ""}$${Math.abs(cell.value).toLocaleString()}`;
|
|
129
87
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
88
|
+
return (
|
|
89
|
+
<div key={cell.label} className="bg-muted p-4">
|
|
90
|
+
<p className="text-xs text-muted-foreground font-medium mb-1.5">
|
|
91
|
+
{cell.label}
|
|
92
|
+
</p>
|
|
93
|
+
<TooltipProvider>
|
|
94
|
+
<Tooltip>
|
|
95
|
+
<TooltipTrigger asChild>
|
|
96
|
+
<span
|
|
97
|
+
className={cn(
|
|
98
|
+
"text-lg font-bold cursor-default",
|
|
99
|
+
cell.negative
|
|
100
|
+
? "text-brand-secondary"
|
|
101
|
+
: "text-primary",
|
|
102
|
+
)}
|
|
103
|
+
>
|
|
104
|
+
{displayValue}
|
|
105
|
+
</span>
|
|
106
|
+
</TooltipTrigger>
|
|
107
|
+
<TooltipContent>{fullValue}</TooltipContent>
|
|
108
|
+
</Tooltip>
|
|
109
|
+
</TooltipProvider>
|
|
110
|
+
</div>
|
|
111
|
+
);
|
|
112
|
+
})}
|
|
144
113
|
</div>
|
|
145
|
-
|
|
146
|
-
<div className="border-t border-border" />
|
|
147
|
-
|
|
148
|
-
{/* Total Liabilities */}
|
|
149
|
-
<MetricRow
|
|
150
|
-
label="Total Liabilities"
|
|
151
|
-
value={`-${formatCurrencyAbbrev(liabilities)}`}
|
|
152
|
-
valueClass="text-xl text-destructive"
|
|
153
|
-
/>
|
|
154
114
|
</CardContent>
|
|
155
115
|
</Card>
|
|
156
116
|
);
|
|
@@ -196,7 +196,7 @@ export function LoanToValueRatio({
|
|
|
196
196
|
*/
|
|
197
197
|
|
|
198
198
|
export interface AddScenarioButtonProps {
|
|
199
|
-
/** Button label (default: "
|
|
199
|
+
/** Button label (default: "Add New Scenario") */
|
|
200
200
|
title?: string;
|
|
201
201
|
/** Disables the button */
|
|
202
202
|
disabled?: boolean;
|
|
@@ -206,20 +206,17 @@ export interface AddScenarioButtonProps {
|
|
|
206
206
|
}
|
|
207
207
|
|
|
208
208
|
export function AddScenarioButton({
|
|
209
|
-
title = "
|
|
209
|
+
title = "Add New Scenario",
|
|
210
210
|
disabled = false,
|
|
211
211
|
onClick,
|
|
212
212
|
className,
|
|
213
213
|
}: AddScenarioButtonProps) {
|
|
214
214
|
return (
|
|
215
215
|
<Button
|
|
216
|
-
variant="outline
|
|
216
|
+
variant="outline"
|
|
217
217
|
disabled={disabled}
|
|
218
218
|
onClick={onClick}
|
|
219
|
-
className={cn(
|
|
220
|
-
"w-full justify-between border-dashed text-base",
|
|
221
|
-
className,
|
|
222
|
-
)}
|
|
219
|
+
className={cn("w-full justify-between", className)}
|
|
223
220
|
>
|
|
224
221
|
<span>{title}</span>
|
|
225
222
|
<Plus size={18} className="shrink-0" />
|
|
@@ -259,7 +259,7 @@ export function BorrowingCapacityLineChart({
|
|
|
259
259
|
maxRotation: 0,
|
|
260
260
|
minRotation: 0,
|
|
261
261
|
color: FALLBACK_TICK,
|
|
262
|
-
font: { size:
|
|
262
|
+
font: { size: 12, family: fontFamily },
|
|
263
263
|
callback: function (_, index) {
|
|
264
264
|
const iso = labels[index];
|
|
265
265
|
return iso ? formatDateLabel(iso) : "";
|
|
@@ -275,7 +275,7 @@ export function BorrowingCapacityLineChart({
|
|
|
275
275
|
maxTicksLimit: 6,
|
|
276
276
|
padding: 8,
|
|
277
277
|
color: FALLBACK_TICK,
|
|
278
|
-
font: { size:
|
|
278
|
+
font: { size: 12, family: fontFamily },
|
|
279
279
|
callback: (v) => formatAbbrev(Number(v)),
|
|
280
280
|
},
|
|
281
281
|
},
|
|
@@ -314,11 +314,11 @@ export function BorrowingCapacityLineChart({
|
|
|
314
314
|
>
|
|
315
315
|
<CardHeader className="px-3 sm:px-6">
|
|
316
316
|
<div className="flex items-start justify-between gap-4">
|
|
317
|
-
<CardTitle className="text-xs font-semibold uppercase tracking-
|
|
317
|
+
<CardTitle className="text-xs font-semibold uppercase tracking-wider text-muted-foreground">
|
|
318
318
|
{title}
|
|
319
319
|
</CardTitle>
|
|
320
320
|
{kpiValue != null && (
|
|
321
|
-
<span className="shrink-0 text-
|
|
321
|
+
<span className="shrink-0 text-lg font-bold tabular-nums">
|
|
322
322
|
{formatCurrency(kpiValue / 100)}
|
|
323
323
|
</span>
|
|
324
324
|
)}
|
|
@@ -60,6 +60,17 @@ export interface CashBalanceDataPoint {
|
|
|
60
60
|
y: number;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
/**
|
|
64
|
+
* Optional secondary line datasets rendered on a right-hand Y-axis.
|
|
65
|
+
* Useful for overlaying income / expense lines against the balance.
|
|
66
|
+
*/
|
|
67
|
+
export interface ExtraLineDataset {
|
|
68
|
+
label: string;
|
|
69
|
+
data: CashBalanceDataPoint[];
|
|
70
|
+
color: string;
|
|
71
|
+
dashed?: boolean;
|
|
72
|
+
}
|
|
73
|
+
|
|
63
74
|
export interface CashBalanceLineChartProps {
|
|
64
75
|
chartData?: CashBalanceDataPoint[] | null;
|
|
65
76
|
title?: string;
|
|
@@ -80,6 +91,11 @@ export interface CashBalanceLineChartProps {
|
|
|
80
91
|
showLegend?: boolean;
|
|
81
92
|
/** Legend placement relative to the chart */
|
|
82
93
|
legendPosition?: "top" | "bottom";
|
|
94
|
+
/**
|
|
95
|
+
* Optional secondary line datasets (e.g. income / expense) rendered on a
|
|
96
|
+
* right-hand Y-axis so their scale stays independent from the balance line.
|
|
97
|
+
*/
|
|
98
|
+
extraDatasets?: ExtraLineDataset[];
|
|
83
99
|
}
|
|
84
100
|
|
|
85
101
|
// ---------------------------------------------------------------------------
|
|
@@ -98,6 +114,7 @@ export function CashBalanceLineChart({
|
|
|
98
114
|
showBalanceValue = false,
|
|
99
115
|
showLegend = false,
|
|
100
116
|
legendPosition = "bottom",
|
|
117
|
+
extraDatasets,
|
|
101
118
|
}: CashBalanceLineChartProps) {
|
|
102
119
|
const themeVars = useThemeVars();
|
|
103
120
|
const brandPrimary =
|
|
@@ -118,29 +135,54 @@ export function CashBalanceLineChart({
|
|
|
118
135
|
return indices;
|
|
119
136
|
}, [hasData, chartData]);
|
|
120
137
|
|
|
138
|
+
const hasExtra = Array.isArray(extraDatasets) && extraDatasets.length > 0;
|
|
139
|
+
|
|
121
140
|
const data = useMemo<ChartData<"line">>(() => {
|
|
122
141
|
if (!hasData) return { labels: [], datasets: [] };
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
142
|
+
|
|
143
|
+
const mainDs = {
|
|
144
|
+
label: title,
|
|
145
|
+
data: chartData!.map((p) => p.y),
|
|
146
|
+
fill: false,
|
|
147
|
+
borderColor: brandPrimary,
|
|
148
|
+
backgroundColor: "transparent",
|
|
149
|
+
borderWidth: 2.5,
|
|
150
|
+
borderDash: [],
|
|
151
|
+
tension: 0.4,
|
|
152
|
+
pointRadius: 0,
|
|
153
|
+
pointHoverRadius: 6,
|
|
154
|
+
pointHoverBackgroundColor: FALLBACK_BG,
|
|
155
|
+
pointHoverBorderColor: brandPrimary,
|
|
156
|
+
pointHoverBorderWidth: 3,
|
|
157
|
+
pointHitRadius: 10,
|
|
158
|
+
yAxisID: "y",
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const extraDs = hasExtra
|
|
162
|
+
? extraDatasets!.map((ds) => ({
|
|
163
|
+
label: ds.label,
|
|
164
|
+
data: ds.data.map((p) => p.y),
|
|
129
165
|
fill: false,
|
|
130
|
-
borderColor:
|
|
166
|
+
borderColor: ds.color,
|
|
131
167
|
backgroundColor: "transparent",
|
|
132
|
-
borderWidth:
|
|
168
|
+
borderWidth: 1.5,
|
|
169
|
+
borderDash: ds.dashed ? ([5, 4] as number[]) : ([] as number[]),
|
|
133
170
|
tension: 0.4,
|
|
134
171
|
pointRadius: 0,
|
|
135
|
-
pointHoverRadius:
|
|
172
|
+
pointHoverRadius: 4,
|
|
136
173
|
pointHoverBackgroundColor: FALLBACK_BG,
|
|
137
|
-
pointHoverBorderColor:
|
|
138
|
-
pointHoverBorderWidth:
|
|
174
|
+
pointHoverBorderColor: ds.color,
|
|
175
|
+
pointHoverBorderWidth: 2,
|
|
139
176
|
pointHitRadius: 10,
|
|
140
|
-
|
|
141
|
-
|
|
177
|
+
yAxisID: "y2",
|
|
178
|
+
}))
|
|
179
|
+
: [];
|
|
180
|
+
|
|
181
|
+
return {
|
|
182
|
+
labels: chartData!.map((p) => p.x),
|
|
183
|
+
datasets: [mainDs, ...extraDs],
|
|
142
184
|
};
|
|
143
|
-
}, [hasData, chartData, brandPrimary, title]);
|
|
185
|
+
}, [hasData, chartData, brandPrimary, title, hasExtra, extraDatasets]);
|
|
144
186
|
|
|
145
187
|
const options = useMemo<ChartOptions<"line">>(
|
|
146
188
|
() => ({
|
|
@@ -153,18 +195,19 @@ export function CashBalanceLineChart({
|
|
|
153
195
|
enabled: true,
|
|
154
196
|
mode: "index",
|
|
155
197
|
intersect: false,
|
|
156
|
-
displayColors:
|
|
198
|
+
displayColors: hasExtra,
|
|
157
199
|
padding: 12,
|
|
158
200
|
cornerRadius: 0,
|
|
159
201
|
titleFont: { size: 11, weight: "600", family: fontFamily },
|
|
160
|
-
bodyFont: { size:
|
|
202
|
+
bodyFont: { size: 12, weight: "500", family: fontFamily },
|
|
161
203
|
callbacks: {
|
|
162
204
|
title: (items) => {
|
|
163
205
|
const iso = items[0]?.label;
|
|
164
206
|
return iso ? formatDateTooltip(iso) : "";
|
|
165
207
|
},
|
|
166
208
|
label: (ctx) => {
|
|
167
|
-
|
|
209
|
+
const value = formatCurrency(ctx.parsed.y, { decimals: 2 });
|
|
210
|
+
return hasExtra ? ` ${ctx.dataset.label}: ${value}` : value;
|
|
168
211
|
},
|
|
169
212
|
},
|
|
170
213
|
},
|
|
@@ -181,7 +224,7 @@ export function CashBalanceLineChart({
|
|
|
181
224
|
maxRotation: 0,
|
|
182
225
|
minRotation: 0,
|
|
183
226
|
color: FALLBACK_TICK,
|
|
184
|
-
font: { size:
|
|
227
|
+
font: { size: 12, family: fontFamily },
|
|
185
228
|
callback: function (_, index) {
|
|
186
229
|
if (!tickIndices.has(index) || !chartData) return "";
|
|
187
230
|
return formatDateLabel(chartData[index].x);
|
|
@@ -198,13 +241,27 @@ export function CashBalanceLineChart({
|
|
|
198
241
|
maxTicksLimit: 5,
|
|
199
242
|
padding: 8,
|
|
200
243
|
color: FALLBACK_TICK,
|
|
201
|
-
font: { size:
|
|
244
|
+
font: { size: 12, family: fontFamily },
|
|
245
|
+
callback: (v) => formatAbbrev(Number(v)),
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
y2: {
|
|
249
|
+
display: hasExtra && showYAxis,
|
|
250
|
+
position: "right" as const,
|
|
251
|
+
beginAtZero: true,
|
|
252
|
+
grid: { display: false },
|
|
253
|
+
border: { display: false },
|
|
254
|
+
ticks: {
|
|
255
|
+
maxTicksLimit: 5,
|
|
256
|
+
padding: 8,
|
|
257
|
+
color: FALLBACK_TICK,
|
|
258
|
+
font: { size: 12, family: fontFamily },
|
|
202
259
|
callback: (v) => formatAbbrev(Number(v)),
|
|
203
260
|
},
|
|
204
261
|
},
|
|
205
262
|
},
|
|
206
263
|
}),
|
|
207
|
-
[tickIndices, chartData, showXAxis, showYAxis, fontFamily],
|
|
264
|
+
[tickIndices, chartData, showXAxis, showYAxis, fontFamily, hasExtra],
|
|
208
265
|
);
|
|
209
266
|
|
|
210
267
|
const latestValue = hasData ? chartData![chartData!.length - 1].y : null;
|
|
@@ -214,18 +271,22 @@ export function CashBalanceLineChart({
|
|
|
214
271
|
className={cn("w-full py-4 sm:py-6 gap-2", className)}
|
|
215
272
|
style={{ maxWidth: width, fontFamily }}
|
|
216
273
|
>
|
|
217
|
-
|
|
218
|
-
<
|
|
219
|
-
<
|
|
220
|
-
{title
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
274
|
+
{(title || (showBalanceValue && latestValue !== null)) && (
|
|
275
|
+
<CardHeader className="px-3 sm:px-6">
|
|
276
|
+
<div className="flex flex-col gap-0.5">
|
|
277
|
+
{title && (
|
|
278
|
+
<CardTitle className="text-xs font-semibold uppercase tracking-wider text-muted-foreground">
|
|
279
|
+
{title}
|
|
280
|
+
</CardTitle>
|
|
281
|
+
)}
|
|
282
|
+
{showBalanceValue && latestValue !== null && (
|
|
283
|
+
<p className="text-lg font-bold tabular-nums leading-tight">
|
|
284
|
+
{formatCurrency(latestValue, { decimals: 2 })}
|
|
285
|
+
</p>
|
|
286
|
+
)}
|
|
287
|
+
</div>
|
|
288
|
+
</CardHeader>
|
|
289
|
+
)}
|
|
229
290
|
|
|
230
291
|
<CardContent className="px-3 sm:px-6">
|
|
231
292
|
{isLoading ? (
|
|
@@ -243,11 +304,21 @@ export function CashBalanceLineChart({
|
|
|
243
304
|
<div className="flex flex-col gap-2">
|
|
244
305
|
{showLegend && legendPosition === "top" && (
|
|
245
306
|
<div className="flex flex-wrap items-center gap-x-4 gap-y-1.5">
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
307
|
+
{title && (
|
|
308
|
+
<ChartLegendItem
|
|
309
|
+
label={title}
|
|
310
|
+
color={brandPrimary}
|
|
311
|
+
lineStyle="solid"
|
|
312
|
+
/>
|
|
313
|
+
)}
|
|
314
|
+
{extraDatasets?.map((ds) => (
|
|
315
|
+
<ChartLegendItem
|
|
316
|
+
key={ds.label}
|
|
317
|
+
label={ds.label}
|
|
318
|
+
color={ds.color}
|
|
319
|
+
lineStyle={ds.dashed ? "dashed" : "solid"}
|
|
320
|
+
/>
|
|
321
|
+
))}
|
|
251
322
|
</div>
|
|
252
323
|
)}
|
|
253
324
|
<div style={{ height, width: "100%", position: "relative" }}>
|
|
@@ -261,11 +332,21 @@ export function CashBalanceLineChart({
|
|
|
261
332
|
</div>
|
|
262
333
|
{showLegend && legendPosition === "bottom" && (
|
|
263
334
|
<div className="flex flex-wrap items-center gap-x-4 gap-y-1.5">
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
335
|
+
{title && (
|
|
336
|
+
<ChartLegendItem
|
|
337
|
+
label={title}
|
|
338
|
+
color={brandPrimary}
|
|
339
|
+
lineStyle="solid"
|
|
340
|
+
/>
|
|
341
|
+
)}
|
|
342
|
+
{extraDatasets?.map((ds) => (
|
|
343
|
+
<ChartLegendItem
|
|
344
|
+
key={ds.label}
|
|
345
|
+
label={ds.label}
|
|
346
|
+
color={ds.color}
|
|
347
|
+
lineStyle={ds.dashed ? "dashed" : "solid"}
|
|
348
|
+
/>
|
|
349
|
+
))}
|
|
269
350
|
</div>
|
|
270
351
|
)}
|
|
271
352
|
</div>
|
|
@@ -295,14 +295,17 @@ export function CashflowBarChart({
|
|
|
295
295
|
display: showXAxis,
|
|
296
296
|
grid: { display: false },
|
|
297
297
|
border: { display: false },
|
|
298
|
-
ticks: {
|
|
298
|
+
ticks: {
|
|
299
|
+
font: { size: 12, family: fontFamily },
|
|
300
|
+
color: FALLBACK_TICK,
|
|
301
|
+
},
|
|
299
302
|
},
|
|
300
303
|
y: {
|
|
301
304
|
display: showYAxis,
|
|
302
305
|
grid: { display: false },
|
|
303
306
|
border: { display: false },
|
|
304
307
|
ticks: {
|
|
305
|
-
font: { size:
|
|
308
|
+
font: { size: 12, family: fontFamily },
|
|
306
309
|
color: FALLBACK_TICK,
|
|
307
310
|
maxTicksLimit: 5,
|
|
308
311
|
padding: 8,
|
|
@@ -311,7 +314,7 @@ export function CashflowBarChart({
|
|
|
311
314
|
},
|
|
312
315
|
},
|
|
313
316
|
}),
|
|
314
|
-
[showXAxis, showYAxis, sliced],
|
|
317
|
+
[showXAxis, showYAxis, sliced, fontFamily],
|
|
315
318
|
);
|
|
316
319
|
|
|
317
320
|
return (
|
|
@@ -320,7 +323,7 @@ export function CashflowBarChart({
|
|
|
320
323
|
style={{ maxWidth: width, fontFamily }}
|
|
321
324
|
>
|
|
322
325
|
<CardHeader className="px-3 sm:px-6">
|
|
323
|
-
<CardTitle className="text-xs font-semibold uppercase tracking-
|
|
326
|
+
<CardTitle className="text-xs font-semibold uppercase tracking-wider text-muted-foreground">
|
|
324
327
|
{title}
|
|
325
328
|
</CardTitle>
|
|
326
329
|
{showPeriodSelector && (
|
|
@@ -190,7 +190,7 @@ export function ChartLegendItem({
|
|
|
190
190
|
}}
|
|
191
191
|
/>
|
|
192
192
|
)}
|
|
193
|
-
<span className="text-
|
|
193
|
+
<span className="text-sm text-muted-foreground leading-none">
|
|
194
194
|
{label}
|
|
195
195
|
</span>
|
|
196
196
|
</div>
|
|
@@ -224,15 +224,15 @@ export function DoughnutLegendRow({
|
|
|
224
224
|
flexShrink: 0,
|
|
225
225
|
}}
|
|
226
226
|
/>
|
|
227
|
-
<span className="text-
|
|
227
|
+
<span className="text-sm text-muted-foreground leading-none truncate">
|
|
228
228
|
{label}
|
|
229
229
|
</span>
|
|
230
230
|
</div>
|
|
231
231
|
<div className="flex items-center gap-2 shrink-0">
|
|
232
|
-
<span className="text-
|
|
232
|
+
<span className="text-sm font-medium leading-none">
|
|
233
233
|
{formatAbbrev(value)}
|
|
234
234
|
</span>
|
|
235
|
-
<span className="text-
|
|
235
|
+
<span className="text-sm text-muted-foreground leading-none w-12 text-right">
|
|
236
236
|
{percent}
|
|
237
237
|
</span>
|
|
238
238
|
</div>
|