'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, Legend, Line, LineChart, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts'; import { ChartNoAxesCombined, RefreshCcw, Search } from 'lucide-react'; import { AppShell } from '@/components/shell/app-shell'; import { MetricCard } from '@/components/dashboard/metric-card'; 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 { queryKeys } from '@/lib/query/keys'; import { companyAnalysisQueryOptions } from '@/lib/query/options'; import type { CompanyAnalysis } from '@/lib/types'; type StatementPeriodPoint = { filingDate: string; filingType: '10-K' | '10-Q'; periodLabel: 'Quarter End' | 'Fiscal Year End'; }; type FinancialSeriesPoint = StatementPeriodPoint & { periodKind: 'quarterly' | 'fiscalYearEnd'; label: string; revenue: number | null; netIncome: number | null; totalAssets: number | null; cash: number | null; debt: number | null; netMargin: number | null; debtToAssets: number | null; }; type CompanyMetricPoint = StatementPeriodPoint & { metrics: string[]; }; type StatementMatrixRow = { key: string; label: string; value: (point: TPoint, index: number, series: TPoint[]) => string; valueClassName?: (point: TPoint, index: number, series: TPoint[]) => string | undefined; }; type ChartPeriodFilter = 'quarterlyAndFiscalYearEnd' | 'quarterlyOnly' | 'fiscalYearEndOnly'; const CHART_PERIOD_FILTER_OPTIONS: Array<{ value: ChartPeriodFilter; label: string }> = [ { value: 'quarterlyAndFiscalYearEnd', label: 'Quarterly + Fiscal Year End' }, { value: 'quarterlyOnly', label: 'Quarterly only' }, { value: 'fiscalYearEndOnly', label: 'Fiscal Year End only' } ]; 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 CHART_TEXT = '#e8fff8'; 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 renderLegendLabel(value: string) { return {value}; } function formatShortDate(value: string) { const parsed = new Date(value); if (Number.isNaN(parsed.getTime())) { return 'Unknown'; } return format(parsed, 'MMM yyyy'); } function formatLongDate(value: string) { const parsed = new Date(value); if (Number.isNaN(parsed.getTime())) { return 'Unknown'; } return format(parsed, 'MMM dd, yyyy'); } function ratioPercent(numerator: number | null, denominator: number | null) { if (numerator === null || denominator === null || denominator === 0) { return null; } return (numerator / denominator) * 100; } function asDisplayCurrency( value: number | null, scale: NumberScaleUnit ) { return value === null ? 'n/a' : formatCurrencyByScale(value, scale); } function asDisplayPercent(value: number | null) { return value === null ? 'n/a' : formatPercent(value); } function asDisplayMultiple(value: number | null) { return value === null ? 'n/a' : `${value.toFixed(2)}x`; } function asDisplaySignedCurrency( value: number | null, scale: NumberScaleUnit ) { if (value === null) { return 'n/a'; } const formatted = formatCurrencyByScale(value, scale); return value > 0 ? `+${formatted}` : formatted; } function normalizeTooltipValue(value: unknown) { if (Array.isArray(value)) { const [first] = value; return typeof first === 'number' || typeof first === 'string' ? first : null; } if (typeof value === 'number' || typeof value === 'string') { return value; } return null; } function asTooltipCurrency( value: unknown, scale: NumberScaleUnit ) { const normalized = normalizeTooltipValue(value); if (normalized === null) { return 'n/a'; } const numeric = Number(normalized); if (!Number.isFinite(numeric)) { return 'n/a'; } return formatCurrencyByScale(numeric, scale); } function asAxisCurrencyTick( value: number, scale: NumberScaleUnit ) { return formatCurrencyByScale(value, scale, { maximumFractionDigits: 1 }); } function includesChartPeriod(point: FinancialSeriesPoint, filter: ChartPeriodFilter) { if (filter === 'quarterlyOnly') { return point.periodKind === 'quarterly'; } if (filter === 'fiscalYearEndOnly') { return point.periodKind === 'fiscalYearEnd'; } return true; } function ratioMultiple(numerator: number | null, denominator: number | null) { if (numerator === null || denominator === null || denominator === 0) { return null; } return numerator / denominator; } function StatementMatrixTable({ points, rows }: { points: TPoint[]; rows: Array>; }) { return (
{points.map((point, index) => ( ))} {rows.map((row) => ( {points.map((point, index, series) => ( ))} ))}
Metric
{formatLongDate(point.filingDate)} {point.filingType} · {point.periodLabel}
{row.label} {row.value(point, index, series)}
); } function buildOperatingStatementRows( scale: NumberScaleUnit ): Array> { return [ { key: 'revenue', label: 'Revenue', value: (point) => asDisplayCurrency(point.revenue, scale) }, { key: 'net-income', label: 'Net Income', value: (point) => asDisplayCurrency(point.netIncome, scale), valueClassName: (point) => { if (point.netIncome === null) { return undefined; } return point.netIncome >= 0 ? 'text-[#96f5bf]' : 'text-[#ff9f9f]'; } }, { key: 'net-margin', label: 'Net Margin', value: (point) => asDisplayPercent(point.netMargin) } ]; } function buildBalanceSheetRows( scale: NumberScaleUnit ): Array> { return [ { key: 'total-assets', label: 'Total Assets', value: (point) => asDisplayCurrency(point.totalAssets, scale) }, { key: 'cash', label: 'Cash', value: (point) => asDisplayCurrency(point.cash, scale) }, { key: 'debt', label: 'Debt', value: (point) => asDisplayCurrency(point.debt, scale) }, { key: 'debt-to-assets', label: 'Debt / Assets', value: (point) => asDisplayPercent(point.debtToAssets), valueClassName: (point) => { if (point.debtToAssets === null) { return undefined; } return point.debtToAssets <= 60 ? 'text-[#96f5bf]' : 'text-[#ffd08a]'; } } ]; } function buildCashFlowProxyRows( scale: NumberScaleUnit ): Array> { return [ { key: 'ending-cash', label: 'Ending Cash Balance', value: (point) => asDisplayCurrency(point.cash, scale) }, { key: 'cash-change', label: 'Cash Change vs Prior', value: (point, index, series) => { if (index === 0) { return 'n/a'; } const previous = series[index - 1]; if (point.cash === null || previous.cash === null) { return 'n/a'; } return asDisplaySignedCurrency(point.cash - previous.cash, scale); }, valueClassName: (point, index, series) => { if (index === 0) { return undefined; } const previous = series[index - 1]; if (point.cash === null || previous.cash === null) { return undefined; } return point.cash - previous.cash >= 0 ? 'text-[#96f5bf]' : 'text-[#ff9f9f]'; } }, { key: 'debt-change', label: 'Debt Change vs Prior', value: (point, index, series) => { if (index === 0) { return 'n/a'; } const previous = series[index - 1]; if (point.debt === null || previous.debt === null) { return 'n/a'; } return asDisplaySignedCurrency(point.debt - previous.debt, scale); }, valueClassName: (point, index, series) => { if (index === 0) { return undefined; } const previous = series[index - 1]; if (point.debt === null || previous.debt === null) { return undefined; } return point.debt - previous.debt <= 0 ? 'text-[#96f5bf]' : 'text-[#ff9f9f]'; } }, { key: 'cash-to-debt', label: 'Cash / Debt', value: (point) => asDisplayMultiple(ratioMultiple(point.cash, point.debt)) } ]; } function isFinancialPeriodForm(filingType: CompanyAnalysis['financials'][number]['filingType']): filingType is '10-K' | '10-Q' { return filingType === '10-K' || filingType === '10-Q'; } 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 [analysis, setAnalysis] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [chartPeriodFilter, setChartPeriodFilter] = useState('quarterlyAndFiscalYearEnd'); const [financialValueScale, setFinancialValueScale] = useState('millions'); useEffect(() => { const fromQuery = searchParams.get('ticker'); if (!fromQuery) { return; } const normalized = fromQuery.trim().toUpperCase(); if (!normalized) { return; } setTickerInput(normalized); setTicker(normalized); }, [searchParams]); const loadFinancials = useCallback(async (symbol: string) => { const options = companyAnalysisQueryOptions(symbol); if (!queryClient.getQueryData(options.queryKey)) { setLoading(true); } setError(null); try { const response = await queryClient.ensureQueryData(options); setAnalysis(response.analysis); } catch (err) { setError(err instanceof Error ? err.message : 'Unable to load financial history'); setAnalysis(null); } finally { setLoading(false); } }, [queryClient]); useEffect(() => { if (!isPending && isAuthenticated) { void loadFinancials(ticker); } }, [isPending, isAuthenticated, ticker, loadFinancials]); const financialSeries = useMemo(() => { return (analysis?.financials ?? []) .filter((entry): entry is CompanyAnalysis['financials'][number] & { filingType: '10-K' | '10-Q' } => { return isFinancialPeriodForm(entry.filingType); }) .slice() .sort((a, b) => Date.parse(a.filingDate) - Date.parse(b.filingDate)) .map((entry) => ({ filingDate: entry.filingDate, filingType: entry.filingType, periodKind: entry.filingType === '10-Q' ? 'quarterly' : 'fiscalYearEnd', periodLabel: entry.filingType === '10-Q' ? 'Quarter End' : 'Fiscal Year End', label: formatShortDate(entry.filingDate), revenue: entry.revenue ?? null, netIncome: entry.netIncome ?? null, totalAssets: entry.totalAssets ?? null, cash: entry.cash ?? null, debt: entry.debt ?? null, netMargin: ratioPercent(entry.netIncome ?? null, entry.revenue ?? null), debtToAssets: ratioPercent(entry.debt ?? null, entry.totalAssets ?? null) })); }, [analysis?.financials]); const chartSeries = useMemo(() => { return financialSeries.filter((point) => includesChartPeriod(point, chartPeriodFilter)); }, [financialSeries, chartPeriodFilter]); const selectedChartFilterLabel = useMemo(() => { return CHART_PERIOD_FILTER_OPTIONS.find((option) => option.value === chartPeriodFilter)?.label ?? 'Quarterly + Fiscal Year End'; }, [chartPeriodFilter]); const selectedFinancialScaleLabel = useMemo(() => { return FINANCIAL_VALUE_SCALE_OPTIONS.find((option) => option.value === financialValueScale)?.label ?? 'Millions (M)'; }, [financialValueScale]); const latestSnapshot = financialSeries[financialSeries.length - 1] ?? null; const liquidityRatio = useMemo(() => { if (!latestSnapshot || latestSnapshot.cash === null || latestSnapshot.debt === null || latestSnapshot.debt === 0) { return null; } return latestSnapshot.cash / latestSnapshot.debt; }, [latestSnapshot]); const coverage = useMemo(() => { const total = chartSeries.length; const asCoverage = (entries: number) => { if (total === 0) { return '0%'; } return `${Math.round((entries / total) * 100)}%`; }; return { total, revenue: asCoverage(chartSeries.filter((point) => point.revenue !== null).length), netIncome: asCoverage(chartSeries.filter((point) => point.netIncome !== null).length), assets: asCoverage(chartSeries.filter((point) => point.totalAssets !== null).length), cash: asCoverage(chartSeries.filter((point) => point.cash !== null).length), debt: asCoverage(chartSeries.filter((point) => point.debt !== null).length) }; }, [chartSeries]); const companyMetricSeries = useMemo(() => { return (analysis?.filings ?? []) .filter((entry): entry is CompanyAnalysis['filings'][number] & { filing_type: '10-K' | '10-Q' } => { return isFinancialPeriodForm(entry.filing_type); }) .slice() .sort((a, b) => Date.parse(a.filing_date) - Date.parse(b.filing_date)) .map((entry) => ({ filingDate: entry.filing_date, filingType: entry.filing_type, periodLabel: entry.filing_type === '10-Q' ? 'Quarter End' : 'Fiscal Year End', metrics: entry.analysis?.companyMetrics ?? [] })); }, [analysis?.filings]); const companyMetricRows = useMemo>>(() => { const rowCount = companyMetricSeries.reduce((max, point) => Math.max(max, point.metrics.length), 0); return Array.from({ length: rowCount }, (_, index) => ({ key: `company-metric-${index + 1}`, label: `Metric ${index + 1}`, value: (point) => point.metrics[index] ?? 'n/a' })); }, [companyMetricSeries]); const operatingStatementRows = useMemo(() => { return buildOperatingStatementRows(financialValueScale); }, [financialValueScale]); const balanceSheetRows = useMemo(() => { return buildBalanceSheetRows(financialValueScale); }, [financialValueScale]); const cashFlowProxyRows = useMemo(() => { return buildCashFlowProxyRows(financialValueScale); }, [financialValueScale]); if (isPending || !isAuthenticated) { return
Loading financial terminal...
; } return ( { void queryClient.invalidateQueries({ queryKey: queryKeys.companyAnalysis(ticker.trim().toUpperCase()) }); 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" /> {analysis ? ( <> prefetchResearchTicker(analysis.company.ticker)} onFocus={() => prefetchResearchTicker(analysis.company.ticker)} className="text-sm text-[color:var(--accent)] hover:text-[color:var(--accent-strong)]" > Open full analysis prefetchResearchTicker(analysis.company.ticker)} onFocus={() => prefetchResearchTicker(analysis.company.ticker)} className="text-sm text-[color:var(--accent)] hover:text-[color:var(--accent-strong)]" > Open filings stream ) : null}
{error ? (

{error}

) : null}
= 0} />
{CHART_PERIOD_FILTER_OPTIONS.map((option) => ( ))}
{FINANCIAL_VALUE_SCALE_OPTIONS.map((option) => ( ))}
{loading ? (

Loading statement data...

) : chartSeries.length === 0 ? (

No filing metrics match the current chart period filter.

) : (
asAxisCurrencyTick(value, financialValueScale)} /> asTooltipCurrency(value, financialValueScale)} contentStyle={{ backgroundColor: CHART_TOOLTIP_BG, border: `1px solid ${CHART_TOOLTIP_BORDER}`, borderRadius: '0.75rem' }} labelStyle={{ color: CHART_TEXT }} itemStyle={{ color: CHART_TEXT }} cursor={{ fill: 'rgba(104, 255, 213, 0.08)' }} />
)}
{loading ? (

Loading balance sheet data...

) : chartSeries.length === 0 ? (

No balance sheet metrics match the current chart period filter.

) : (
asAxisCurrencyTick(value, financialValueScale)} /> asTooltipCurrency(value, financialValueScale)} contentStyle={{ backgroundColor: CHART_TOOLTIP_BG, border: `1px solid ${CHART_TOOLTIP_BORDER}`, borderRadius: '0.75rem' }} labelStyle={{ color: CHART_TEXT }} itemStyle={{ color: CHART_TEXT }} cursor={{ fill: 'rgba(104, 255, 213, 0.08)' }} />
)}
{loading ? (

Loading ratio trends...

) : chartSeries.length === 0 ? (

No ratio points match the current chart period filter.

) : (
`${value.toFixed(0)}%`} /> { const normalized = normalizeTooltipValue(value); if (normalized === null) { return 'n/a'; } const numeric = Number(normalized); return Number.isFinite(numeric) ? `${numeric.toFixed(2)}%` : 'n/a'; }} contentStyle={{ backgroundColor: CHART_TOOLTIP_BG, border: `1px solid ${CHART_TOOLTIP_BORDER}`, borderRadius: '0.75rem' }} labelStyle={{ color: CHART_TEXT }} itemStyle={{ color: CHART_TEXT }} cursor={{ stroke: 'rgba(104, 255, 213, 0.35)', strokeWidth: 1 }} />
)}
Revenue coverage
{coverage.revenue}
Net income coverage
{coverage.netIncome}
Asset coverage
{coverage.assets}
Cash coverage
{coverage.cash}
Debt coverage
{coverage.debt}
{loading ? (

Loading operating statement...

) : financialSeries.length === 0 ? (

No operating statement rows are available for this ticker yet.

) : ( )}
{loading ? (

Loading balance sheet...

) : financialSeries.length === 0 ? (

No balance sheet rows are available for this ticker yet.

) : ( )}
{loading ? (

Loading cash flow statement...

) : financialSeries.length === 0 ? (

No cash flow statement rows are available for this ticker yet.

) : ( )}
{loading ? (

Loading company metrics...

) : companyMetricSeries.length === 0 ? (

No filing periods are available for company metrics yet.

) : companyMetricRows.length === 0 ? (

No company-specific KPI metrics were extracted for the selected filings.

) : ( )}
Financial lens: revenue + margin + balance sheet strength
); }