From b04f744e931c518fe342aa5e43530925bbead4ab Mon Sep 17 00:00:00 2001 From: Tyler Hoang Date: Mon, 18 May 2026 00:19:54 -0700 Subject: feat: add FinancialsCard component with statement tabs and period toggle Renders income/balance/cash_flow statements with annual/quarterly toggle, section headers, indent levels, total rows, margin rows, and negative-value coloring via --negative. Co-Authored-By: Claude Sonnet 4.6 --- frontend/components/prism/FinancialsCard.tsx | 136 +++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 frontend/components/prism/FinancialsCard.tsx (limited to 'frontend/components/prism') diff --git a/frontend/components/prism/FinancialsCard.tsx b/frontend/components/prism/FinancialsCard.tsx new file mode 100644 index 0000000..94a6618 --- /dev/null +++ b/frontend/components/prism/FinancialsCard.tsx @@ -0,0 +1,136 @@ +"use client"; +import type { FinancialRow, FinancialsResponse } from "@/types/api"; +import { fmtLarge } from "@/lib/format"; + +type StatementKey = "income" | "balance" | "cash_flow"; +type PeriodKey = "annual" | "quarterly"; + +type Props = { + data: FinancialsResponse; + statement: StatementKey; + period: PeriodKey; + onChangeStatement: (s: StatementKey) => void; + onChangePeriod: (p: PeriodKey) => void; +}; + +const STMT_LABELS: Record = { + income: "INCOME", + balance: "BALANCE", + cash_flow: "CASH FLOW", +}; + +function fmtFinVal(val: number | null | undefined, isMargin: boolean): string { + if (val === null || val === undefined) return "—"; + if (isMargin) return `${(val * 100).toFixed(1)}%`; + return fmtLarge(val); +} + +function FinRow({ row, lastColIdx }: { row: FinancialRow; lastColIdx: number }) { + if (row.is_section) { + return ( + + + {row.label} + + + ); + } + + const cls = [ + "psm-fin-row", + row.is_total ? "is-total" : "", + row.is_margin ? "is-margin" : "", + row.indent === 1 ? "is-indent" : "", + ] + .filter(Boolean) + .join(" "); + + return ( + + {row.label} + {row.values.map((val, i) => ( + + {fmtFinVal(val, row.is_margin)} + + ))} + + ); +} + +export function FinancialsCard({ + data, + statement, + period, + onChangeStatement, + onChangePeriod, +}: Props) { + const stmt = data[statement]; + const lastColIdx = stmt.columns.length - 1; + + return ( +
+
+
+ {(["income", "balance", "cash_flow"] as StatementKey[]).map((key) => ( + + ))} +
+
+ {(["annual", "quarterly"] as PeriodKey[]).map((p) => ( + + ))} +
+
+ + {stmt.columns.length === 0 ? ( +

Statement data unavailable.

+ ) : ( +
+ + + + + {stmt.columns.map((col, i) => ( + + ))} + + + + {stmt.rows.map((row, i) => ( + + ))} + +
USD (millions) + {col} +
+
+ )} +
+ ); +} -- cgit v1.3-2-g0d8e