'use client'; import { useQueryClient } from '@tanstack/react-query'; import Link from 'next/link'; import { Fragment, Suspense, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { format } from 'date-fns'; import { useSearchParams } from 'next/navigation'; import { Bar, CartesianGrid, Line, LineChart, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts'; import { AlertTriangle, ChevronDown, Download, RefreshCcw, Search } from 'lucide-react'; import { AppShell } from '@/components/shell/app-shell'; import { FinancialControlBar, type FinancialControlAction, type FinancialControlOption, 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 { queueFilingSync } from '@/lib/api'; import { formatCurrencyByScale, formatPercent, type NumberScaleUnit } from '@/lib/format'; import { buildGraphingHref } from '@/lib/graphing/catalog'; import { queryKeys } from '@/lib/query/keys'; import { companyFinancialStatementsQueryOptions } from '@/lib/query/options'; import type { CompanyFinancialStatementsResponse, DerivedFinancialRow, FinancialCadence, FinancialDisplayMode, FinancialSurfaceKind, FinancialUnit, RatioRow, StandardizedFinancialRow, StructuredKpiRow, TaxonomyStatementRow, TrendSeries } from '@/lib/types'; type LoadOptions = { cursor?: string | null; append?: boolean; }; type DisplayRow = TaxonomyStatementRow | StandardizedFinancialRow | RatioRow | StructuredKpiRow; 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 SURFACE_OPTIONS: FinancialControlOption[] = [ { value: 'income_statement', label: 'Income Statement' }, { value: 'balance_sheet', label: 'Balance Sheet' }, { value: 'cash_flow_statement', label: 'Cash Flow Statement' }, { value: 'ratios', label: 'Ratios' }, { value: 'segments_kpis', label: 'Segments & KPIs' }, { value: 'adjusted', label: 'Adjusted' }, { value: 'custom_metrics', label: 'Custom Metrics' } ]; const CADENCE_OPTIONS: FinancialControlOption[] = [ { value: 'annual', label: 'Annual' }, { value: 'quarterly', label: 'Quarterly' }, { value: 'ltm', label: 'LTM' } ]; const DISPLAY_MODE_OPTIONS: FinancialControlOption[] = [ { value: 'standardized', label: 'Standardized' }, { value: 'faithful', label: 'Filing-faithful' } ]; 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)'; function formatLongDate(value: string) { const parsed = new Date(value); if (Number.isNaN(parsed.getTime())) { return 'Unknown'; } return format(parsed, 'MMM dd, yyyy'); } function isTaxonomyRow(row: DisplayRow): row is TaxonomyStatementRow { return 'localName' in row; } function isDerivedRow(row: DisplayRow): row is DerivedFinancialRow { return 'formulaKey' in row; } function isKpiRow(row: DisplayRow): row is StructuredKpiRow { return 'provenanceType' in row; } function rowValue(row: DisplayRow, periodId: string) { return row.values[periodId] ?? null; } function formatMetricValue(value: number | null, unit: FinancialUnit, scale: NumberScaleUnit) { if (value === null) { return 'n/a'; } switch (unit) { case 'currency': return formatCurrencyByScale(value, scale); case 'percent': return formatPercent(value * 100); case 'shares': case 'count': return new Intl.NumberFormat('en-US', { notation: 'compact', maximumFractionDigits: 2 }).format(value); case 'ratio': return `${value.toFixed(2)}x`; default: return String(value); } } function chartTickFormatter(value: number, unit: FinancialUnit, scale: NumberScaleUnit) { if (!Number.isFinite(value)) { return 'n/a'; } return formatMetricValue(value, unit, scale); } function buildDisplayValue(input: { row: DisplayRow; periodId: string; previousPeriodId: string | null; commonSizeRow: DisplayRow | null; displayMode: FinancialDisplayMode; showPercentChange: boolean; showCommonSize: boolean; scale: NumberScaleUnit; surfaceKind: FinancialSurfaceKind; }) { const current = rowValue(input.row, input.periodId); if (input.showPercentChange && input.previousPeriodId) { const previous = rowValue(input.row, input.previousPeriodId); if (current === null || previous === null || previous === 0) { return 'n/a'; } return formatPercent(((current - previous) / previous) * 100); } if (input.showCommonSize) { if (input.surfaceKind === 'ratios' && isDerivedRow(input.row) && input.row.unit === 'percent') { return formatPercent((current ?? 0) * 100); } if (input.displayMode === 'faithful') { return 'n/a'; } const denominator = input.commonSizeRow ? rowValue(input.commonSizeRow, input.periodId) : null; if (current === null || denominator === null || denominator === 0) { return 'n/a'; } return formatPercent((current / denominator) * 100); } const unit = isTaxonomyRow(input.row) ? 'currency' : input.row.unit; return formatMetricValue(current, unit, input.scale); } function groupRows(rows: DisplayRow[], categories: CompanyFinancialStatementsResponse['categories']) { if (categories.length === 0) { return [{ label: null, rows }]; } return categories .map((category) => ({ label: category.label, rows: rows.filter((row) => !isTaxonomyRow(row) && row.category === category.key) })) .filter((group) => group.rows.length > 0); } 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((left, right) => Date.parse(left.periodEnd ?? left.filingDate) - Date.parse(right.periodEnd ?? right.filingDate)); const mergeRows = }>(rows: T[]) => { const map = new Map(); for (const row of rows) { const existing = map.get(row.key); if (!existing) { map.set(row.key, structuredClone(row)); continue; } existing.values = { ...existing.values, ...row.values }; } return [...map.values()]; }; return { ...next, periods, statementRows: next.statementRows && base.statementRows ? { faithful: mergeRows([...base.statementRows.faithful, ...next.statementRows.faithful]), standardized: mergeRows([...base.statementRows.standardized, ...next.statementRows.standardized]) } : next.statementRows, ratioRows: next.ratioRows && base.ratioRows ? mergeRows([...base.ratioRows, ...next.ratioRows]) : next.ratioRows, kpiRows: next.kpiRows && base.kpiRows ? mergeRows([...base.kpiRows, ...next.kpiRows]) : next.kpiRows, trendSeries: next.trendSeries, categories: next.categories, dimensionBreakdown: next.dimensionBreakdown ?? base.dimensionBreakdown }; } function ChartFrame({ children }: { children: React.ReactNode }) { const containerRef = useRef(null); const [ready, setReady] = useState(false); useEffect(() => { const element = containerRef.current; if (!element) { return; } const observer = new ResizeObserver((entries) => { const next = entries[0]; if (!next) { return; } const { width, height } = next.contentRect; setReady(width > 0 && height > 0); }); observer.observe(element); return () => observer.disconnect(); }, []); return
{ready ? children : null}
; } 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 [surfaceKind, setSurfaceKind] = useState('income_statement'); const [cadence, setCadence] = useState('annual'); const [displayMode, setDisplayMode] = useState('standardized'); const [valueScale, setValueScale] = useState('millions'); const [rowSearch, setRowSearch] = useState(''); const [showPercentChange, setShowPercentChange] = useState(false); const [showCommonSize, setShowCommonSize] = useState(false); const [financials, setFinancials] = useState(null); const [selectedRowKey, setSelectedRowKey] = useState(null); const [loading, setLoading] = useState(true); const [loadingMore, setLoadingMore] = useState(false); const [syncingFinancials, setSyncingFinancials] = 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]); useEffect(() => { const statementSurface = surfaceKind === 'income_statement' || surfaceKind === 'balance_sheet' || surfaceKind === 'cash_flow_statement'; if (!statementSurface && displayMode !== 'standardized') { setDisplayMode('standardized'); } if (displayMode === 'faithful') { setShowPercentChange(false); setShowCommonSize(false); } if (surfaceKind === 'adjusted' || surfaceKind === 'custom_metrics') { setShowPercentChange(false); setShowCommonSize(false); } }, [displayMode, surfaceKind]); const loadFinancials = useCallback(async (symbol: string, options?: LoadOptions) => { const normalizedTicker = symbol.trim().toUpperCase(); const nextCursor = options?.cursor ?? null; const includeDimensions = selectedRowKey !== null && (surfaceKind === 'segments_kpis' || surfaceKind === 'income_statement' || surfaceKind === 'balance_sheet' || surfaceKind === 'cash_flow_statement'); if (!options?.append) { setLoading(true); } else { setLoadingMore(true); } setError(null); try { const response = await queryClient.ensureQueryData(companyFinancialStatementsQueryOptions({ ticker: normalizedTicker, surfaceKind, cadence, includeDimensions, includeFacts: false, cursor: nextCursor, limit: 12 })); setFinancials((current) => options?.append ? mergeFinancialPages(current, response.financials) : response.financials); } catch (err) { setError(err instanceof Error ? err.message : 'Unable to load financial history'); if (!options?.append) { setFinancials(null); } } finally { setLoading(false); setLoadingMore(false); } }, [cadence, queryClient, selectedRowKey, surfaceKind]); const syncFinancials = useCallback(async () => { const targetTicker = (financials?.company.ticker ?? ticker).trim().toUpperCase(); if (!targetTicker) { return; } setSyncingFinancials(true); setError(null); try { await queueFilingSync({ ticker: targetTicker, limit: 20 }); void queryClient.invalidateQueries({ queryKey: queryKeys.recentTasks(20) }); void queryClient.invalidateQueries({ queryKey: ['filings'] }); void queryClient.invalidateQueries({ queryKey: ['financials-v3'] }); await loadFinancials(targetTicker); } catch (err) { setError(err instanceof Error ? err.message : `Failed to queue financial sync for ${targetTicker}`); } finally { setSyncingFinancials(false); } }, [financials?.company.ticker, loadFinancials, queryClient, ticker]); useEffect(() => { if (!isPending && isAuthenticated) { void loadFinancials(ticker); } }, [isPending, isAuthenticated, loadFinancials, ticker]); const periods = useMemo(() => { return [...(financials?.periods ?? [])] .sort((left, right) => Date.parse(left.periodEnd ?? left.filingDate) - Date.parse(right.periodEnd ?? right.filingDate)); }, [financials?.periods]); const activeRows = useMemo(() => { if (!financials) { return []; } switch (surfaceKind) { case 'income_statement': case 'balance_sheet': case 'cash_flow_statement': return displayMode === 'faithful' ? financials.statementRows?.faithful ?? [] : financials.statementRows?.standardized ?? []; case 'ratios': return financials.ratioRows ?? []; case 'segments_kpis': return financials.kpiRows ?? []; default: return []; } }, [displayMode, financials, surfaceKind]); const filteredRows = useMemo(() => { const normalizedSearch = rowSearch.trim().toLowerCase(); if (!normalizedSearch) { return activeRows; } return activeRows.filter((row) => row.label.toLowerCase().includes(normalizedSearch)); }, [activeRows, rowSearch]); const groupedRows = useMemo(() => { return groupRows(filteredRows, financials?.categories ?? []); }, [filteredRows, financials?.categories]); const selectedRow = useMemo(() => { if (!selectedRowKey) { return null; } return activeRows.find((row) => row.key === selectedRowKey) ?? null; }, [activeRows, selectedRowKey]); const dimensionRows = useMemo(() => { if (!selectedRow || !financials?.dimensionBreakdown) { return []; } return financials.dimensionBreakdown[selectedRow.key] ?? []; }, [financials?.dimensionBreakdown, selectedRow]); const commonSizeRow = useMemo(() => { if (displayMode === 'faithful' || !financials?.statementRows) { return null; } const standardizedRows = financials.statementRows.standardized; if (surfaceKind === 'income_statement' || surfaceKind === 'cash_flow_statement') { return standardizedRows.find((row) => row.key === 'revenue') ?? null; } if (surfaceKind === 'balance_sheet') { return standardizedRows.find((row) => row.key === 'total_assets') ?? null; } return null; }, [displayMode, financials?.statementRows, surfaceKind]); const trendSeries = financials?.trendSeries ?? []; const chartData = useMemo(() => { return periods.map((period) => ({ label: formatLongDate(period.periodEnd ?? period.filingDate), ...Object.fromEntries(trendSeries.map((series) => [series.key, series.values[period.id] ?? null])) })); }, [periods, trendSeries]); const controlSections = useMemo(() => { const sections: FinancialControlSection[] = [ { id: 'surface', label: 'Surface', value: surfaceKind, options: SURFACE_OPTIONS, onChange: (value) => { setSurfaceKind(value as FinancialSurfaceKind); setSelectedRowKey(null); } }, { id: 'cadence', label: 'Cadence', value: cadence, options: CADENCE_OPTIONS, onChange: (value) => { setCadence(value as FinancialCadence); setSelectedRowKey(null); } } ]; if (surfaceKind === 'income_statement' || surfaceKind === 'balance_sheet' || surfaceKind === 'cash_flow_statement') { sections.push({ id: 'display', label: 'Display', value: displayMode, options: DISPLAY_MODE_OPTIONS, onChange: (value) => { setDisplayMode(value as FinancialDisplayMode); setSelectedRowKey(null); } }); } sections.push({ id: 'scale', label: 'Scale', value: valueScale, options: FINANCIAL_VALUE_SCALE_OPTIONS, onChange: (value) => setValueScale(value as NumberScaleUnit) }); return sections; }, [cadence, displayMode, surfaceKind, valueScale]); const controlActions = useMemo(() => { const actions: FinancialControlAction[] = []; if ((surfaceKind === 'income_statement' || surfaceKind === 'balance_sheet' || surfaceKind === 'cash_flow_statement' || surfaceKind === 'ratios') && displayMode !== 'faithful') { actions.push({ id: 'percent-change', label: showPercentChange ? '% Change On' : '% Change', variant: showPercentChange ? 'primary' : 'secondary', onClick: () => { setShowPercentChange((current) => !current); setShowCommonSize(false); } }); actions.push({ id: 'common-size', label: showCommonSize ? 'Common Size On' : 'Common Size', variant: showCommonSize ? 'primary' : 'secondary', onClick: () => { setShowCommonSize((current) => !current); setShowPercentChange(false); }, disabled: surfaceKind === 'ratios' && selectedRow !== null && isDerivedRow(selectedRow) && selectedRow.unit !== 'percent' }); } if (financials?.nextCursor) { actions.push({ id: 'load-older', label: loadingMore ? 'Loading Older...' : 'Load Older', variant: 'secondary', disabled: loadingMore, onClick: () => { if (!financials.nextCursor) { return; } void loadFinancials(ticker, { cursor: financials.nextCursor, append: true }); } }); } return actions; }, [displayMode, financials?.nextCursor, loadFinancials, loadingMore, selectedRow, showCommonSize, showPercentChange, surfaceKind, ticker]); if (isPending || !isAuthenticated) { return
Loading financial terminal...
; } return ( )} >
{ event.preventDefault(); const normalized = tickerInput.trim().toUpperCase(); if (!normalized) { return; } setTicker(normalized); }} > setTickerInput(event.target.value.toUpperCase())} placeholder="Ticker (AAPL)" className="w-full sm: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 prefetchResearchTicker(financials.company.ticker)} onFocus={() => prefetchResearchTicker(financials.company.ticker)} className="text-sm text-[color:var(--accent)] hover:text-[color:var(--accent-strong)]" > Open graphing ) : null}
{error ? (

{error}

) : null}
setRowSearch(event.target.value)} placeholder="Search rows by label" className="w-full sm:max-w-sm" /> {filteredRows.length} of {activeRows.length} rows
{loading ? (

Loading trend chart...

) : chartData.length === 0 || trendSeries.length === 0 ? (

No trend data available for the selected surface.

) : ( chartTickFormatter(value, trendSeries[0]?.unit ?? 'currency', valueScale)} /> { const numeric = Number(value); const unit = trendSeries.find((series) => series.key === entry.dataKey)?.unit ?? 'currency'; return formatMetricValue(Number.isFinite(numeric) ? numeric : null, unit, valueScale); }} contentStyle={{ backgroundColor: CHART_TOOLTIP_BG, border: `1px solid ${CHART_TOOLTIP_BORDER}`, borderRadius: '0.75rem' }} /> {trendSeries.map((series, index) => ( ))} )}
{loading ? (

Loading financial matrix...

) : surfaceKind === 'adjusted' || surfaceKind === 'custom_metrics' ? (

This surface is not yet available in v1.

Adjusted and custom metrics are API-visible placeholders only. No edits or derived rows are available yet.

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

No rows available for the selected filters yet.

) : (
{periods.map((period) => ( ))} {groupedRows.map((group) => ( {group.label ? ( ) : null} {group.rows.map((row) => ( setSelectedRowKey(row.key)} > {periods.map((period, index) => ( ))} ))} ))}
Metric
{formatLongDate(period.periodEnd ?? period.filingDate)} {period.filingType} ยท {period.periodLabel}
{group.label}
{row.label} {'hasDimensions' in row && row.hasDimensions ? : null}
{isDerivedRow(row) && row.formulaKey ? ( Formula: {row.formulaKey} ) : null} {isKpiRow(row) ? ( Provenance: {row.provenanceType} ) : null}
{buildDisplayValue({ row, periodId: period.id, previousPeriodId: index > 0 ? periods[index - 1]?.id ?? null : null, commonSizeRow, displayMode, showPercentChange, showCommonSize, scale: valueScale, surfaceKind })}
)}
{!selectedRow ? (

Select a row to inspect details.

) : (

Label

{selectedRow.label}

Key

{selectedRow.key}

{isTaxonomyRow(selectedRow) ? (

Taxonomy Concept

{selectedRow.qname}

) : (

Category

{selectedRow.category}

Unit

{selectedRow.unit}

)} {isDerivedRow(selectedRow) ? (

Source Row Keys

{selectedRow.sourceRowKeys.join(', ') || 'n/a'}

Source Concepts

{selectedRow.sourceConcepts.join(', ') || 'n/a'}

Source Fact IDs

{selectedRow.sourceFactIds.join(', ') || 'n/a'}

) : null} {!selectedRow || !('hasDimensions' in selectedRow) || !selectedRow.hasDimensions ? (

No dimensional drill-down is available for this row.

) : dimensionRows.length === 0 ? (

No dimensional facts were returned for the selected row.

) : (
{dimensionRows.map((row, index) => ( ))}
Period Axis Member Value
{periods.find((period) => period.id === row.periodId)?.periodLabel ?? row.periodId} {row.axis} {row.member} {formatMetricValue(row.value, 'currency', valueScale)}
)}
)}
{(surfaceKind === 'income_statement' || surfaceKind === 'balance_sheet' || surfaceKind === 'cash_flow_statement') && financials ? (
Overall status: {financials.metrics.validation?.status ?? 'not_run'}
{(financials.metrics.validation?.checks.length ?? 0) === 0 ? (

No validation checks available yet.

) : (
{financials.metrics.validation?.checks.map((check) => ( ))}
Metric Taxonomy LLM (PDF) Status Pages
{check.metricKey} {formatMetricValue(check.taxonomyValue, 'currency', valueScale)} {formatMetricValue(check.llmValue, 'currency', valueScale)} {check.status} {check.evidencePages.join(', ') || 'n/a'}
)}
) : null}
); } export default function FinancialsPage() { return ( Loading financial terminal...}> ); }