'use client'; import Link from 'next/link'; import { Suspense, useCallback, useEffect, useMemo, useState } from 'react'; import { format } from 'date-fns'; import { CartesianGrid, Line, LineChart, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts'; import { BrainCircuit, ChartNoAxesCombined, RefreshCcw, Search } from 'lucide-react'; import { useSearchParams } from 'next/navigation'; import { AppShell } from '@/components/shell/app-shell'; 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 { getCompanyAnalysis } from '@/lib/api'; import { asNumber, formatCompactCurrency, formatCurrency, formatPercent } from '@/lib/format'; import type { CompanyAnalysis } from '@/lib/types'; type FinancialPeriodFilter = 'quarterlyAndFiscalYearEnd' | 'quarterlyOnly' | 'fiscalYearEndOnly'; type FinancialSeriesPoint = { filingDate: string; filingType: '10-K' | '10-Q'; periodLabel: 'Quarter End' | 'Fiscal Year End'; revenue: number | null; netIncome: number | null; assets: number | null; netMargin: number | null; }; const FINANCIAL_PERIOD_FILTER_OPTIONS: Array<{ value: FinancialPeriodFilter; label: string }> = [ { value: 'quarterlyAndFiscalYearEnd', label: 'Quarterly + Fiscal Year End' }, { value: 'quarterlyOnly', label: 'Quarterly only' }, { value: 'fiscalYearEndOnly', label: 'Fiscal Year End only' } ]; function formatShortDate(value: string) { return format(new Date(value), 'MMM yyyy'); } function formatLongDate(value: string) { return format(new Date(value), '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 isFinancialSnapshotForm( filingType: CompanyAnalysis['filings'][number]['filing_type'] ): filingType is '10-K' | '10-Q' { return filingType === '10-K' || filingType === '10-Q'; } function includesFinancialPeriod(filingType: '10-K' | '10-Q', filter: FinancialPeriodFilter) { if (filter === 'quarterlyOnly') { return filingType === '10-Q'; } if (filter === 'fiscalYearEndOnly') { return filingType === '10-K'; } return true; } export default function AnalysisPage() { return ( Loading analysis desk...}> ); } function AnalysisPageContent() { const { isPending, isAuthenticated } = useAuthGuard(); const searchParams = useSearchParams(); 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 [financialPeriodFilter, setFinancialPeriodFilter] = useState('quarterlyAndFiscalYearEnd'); useEffect(() => { const fromQuery = searchParams.get('ticker'); if (!fromQuery) { return; } const normalized = fromQuery.trim().toUpperCase(); if (!normalized) { return; } setTickerInput(normalized); setTicker(normalized); }, [searchParams]); const loadAnalysis = useCallback(async (symbol: string) => { setLoading(true); setError(null); try { const response = await getCompanyAnalysis(symbol); setAnalysis(response.analysis); } catch (err) { setError(err instanceof Error ? err.message : 'Unable to load company analysis'); setAnalysis(null); } finally { setLoading(false); } }, []); useEffect(() => { if (!isPending && isAuthenticated) { void loadAnalysis(ticker); } }, [isPending, isAuthenticated, ticker, loadAnalysis]); const priceSeries = useMemo(() => { return (analysis?.priceHistory ?? []).map((point) => ({ ...point, label: formatShortDate(point.date) })); }, [analysis?.priceHistory]); const financialSeries = useMemo(() => { return (analysis?.financials ?? []) .filter((item): item is CompanyAnalysis['financials'][number] & { filingType: '10-K' | '10-Q' } => { return isFinancialSnapshotForm(item.filingType); }) .slice() .sort((a, b) => Date.parse(a.filingDate) - Date.parse(b.filingDate)) .map((item) => ({ filingDate: item.filingDate, filingType: item.filingType, periodLabel: item.filingType === '10-Q' ? 'Quarter End' : 'Fiscal Year End', revenue: item.revenue, netIncome: item.netIncome, assets: item.totalAssets, netMargin: ratioPercent(item.netIncome ?? null, item.revenue ?? null) })); }, [analysis?.financials]); const filteredFinancialSeries = useMemo(() => { return financialSeries.filter((point) => includesFinancialPeriod(point.filingType, financialPeriodFilter)); }, [financialSeries, financialPeriodFilter]); const periodEndFilings = useMemo(() => { return (analysis?.filings ?? []).filter((filing) => isFinancialSnapshotForm(filing.filing_type)); }, [analysis?.filings]); if (isPending || !isAuthenticated) { return
Loading analysis desk...
; } return ( void loadAnalysis(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 ? ( Open filing stream ) : null}
{error ? (

{error}

) : null}

{analysis?.company.companyName ?? ticker}

{analysis?.company.ticker ?? ticker}

{analysis?.company.sector ?? 'Sector unavailable'}

{formatCurrency(analysis?.quote ?? 0)}

CIK {analysis?.company.cik ?? 'n/a'}

{formatCurrency(analysis?.position?.market_value)}

{analysis?.position ? `${asNumber(analysis.position.shares).toLocaleString()} shares` : 'Not held in portfolio'}

= 0 ? 'text-[#96f5bf]' : 'text-[#ff9f9f]'}`}> {formatCurrency(analysis?.position?.gain_loss)}

{formatPercent(analysis?.position?.gain_loss_pct)}

{loading ? (

Loading price history...

) : priceSeries.length === 0 ? (

No price history available.

) : (
`$${value.toFixed(0)}`} /> formatCurrency(value)} />
)}
{FINANCIAL_PERIOD_FILTER_OPTIONS.map((option) => ( ))}
)} > {loading ? (

Loading financials...

) : filteredFinancialSeries.length === 0 ? (

No financial rows match the selected period filter.

) : (
{filteredFinancialSeries.map((point, index) => ( ))}
Filed Period Form Revenue Net Income Assets Net Margin
{formatLongDate(point.filingDate)} {point.periodLabel} {point.filingType} {point.revenue === null ? 'n/a' : formatCompactCurrency(point.revenue)} = 0 ? 'text-[#96f5bf]' : 'text-[#ff9f9f]'}> {point.netIncome === null ? 'n/a' : formatCompactCurrency(point.netIncome)} {point.assets === null ? 'n/a' : formatCompactCurrency(point.assets)} {point.netMargin === null ? 'n/a' : formatPercent(point.netMargin)}
)} {loading ? (

Loading filings...

) : periodEndFilings.length === 0 ? (

No 10-K or 10-Q filings available for this ticker.

) : (
{periodEndFilings.map((filing) => ( ))}
Filed Period Type Revenue Net Income Assets Document
{format(new Date(filing.filing_date), 'MMM dd, yyyy')} {filing.filing_type === '10-Q' ? 'Quarter End' : 'Fiscal Year End'} {filing.filing_type} {filing.metrics?.revenue !== null && filing.metrics?.revenue !== undefined ? formatCompactCurrency(filing.metrics.revenue) : 'n/a'} {filing.metrics?.netIncome !== null && filing.metrics?.netIncome !== undefined ? formatCompactCurrency(filing.metrics.netIncome) : 'n/a'} {filing.metrics?.totalAssets !== null && filing.metrics?.totalAssets !== undefined ? formatCompactCurrency(filing.metrics.totalAssets) : 'n/a'} {filing.filing_url ? ( SEC filing ) : ( 'n/a' )}
)}
{loading ? (

Loading AI reports...

) : !analysis || analysis.aiReports.length === 0 ? (

No AI reports generated yet. Run filing analysis from the filings stream.

) : (
{analysis.aiReports.map((report) => (

{report.filingType} ยท {format(new Date(report.filingDate), 'MMM dd, yyyy')}

{report.provider} / {report.model}

{report.summary}

{report.accessionNumber}

Open summary
))}
)}
Analysis scope: price + filings + ai synthesis
); }