import * as React from "react"; import { cn } from "@/lib/utils"; /** * Financial display primitives — WealthX DS * * Atomic building blocks for financial data display. * Used inside summary report drawers, opportunity detail panels, * and any financial data card in the backoffice and frontend. * * Compose these upward: * Atom → FinancialDetailField, FinancialLineItem, FinancialLvrBar … * Card → PropertyCard, DebtCard, ApplicantInfoCard … * Section → PropertyHoldingsGrid, IncomeExpensesGrid … * Drawer → SummaryReportDrawer, OpportunityDetailsDrawer … */ const NO_DATA = "—"; // --------------------------------------------------------------------------- // FinancialSectionLabel // --------------------------------------------------------------------------- export interface FinancialSectionLabelProps { children: React.ReactNode; } /** All-caps muted section heading. Used above major sections inside a financial drawer. */ export function FinancialSectionLabel({ children, }: FinancialSectionLabelProps) { return ( {children} ); } // --------------------------------------------------------------------------- // FinancialCardHeader // --------------------------------------------------------------------------- export interface FinancialCardHeaderProps { children: React.ReactNode; } /** All-caps muted label at the top of a card — lighter than SectionLabel. */ export function FinancialCardHeader({ children }: FinancialCardHeaderProps) { return (
{children}
); } // --------------------------------------------------------------------------- // FinancialSubsectionTitle // --------------------------------------------------------------------------- export interface FinancialSubsectionTitleProps { children: React.ReactNode; } /** Uppercase bold title inside a card section (e.g. "Card Stats", "Loan Stats"). */ export function FinancialSubsectionTitle({ children, }: FinancialSubsectionTitleProps) { return ( {children} ); } // --------------------------------------------------------------------------- // FinancialDetailField // --------------------------------------------------------------------------- export type FinancialDetailFieldVariant = "caption" | "overline" | "footer"; export interface FinancialDetailFieldProps { label: string; /** * The value to display. Accepts any renderable node — formatted strings, * badges, or `null`/`undefined` (renders `—`). */ value?: React.ReactNode; /** * Controls label typography and layout density: * - `caption` (default) — small 2-line min-height label; use in dense grids * - `overline` — tighter gap; use in mid-density sections * - `footer` — flex-col layout; use inside `FinancialSubtotalFrame` */ variant?: FinancialDetailFieldVariant; } /** * Label + value vertical stack — the core data-display cell. * * | Variant | Label style | Use when | * |-----------|------------------------|-----------------------------------| * | caption | small, 2-line min-h | Dense grids (default) | * | overline | overline, tighter gap | Mid-density sections | * | footer | overline + flex-col | SubtotalFrame cells | */ export function FinancialDetailField({ label, value, variant = "caption", }: FinancialDetailFieldProps) { if (variant === "footer") { return (
{label} {value ?? NO_DATA}
); } const labelClass = variant === "caption" ? "block min-h-[2rem] text-caption text-muted-foreground" : "min-h-[1.75rem] text-overline text-muted-foreground"; return (
{label} {value ?? NO_DATA}
); } // --------------------------------------------------------------------------- // FinancialLineItem // --------------------------------------------------------------------------- export interface FinancialLineItemProps { label: string; /** Formatted value string e.g. "$9,500". Renders `—` when falsy. */ value?: string | null; /** * When `true`, renders the value in `text-destructive` (red). * Use for expenses, liabilities, or negative cashflow values. */ destructive?: boolean; } /** * Horizontal row: label on left, value on right. * Use `destructive` for expenses, liabilities, or negative cashflow values. */ export function FinancialLineItem({ label, value, destructive, }: FinancialLineItemProps) { return (
{label} {value || NO_DATA}
); } // --------------------------------------------------------------------------- // FinancialLvrBar // --------------------------------------------------------------------------- export interface FinancialLvrBarProps { /** * LVR percentage (0–100). Drives colour threshold: * - `< 70` → success (low risk) * - `70–79` → warning (borderline) * - `≥ 80` → destructive (high risk) */ percent?: number; /** Human-readable label rendered on the right e.g. `"56% — Good"`. */ label?: string; } /** * LVR progress bar with label. * * Colour thresholds map to semantic tokens: * - < 70% → success (low risk) * - 70–79% → warning (borderline) * - ≥ 80% → destructive (high risk) */ export function FinancialLvrBar({ percent = 56, label = "56% — Good", }: FinancialLvrBarProps) { const color = percent >= 80 ? "destructive" : percent >= 70 ? "warning" : "success"; const textClass = { success: "text-success", warning: "text-warning", destructive: "text-destructive", }[color]; const trackClass = { success: "bg-success/15", warning: "bg-warning/15", destructive: "bg-destructive/15", }[color]; const fillClass = { success: "bg-success", warning: "bg-warning", destructive: "bg-destructive", }[color]; return (
LVR {label}
); } // --------------------------------------------------------------------------- // FinancialSubtotalFrame // --------------------------------------------------------------------------- export interface FinancialSubtotalFrameProps { children: React.ReactNode; } /** * Brand-tinted footer container — sits at the bottom of a property or debt card. * Background uses the tenant primary color at 10 % opacity. */ export function FinancialSubtotalFrame({ children, }: FinancialSubtotalFrameProps) { return (
{children}
); } // --------------------------------------------------------------------------- // FinancialSubtotalBlock // --------------------------------------------------------------------------- export interface FinancialSubtotalBlockProps { /** * Monthly average value e.g. `"$2,840"`. * When provided without `totalLast12Months`, renders right-aligned as a single figure. */ monthlyAverage?: string; /** Total for the last 12 months e.g. `"$34,080"`. Only shown when provided alongside `monthlyAverage`. */ totalLast12Months?: string; /** Override the "Monthly Average" label (e.g. `"Net Surplus"`). */ label?: string; } /** * One or two summary values inside a SubtotalFrame. * * - Two values → Monthly Average (left) + Total Last 12 Months (right) * - One value → right-aligned single figure (pass only `monthlyAverage`) * - Custom label → override "Monthly Average" via `label` prop */ export function FinancialSubtotalBlock({ monthlyAverage, totalLast12Months, label, }: FinancialSubtotalBlockProps) { const isSingle = monthlyAverage && !totalLast12Months; return (
{label ?? "Monthly Average"} {monthlyAverage ?? NO_DATA}
{!isSingle && (
Total Last 12 Months {totalLast12Months ?? NO_DATA}
)}
); }