'use client'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { Suspense } from 'react'; import { format } from 'date-fns'; import { Bot, Download, ExternalLink, Search, TimerReset } from 'lucide-react'; import { useSearchParams } from 'next/navigation'; import { AppShell } from '@/components/shell/app-shell'; import { Panel } from '@/components/ui/panel'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { StatusPill } from '@/components/ui/status-pill'; import { useAuthGuard } from '@/hooks/use-auth-guard'; import { useTaskPoller } from '@/hooks/use-task-poller'; import { getTask, listFilings, queueFilingAnalysis, queueFilingSync } from '@/lib/api'; import type { Filing, Task } from '@/lib/types'; import { formatCompactCurrency } from '@/lib/format'; export default function FilingsPage() { return ( Opening filings stream...}> ); } function formatFilingDate(value: string) { const date = new Date(value); if (Number.isNaN(date.getTime())) { return 'Unknown'; } return format(date, 'MMM dd, yyyy'); } function resolveOriginalFilingUrl(filing: Filing) { if (filing.filing_url) { return filing.filing_url; } if (!filing.primary_document) { return null; } const cikDigits = filing.cik.replace(/\D/g, ''); const cikValue = Number.parseInt(cikDigits, 10); if (!cikDigits || Number.isNaN(cikValue)) { return null; } const compactAccession = filing.accession_number.replace(/-/g, ''); if (!compactAccession) { return null; } return `https://www.sec.gov/Archives/edgar/data/${cikValue}/${compactAccession}/${filing.primary_document}`; } type FilingExternalLinkProps = { href: string; label: string; }; function FilingExternalLink({ href, label }: FilingExternalLinkProps) { return ( {label} ); } function FilingsPageContent() { const { isPending, isAuthenticated } = useAuthGuard(); const searchParams = useSearchParams(); const [filings, setFilings] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [syncTickerInput, setSyncTickerInput] = useState(''); const [filterTickerInput, setFilterTickerInput] = useState(''); const [searchTicker, setSearchTicker] = useState(''); const [activeTask, setActiveTask] = useState(null); useEffect(() => { const ticker = searchParams.get('ticker'); if (ticker) { const normalized = ticker.toUpperCase(); setSyncTickerInput(normalized); setFilterTickerInput(normalized); setSearchTicker(normalized); } }, [searchParams]); const loadFilings = useCallback(async (ticker?: string) => { setLoading(true); setError(null); try { const response = await listFilings({ ticker, limit: 120 }); setFilings(response.filings); } catch (err) { setError(err instanceof Error ? err.message : 'Unable to fetch filings'); } finally { setLoading(false); } }, []); useEffect(() => { if (!isPending && isAuthenticated) { void loadFilings(searchTicker || undefined); } }, [isPending, isAuthenticated, searchTicker, loadFilings]); const polledTask = useTaskPoller({ taskId: activeTask?.id ?? null, onTerminalState: async () => { setActiveTask(null); await loadFilings(searchTicker || undefined); } }); const liveTask = polledTask ?? activeTask; const triggerSync = async () => { if (!syncTickerInput.trim()) { return; } try { const { task } = await queueFilingSync({ ticker: syncTickerInput.trim().toUpperCase(), limit: 20 }); const latest = await getTask(task.id); setActiveTask(latest.task); } catch (err) { setError(err instanceof Error ? err.message : 'Failed to queue filing sync'); } }; const triggerAnalysis = async (accessionNumber: string) => { try { const { task } = await queueFilingAnalysis(accessionNumber); const latest = await getTask(task.id); setActiveTask(latest.task); } catch (err) { setError(err instanceof Error ? err.message : 'Failed to queue filing analysis'); } }; const groupedByTicker = useMemo(() => { const counts = new Map(); for (const filing of filings) { counts.set(filing.ticker, (counts.get(filing.ticker) ?? 0) + 1); } return counts; }, [filings]); if (isPending || !isAuthenticated) { return
Opening filings stream...
; } return ( void loadFilings(searchTicker || undefined)}> Refresh table )} > {liveTask ? (

{liveTask.id}

{liveTask.error ?

{liveTask.error}

: null}
) : null}
{ event.preventDefault(); void triggerSync(); }} > setSyncTickerInput(event.target.value.toUpperCase())} placeholder="Ticker (AAPL)" className="w-full sm:max-w-xs" />
{ event.preventDefault(); setSearchTicker(filterTickerInput.trim().toUpperCase()); }} > setFilterTickerInput(event.target.value.toUpperCase())} placeholder="Ticker filter" className="w-full sm:max-w-xs" />
{error ?

{error}

: null} {loading ? (

Fetching filings...

) : filings.length === 0 ? (

No filings available. Queue a sync job to ingest fresh SEC data.

) : (
{filings.map((filing) => { const revenue = filing.metrics?.revenue; const hasAnalysis = Boolean(filing.analysis?.text || filing.analysis?.legacyInsights); const originalFilingUrl = resolveOriginalFilingUrl(filing); return (

{filing.ticker} ยท {filing.filing_type}

{formatFilingDate(filing.filing_date)}

{hasAnalysis ? 'AI ready' : 'AI pending'}

{filing.company_name}

Revenue Snapshot
{revenue ? formatCompactCurrency(revenue) : 'n/a'}
Accession
{filing.accession_number}
{originalFilingUrl ? ( ) : ( Original filing unavailable )} {filing.submission_url ? ( ) : null}
); })}
{filings.map((filing) => { const revenue = filing.metrics?.revenue; const hasAnalysis = Boolean(filing.analysis?.text || filing.analysis?.legacyInsights); const originalFilingUrl = resolveOriginalFilingUrl(filing); return ( ); })}
Ticker Type Filed Revenue Snapshot Company AI Links Action
{filing.ticker}
{groupedByTicker.get(filing.ticker)} filings
{filing.filing_type} {formatFilingDate(filing.filing_date)} {revenue ? formatCompactCurrency(revenue) : 'n/a'} {filing.company_name} {hasAnalysis ? 'Ready' : 'Not generated'}
{originalFilingUrl ? ( ) : ( Unavailable )} {filing.submission_url ? ( ) : null}
)}
); }