'use client'; 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 { StatusPill } from '@/components/ui/status-pill'; import { useAuthGuard } from '@/hooks/use-auth-guard'; import { useTaskPoller } from '@/hooks/use-task-poller'; import { getLatestPortfolioInsight, getPortfolioSummary, getTask, listFilings, listRecentTasks, listWatchlist, queuePortfolioInsights, queuePriceRefresh } from '@/lib/api'; import type { PortfolioInsight, PortfolioSummary, Task } from '@/lib/types'; import { formatCompactCurrency, formatCurrency, formatPercent } from '@/lib/format'; 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 [state, setState] = useState(EMPTY_STATE); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [activeTaskId, setActiveTaskId] = useState(null); const loadData = useCallback(async () => { setLoading(true); setError(null); try { const [summaryRes, filingsRes, watchlistRes, tasksRes, insightRes] = await Promise.all([ getPortfolioSummary(), listFilings({ limit: 200 }), listWatchlist(), listRecentTasks(20), getLatestPortfolioInsight() ]); 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); } }, []); useEffect(() => { if (!isPending && isAuthenticated) { void loadData(); } }, [isPending, isAuthenticated, loadData]); const trackedTask = useTaskPoller({ taskId: activeTaskId, onTerminalState: () => { setActiveTaskId(null); void 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 ( {activeTaskId && trackedTask ? (

{trackedTask.task_type.replace('_', ' ')}

{trackedTask.error ? (

{trackedTask.error}

) : null}
) : null} {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.

Filings

Sync SEC filings and trigger AI memo analysis.

Portfolio

Manage holdings and mark to market in real time.

Watchlist

Track priority tickers for monitoring and ingestion.

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