'use client'; import { useQueryClient } from '@tanstack/react-query'; import Link from 'next/link'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { Activity, Bot, RefreshCw, Sparkles } from 'lucide-react'; import { AppShell } from '@/components/shell/app-shell'; import { Panel } from '@/components/ui/panel'; import { Button } from '@/components/ui/button'; import { MetricCard } from '@/components/dashboard/metric-card'; import { TaskFeed } from '@/components/dashboard/task-feed'; import { useAuthGuard } from '@/hooks/use-auth-guard'; import { useLinkPrefetch } from '@/hooks/use-link-prefetch'; import { queuePortfolioInsights, queuePriceRefresh } from '@/lib/api'; import { buildGraphingHref } from '@/lib/graphing/catalog'; import type { PortfolioInsight, PortfolioSummary, Task } from '@/lib/types'; import { formatCompactCurrency, formatCurrency, formatPercent } from '@/lib/format'; import { queryKeys } from '@/lib/query/keys'; import { filingsQueryOptions, latestPortfolioInsightQueryOptions, portfolioSummaryQueryOptions, recentTasksQueryOptions, watchlistQueryOptions } from '@/lib/query/options'; type DashboardState = { summary: PortfolioSummary; filingsCount: number; watchlistCount: number; tasks: Task[]; latestInsight: PortfolioInsight | null; }; const EMPTY_STATE: DashboardState = { summary: { positions: 0, total_value: '0', total_gain_loss: '0', total_cost_basis: '0', avg_return_pct: '0' }, filingsCount: 0, watchlistCount: 0, tasks: [], latestInsight: null }; export default function CommandCenterPage() { const { isPending, isAuthenticated, session } = useAuthGuard(); const queryClient = useQueryClient(); const { prefetchPortfolioSurfaces } = useLinkPrefetch(); const [state, setState] = useState(EMPTY_STATE); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const loadData = useCallback(async () => { const summaryOptions = portfolioSummaryQueryOptions(); const filingsOptions = filingsQueryOptions({ limit: 200 }); const watchlistOptions = watchlistQueryOptions(); const tasksOptions = recentTasksQueryOptions(20); const insightOptions = latestPortfolioInsightQueryOptions(); if (!queryClient.getQueryData(summaryOptions.queryKey)) { setLoading(true); } setError(null); try { const [summaryRes, filingsRes, watchlistRes, tasksRes, insightRes] = await Promise.all([ queryClient.ensureQueryData(summaryOptions), queryClient.ensureQueryData(filingsOptions), queryClient.ensureQueryData(watchlistOptions), queryClient.ensureQueryData(tasksOptions), queryClient.ensureQueryData(insightOptions) ]); setState({ summary: summaryRes.summary, filingsCount: filingsRes.filings.length, watchlistCount: watchlistRes.items.length, tasks: tasksRes.tasks, latestInsight: insightRes.insight }); } catch (err) { setError(err instanceof Error ? err.message : 'Failed to load dashboard'); } finally { setLoading(false); } }, [queryClient]); useEffect(() => { if (!isPending && isAuthenticated) { void loadData(); } }, [isPending, isAuthenticated, loadData]); const headerActions = ( <> ); const signedGain = useMemo(() => { const gain = Number(state.summary.total_gain_loss ?? 0); return gain >= 0 ? `+${formatCurrency(gain)}` : formatCurrency(gain); }, [state.summary.total_gain_loss]); if (isPending || !isAuthenticated) { return
Booting secure terminal...
; } return ( {error ? (

{error}

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

Loading tasks...

) : ( )}
{loading ? (

Loading intelligence output...

) : state.latestInsight ? ( <>
{state.latestInsight.provider} :: {state.latestInsight.model}

{state.latestInsight.content}

) : (

No AI brief yet. Queue one from the action bar.

)}

Analysis

Inspect one company across prices, filings, financials, and AI reports.

Financials

Focus on multi-period filing metrics, margins, leverage, and balance sheet composition.

Graphing

Compare one normalized metric across multiple companies with shareable chart state.

{ void queryClient.prefetchQuery(filingsQueryOptions({ limit: 120 })); }} onFocus={() => { void queryClient.prefetchQuery(filingsQueryOptions({ limit: 120 })); }} >

Filings

Sync SEC filings and trigger AI memo analysis.

prefetchPortfolioSurfaces()} onFocus={() => prefetchPortfolioSurfaces()} >

Portfolio

Manage the active private portfolio and mark positions to market.

prefetchPortfolioSurfaces()} onFocus={() => prefetchPortfolioSurfaces()} >

Coverage

Track research status, review cadence, and filing freshness per company.

Runtime state: {loading ? 'syncing' : 'stable'}
); }