'use client'; import { useQueryClient } from '@tanstack/react-query'; import Link from 'next/link'; import { Suspense, useCallback, useEffect, useMemo, useState } from 'react'; import { format } from 'date-fns'; import { useSearchParams } from 'next/navigation'; import { Area, AreaChart, Bar, BarChart, CartesianGrid, Line, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts'; import { ChartNoAxesCombined, ChevronDown, RefreshCcw, Search } from 'lucide-react'; import { AppShell } from '@/components/shell/app-shell'; import { MetricCard } from '@/components/dashboard/metric-card'; import { FinancialControlBar, type FinancialControlAction, type FinancialControlSection } from '@/components/financials/control-bar'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Panel } from '@/components/ui/panel'; import { useAuthGuard } from '@/hooks/use-auth-guard'; import { useLinkPrefetch } from '@/hooks/use-link-prefetch'; import { formatCurrencyByScale, formatPercent, type NumberScaleUnit } from '@/lib/format'; import { companyFinancialStatementsQueryOptions } from '@/lib/query/options'; import type { CompanyFinancialStatementsResponse, DimensionBreakdownRow, FilingFaithfulStatementRow, FinancialHistoryWindow, FinancialStatementKind, FinancialStatementMode, StandardizedStatementRow } from '@/lib/types'; type LoadOptions = { cursor?: string | null; append?: boolean; }; const FINANCIAL_VALUE_SCALE_OPTIONS: Array<{ value: NumberScaleUnit; label: string }> = [ { value: 'thousands', label: 'Thousands (K)' }, { value: 'millions', label: 'Millions (M)' }, { value: 'billions', label: 'Billions (B)' } ]; const MODE_OPTIONS: Array<{ value: FinancialStatementMode; label: string }> = [ { value: 'standardized', label: 'Standardized' }, { value: 'filing_faithful', label: 'Filing-faithful' } ]; const STATEMENT_OPTIONS: Array<{ value: FinancialStatementKind; label: string }> = [ { value: 'income', label: 'Income' }, { value: 'balance', label: 'Balance Sheet' }, { value: 'cash_flow', label: 'Cash Flow' }, { value: 'equity', label: 'Equity' }, { value: 'comprehensive_income', label: 'Comprehensive Income' } ]; const WINDOW_OPTIONS: Array<{ value: FinancialHistoryWindow; label: string }> = [ { value: '10y', label: '10 Years' }, { value: 'all', label: 'Full Available' } ]; const CHART_MUTED = '#b4ced9'; const CHART_GRID = 'rgba(126, 217, 255, 0.24)'; const CHART_TOOLTIP_BG = 'rgba(6, 17, 24, 0.95)'; const CHART_TOOLTIP_BORDER = 'rgba(123, 255, 217, 0.45)'; type OverviewPoint = { periodId: string; filingDate: string; label: string; revenue: number | null; netIncome: number | null; totalAssets: number | null; cash: number | null; debt: number | null; }; function formatLongDate(value: string) { const parsed = new Date(value); if (Number.isNaN(parsed.getTime())) { return 'Unknown'; } return format(parsed, 'MMM dd, yyyy'); } function formatShortDate(value: string) { const parsed = new Date(value); if (Number.isNaN(parsed.getTime())) { return 'Unknown'; } return format(parsed, 'MMM yyyy'); } function asDisplayCurrency(value: number | null, scale: NumberScaleUnit) { return value === null ? 'n/a' : formatCurrencyByScale(value, scale); } function asAxisCurrencyTick(value: number, scale: NumberScaleUnit) { return formatCurrencyByScale(value, scale, { maximumFractionDigits: 1 }); } function asTooltipCurrency(value: unknown, scale: NumberScaleUnit) { const numeric = Number(value); if (!Number.isFinite(numeric)) { return 'n/a'; } return formatCurrencyByScale(numeric, scale); } function ratioPercent(numerator: number | null, denominator: number | null) { if (numerator === null || denominator === null || denominator === 0) { return null; } return (numerator / denominator) * 100; } function toStandardizedRows(data: CompanyFinancialStatementsResponse | null) { if (!data || data.mode !== 'standardized') { return []; } return data.rows as StandardizedStatementRow[]; } function toFilingFaithfulRows(data: CompanyFinancialStatementsResponse | null) { if (!data || data.mode !== 'filing_faithful') { return []; } return data.rows as FilingFaithfulStatementRow[]; } function rowValue(row: { values: Record }, periodId: string) { return periodId in row.values ? row.values[periodId] : null; } function mergeFinancialPages( base: CompanyFinancialStatementsResponse | null, next: CompanyFinancialStatementsResponse ) { if (!base) { return next; } const periods = [...base.periods, ...next.periods] .filter((period, index, list) => list.findIndex((item) => item.id === period.id) === index) .sort((a, b) => Date.parse(a.filingDate) - Date.parse(b.filingDate)); const rowMap = new Map(); for (const row of [...base.rows, ...next.rows]) { const existing = rowMap.get(row.key); if (!existing) { rowMap.set(row.key, { ...row, values: { ...row.values } }); continue; } existing.hasDimensions = existing.hasDimensions || row.hasDimensions; for (const [periodId, value] of Object.entries(row.values)) { if (!(periodId in existing.values)) { existing.values[periodId] = value; } } if ('sourceConcepts' in existing && 'sourceConcepts' in row) { for (const concept of row.sourceConcepts) { if (!existing.sourceConcepts.includes(concept)) { existing.sourceConcepts.push(concept); } } } } const dimensionBreakdown = (() => { if (!base.dimensionBreakdown && !next.dimensionBreakdown) { return null; } const map = new Map(); for (const source of [base.dimensionBreakdown, next.dimensionBreakdown]) { if (!source) { continue; } for (const [key, rows] of Object.entries(source)) { const existing = map.get(key); if (existing) { existing.push(...rows); } else { map.set(key, [...rows]); } } } return Object.fromEntries(map.entries()); })(); const mergedRows = [...rowMap.values()]; return { ...next, periods, rows: next.mode === 'standardized' ? mergedRows as StandardizedStatementRow[] : mergedRows as FilingFaithfulStatementRow[], nextCursor: next.nextCursor, coverage: { filings: periods.length, rows: rowMap.size, dimensions: dimensionBreakdown ? Object.values(dimensionBreakdown).reduce((total, rows) => total + rows.length, 0) : 0 }, dataSourceStatus: { ...next.dataSourceStatus, queuedSync: base.dataSourceStatus.queuedSync || next.dataSourceStatus.queuedSync }, dimensionBreakdown }; } function findStandardizedRowValue( data: CompanyFinancialStatementsResponse | null, preferredKey: string, fallbackIncludes: string[] ) { const rows = toStandardizedRows(data); const exact = rows.find((row) => row.key === preferredKey); if (exact) { return exact; } return rows.find((row) => { const haystack = `${row.key} ${row.label} ${row.concept}`.toLowerCase(); return fallbackIncludes.some((needle) => haystack.includes(needle)); }) ?? null; } function buildOverviewSeries( incomeData: CompanyFinancialStatementsResponse | null, balanceData: CompanyFinancialStatementsResponse | null ): OverviewPoint[] { const periodMap = new Map(); for (const source of [incomeData, balanceData]) { for (const period of source?.periods ?? []) { periodMap.set(period.id, { filingDate: period.filingDate }); } } const periods = [...periodMap.entries()] .map(([periodId, data]) => ({ periodId, filingDate: data.filingDate })) .sort((a, b) => Date.parse(a.filingDate) - Date.parse(b.filingDate)); const revenueRow = findStandardizedRowValue(incomeData, 'revenue', ['revenue', 'sales']); const netIncomeRow = findStandardizedRowValue(incomeData, 'net-income', ['net income', 'profit']); const assetsRow = findStandardizedRowValue(balanceData, 'total-assets', ['total assets']); const cashRow = findStandardizedRowValue(balanceData, 'cash-and-equivalents', ['cash']); const debtRow = findStandardizedRowValue(balanceData, 'total-debt', ['debt', 'borrowings']); return periods.map((period) => ({ periodId: period.periodId, filingDate: period.filingDate, label: formatShortDate(period.filingDate), revenue: revenueRow ? rowValue(revenueRow, period.periodId) : null, netIncome: netIncomeRow ? rowValue(netIncomeRow, period.periodId) : null, totalAssets: assetsRow ? rowValue(assetsRow, period.periodId) : null, cash: cashRow ? rowValue(cashRow, period.periodId) : null, debt: debtRow ? rowValue(debtRow, period.periodId) : null })); } export default function FinancialsPage() { return ( Loading financial terminal...}> ); } function FinancialsPageContent() { const { isPending, isAuthenticated } = useAuthGuard(); const searchParams = useSearchParams(); const queryClient = useQueryClient(); const { prefetchResearchTicker } = useLinkPrefetch(); const [tickerInput, setTickerInput] = useState('MSFT'); const [ticker, setTicker] = useState('MSFT'); const [mode, setMode] = useState('standardized'); const [statement, setStatement] = useState('income'); const [window, setWindow] = useState('10y'); const [valueScale, setValueScale] = useState('millions'); const [financials, setFinancials] = useState(null); const [overviewIncome, setOverviewIncome] = useState(null); const [overviewBalance, setOverviewBalance] = useState(null); const [selectedRowKey, setSelectedRowKey] = useState(null); const [dimensionsEnabled, setDimensionsEnabled] = useState(false); const [loading, setLoading] = useState(true); const [loadingMore, setLoadingMore] = useState(false); const [error, setError] = useState(null); useEffect(() => { const fromQuery = searchParams.get('ticker'); if (!fromQuery) { return; } const normalized = fromQuery.trim().toUpperCase(); if (!normalized) { return; } setTickerInput(normalized); setTicker(normalized); }, [searchParams]); const loadOverview = useCallback(async (symbol: string, selectedWindow: FinancialHistoryWindow) => { const [incomeResponse, balanceResponse] = await Promise.all([ queryClient.ensureQueryData(companyFinancialStatementsQueryOptions({ ticker: symbol, mode: 'standardized', statement: 'income', window: selectedWindow, includeDimensions: false, limit: selectedWindow === 'all' ? 120 : 80 })), queryClient.ensureQueryData(companyFinancialStatementsQueryOptions({ ticker: symbol, mode: 'standardized', statement: 'balance', window: selectedWindow, includeDimensions: false, limit: selectedWindow === 'all' ? 120 : 80 })) ]); setOverviewIncome(incomeResponse.financials); setOverviewBalance(balanceResponse.financials); }, [queryClient]); const loadFinancials = useCallback(async (symbol: string, options?: LoadOptions) => { const normalizedTicker = symbol.trim().toUpperCase(); const nextCursor = options?.cursor ?? null; const includeDimensions = dimensionsEnabled || selectedRowKey !== null; if (!options?.append) { setLoading(true); } else { setLoadingMore(true); } setError(null); try { const response = await queryClient.ensureQueryData(companyFinancialStatementsQueryOptions({ ticker: normalizedTicker, mode, statement, window, includeDimensions, cursor: nextCursor, limit: window === 'all' ? 60 : 80 })); setFinancials((current) => { if (options?.append) { return mergeFinancialPages(current, response.financials); } return response.financials; }); await loadOverview(normalizedTicker, window); } catch (err) { setError(err instanceof Error ? err.message : 'Unable to load financial history'); if (!options?.append) { setFinancials(null); } } finally { setLoading(false); setLoadingMore(false); } }, [ queryClient, mode, statement, window, dimensionsEnabled, selectedRowKey, loadOverview ]); useEffect(() => { if (!isPending && isAuthenticated) { void loadFinancials(ticker); } }, [isPending, isAuthenticated, ticker, mode, statement, window, dimensionsEnabled, loadFinancials]); const periods = useMemo(() => { return [...(financials?.periods ?? [])] .sort((a, b) => Date.parse(a.filingDate) - Date.parse(b.filingDate)); }, [financials?.periods]); const standardizedRows = useMemo(() => toStandardizedRows(financials), [financials]); const filingFaithfulRows = useMemo(() => toFilingFaithfulRows(financials), [financials]); const statementRows = mode === 'standardized' ? standardizedRows : filingFaithfulRows; const overviewSeries = useMemo(() => { return buildOverviewSeries(overviewIncome, overviewBalance); }, [overviewIncome, overviewBalance]); const latestOverview = overviewSeries[overviewSeries.length - 1] ?? null; const selectedRow = useMemo(() => { if (!selectedRowKey) { return null; } return statementRows.find((row) => row.key === selectedRowKey) ?? null; }, [selectedRowKey, statementRows]); const dimensionRows = useMemo(() => { if (!selectedRow || !financials?.dimensionBreakdown) { return []; } const direct = financials.dimensionBreakdown[selectedRow.key] ?? []; if (direct.length > 0) { return direct; } if ('concept' in selectedRow && selectedRow.concept) { const conceptKey = selectedRow.concept.toLowerCase(); for (const rows of Object.values(financials.dimensionBreakdown)) { const matched = rows.filter((row) => (row.concept ?? '').toLowerCase() === conceptKey); if (matched.length > 0) { return matched; } } } return []; }, [selectedRow, financials?.dimensionBreakdown]); const selectedScaleLabel = useMemo(() => { return FINANCIAL_VALUE_SCALE_OPTIONS.find((option) => option.value === valueScale)?.label ?? 'Millions (M)'; }, [valueScale]); const controlSections = useMemo(() => [ { id: 'mode', label: 'Mode', value: mode, options: MODE_OPTIONS, onChange: (nextValue) => { setMode(nextValue as FinancialStatementMode); setSelectedRowKey(null); } }, { id: 'statement', label: 'Statement', value: statement, options: STATEMENT_OPTIONS, onChange: (nextValue) => { setStatement(nextValue as FinancialStatementKind); setSelectedRowKey(null); } }, { id: 'history', label: 'Window', value: window, options: WINDOW_OPTIONS, onChange: (nextValue) => { setWindow(nextValue as FinancialHistoryWindow); setSelectedRowKey(null); } }, { id: 'scale', label: 'Scale', value: valueScale, options: FINANCIAL_VALUE_SCALE_OPTIONS, onChange: (nextValue) => setValueScale(nextValue as NumberScaleUnit) } ], [mode, statement, window, valueScale]); const controlActions = useMemo(() => { const actions: FinancialControlAction[] = []; if (window === '10y') { actions.push({ id: 'load-full-history', label: 'Load Full History', variant: 'secondary', onClick: () => { setWindow('all'); setSelectedRowKey(null); } }); } if (window === 'all' && financials?.nextCursor) { actions.push({ id: 'load-older-periods', label: loadingMore ? 'Loading Older...' : 'Load Older Periods', variant: 'secondary', disabled: loadingMore, onClick: () => { if (!financials.nextCursor) { return; } void loadFinancials(ticker, { cursor: financials.nextCursor, append: true }); } }); } return actions; }, [window, financials?.nextCursor, loadingMore, loadFinancials, ticker]); if (isPending || !isAuthenticated) { return
Loading financial terminal...
; } return ( { void loadFinancials(ticker); }} > Refresh )} >
{ event.preventDefault(); const normalized = tickerInput.trim().toUpperCase(); if (!normalized) { return; } setTicker(normalized); }} > setTickerInput(event.target.value.toUpperCase())} placeholder="Ticker (AAPL)" className="max-w-xs" /> {financials ? ( prefetchResearchTicker(financials.company.ticker)} onFocus={() => prefetchResearchTicker(financials.company.ticker)} className="text-sm text-[color:var(--accent)] hover:text-[color:var(--accent-strong)]" > Open analysis ) : null}
{error ? (

{error}

) : null}
= 0} />
{loading ? (

Loading overview chart...

) : overviewSeries.length === 0 ? (

No standardized income history available yet.

) : (
asAxisCurrencyTick(value, valueScale)} /> asTooltipCurrency(value, valueScale)} contentStyle={{ backgroundColor: CHART_TOOLTIP_BG, border: `1px solid ${CHART_TOOLTIP_BORDER}`, borderRadius: '0.75rem' }} />
)}
{loading ? (

Loading balance chart...

) : overviewSeries.length === 0 ? (

No standardized balance history available yet.

) : (
asAxisCurrencyTick(value, valueScale)} /> asTooltipCurrency(value, valueScale)} contentStyle={{ backgroundColor: CHART_TOOLTIP_BG, border: `1px solid ${CHART_TOOLTIP_BORDER}`, borderRadius: '0.75rem' }} />
)}
{loading ? (

Loading statement matrix...

) : periods.length === 0 || statementRows.length === 0 ? (

No statement rows available for the selected filters yet.

) : (
{periods.map((period) => ( ))} {statementRows.map((row) => ( { setSelectedRowKey(row.key); if (row.hasDimensions && !dimensionsEnabled) { setDimensionsEnabled(true); } }} > {periods.map((period) => ( ))} ))}
Metric
{formatLongDate(period.filingDate)} {period.filingType} ยท {period.periodLabel}
{'depth' in row ? (
{row.label} {row.hasDimensions ? : null}
) : (
{row.label} {row.hasDimensions ? : null}
)}
{asDisplayCurrency(rowValue(row, period.id), valueScale)}
)}
{!selectedRow ? (

Select a statement row to inspect dimensional facts.

) : !selectedRow.hasDimensions ? (

No dimensional data is available for {selectedRow.label}.

) : !dimensionsEnabled ? (

Enable dimensions by selecting the row again.

) : dimensionRows.length === 0 ? (

Dimensions are still loading or unavailable for this row.

) : (
{dimensionRows.map((row, index) => { const period = periods.find((item) => item.id === row.periodId); return ( ); })}
Period Axis Member Value
{period ? formatLongDate(period.filingDate) : row.periodId} {row.axis} {row.member} {asDisplayCurrency(row.value, valueScale)}
)}
{financials ? (

Hydrated

{financials.dataSourceStatus.hydratedFilings}

Partial

{financials.dataSourceStatus.partialFilings}

Failed

{financials.dataSourceStatus.failedFilings}

Pending

{financials.dataSourceStatus.pendingFilings}

Background Sync

{financials.dataSourceStatus.queuedSync ? 'Queued' : 'Idle'}

) : null}
Financial Statements V2: standardized + filing-faithful history
); }