'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.

)}

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'}
); }