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 (
);
}
// ---------------------------------------------------------------------------
// 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}
)}
);
}