"use client"; import { Fragment } from "react"; import { ChevronDown, ChevronRight } from "lucide-react"; import type { DetailFinancialRow, FinancialStatementPeriod, SurfaceFinancialRow, } from "@/lib/types"; import { cn } from "@/lib/utils"; import type { StatementInspectorSelection, StatementTreeNode, StatementTreeSection, } from "@/lib/financials/statement-view-model"; type MatrixRow = SurfaceFinancialRow | DetailFinancialRow; type StatementMatrixProps = { periods: FinancialStatementPeriod[]; sections: StatementTreeSection[]; selectedRowRef: StatementInspectorSelection | null; onToggleRow: (key: string) => void; onSelectRow: (selection: StatementInspectorSelection) => void; renderCellValue: ( row: MatrixRow, periodId: string, previousPeriodId: string | null, ) => string; periodLabelFormatter: (value: string) => string; dense?: boolean; }; function isSurfaceNode( node: StatementTreeNode, ): node is Extract { return node.kind === "surface"; } function surfaceBadges(node: Extract) { const badges: Array<{ label: string; tone: "default" | "warning" | "muted"; }> = []; if (node.row.resolutionMethod === "formula_derived") { badges.push({ label: "Formula", tone: node.row.confidence === "low" ? "warning" : "default", }); } if (node.row.resolutionMethod === "not_meaningful") { badges.push({ label: "N/M", tone: "muted" }); } if (node.row.confidence === "low") { badges.push({ label: "Low confidence", tone: "warning" }); } const detailCount = node.row.detailCount ?? node.directDetailCount; if (detailCount > 0) { badges.push({ label: `${detailCount} details`, tone: "default" }); } return badges; } function badgeClass(tone: "default" | "warning" | "muted", dense?: boolean) { const baseClasses = dense ? "rounded border px-1 py-0.5 text-[9px]" : "rounded border px-2 py-0.5 text-[10px]"; if (tone === "warning") { return cn( baseClasses, "border-[#84614f] bg-[rgba(112,76,54,0.22)] text-[#ffd7bf]", ); } if (tone === "muted") { return cn( baseClasses, "border-[color:var(--line-weak)] bg-[rgba(80,85,92,0.16)] text-[color:var(--terminal-muted)]", ); } return cn( baseClasses, "border-[color:var(--line-weak)] bg-[rgba(88,102,122,0.16)] text-[color:var(--terminal-bright)]", ); } function renderNodes( props: StatementMatrixProps & { nodes: StatementTreeNode[]; dense?: boolean }, ) { const { dense = false } = props; const buttonSize = dense ? "size-6" : "size-8"; const buttonClass = dense ? "rounded" : "rounded-lg"; const labelSize = dense ? "text-[13px]" : "text-sm"; const detailLabelSize = dense ? "text-[11px]" : "text-xs"; const paddingY = dense ? "py-1.5" : "py-2"; const gapClass = dense ? "gap-1.5" : "gap-2"; return props.nodes.map((node) => { const labelIndent = node.kind === "detail" ? node.level * 16 + 16 : node.level * 16; const canToggle = isSurfaceNode(node) && node.expandable; const nextSelection: StatementInspectorSelection = node.kind === "surface" ? { kind: "surface", key: node.row.key } : { kind: "detail", key: node.row.key, parentKey: node.parentSurfaceKey, }; const rowClass = cn( "financial-matrix-row", node.kind === "detail" && "financial-matrix-row-detail", ); const stickyCellClass = cn( "financial-matrix-sticky-cell", node.kind === "detail" && "financial-matrix-sticky-cell-detail", ); return (
{canToggle ? ( ) : ( )}
{props.periods.map((period, index) => ( {props.renderCellValue( node.row, period.id, index > 0 ? (props.periods[index - 1]?.id ?? null) : null, )} ))} {isSurfaceNode(node) && node.expanded ? ( <> Expanded children for {node.row.label} {renderNodes({ ...props, nodes: node.children, dense, })} ) : null}
); }); } export function StatementMatrix({ periods, sections, selectedRowRef, onToggleRow, onSelectRow, renderCellValue, periodLabelFormatter, dense = false, }: StatementMatrixProps) { return (
{periods.map((period) => ( ))} {periods.map((period) => ( ))} {sections.map((section) => ( {section.label ? ( ) : null} {renderNodes({ periods, sections, selectedRowRef, onToggleRow, onSelectRow, renderCellValue, periodLabelFormatter, dense, nodes: section.nodes, })} ))}
Metric
{periodLabelFormatter( period.periodEnd ?? period.filingDate, )} {period.filingType} · {period.periodLabel}
{section.label}
); }