'use client'; import { useQueryClient } from '@tanstack/react-query'; import { useCallback, useEffect, useState } from 'react'; import { ArrowRight, Eye, Plus, Trash2 } from 'lucide-react'; import Link from 'next/link'; 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 { useLinkPrefetch } from '@/hooks/use-link-prefetch'; import { useAuthGuard } from '@/hooks/use-auth-guard'; import { useTaskPoller } from '@/hooks/use-task-poller'; import { deleteWatchlistItem, queueFilingSync, upsertWatchlistItem } from '@/lib/api'; import type { Task, WatchlistItem } from '@/lib/types'; import { queryKeys } from '@/lib/query/keys'; import { taskQueryOptions, watchlistQueryOptions } from '@/lib/query/options'; type FormState = { ticker: string; companyName: string; sector: string; }; export default function WatchlistPage() { const { isPending, isAuthenticated } = useAuthGuard(); const queryClient = useQueryClient(); const { prefetchResearchTicker } = useLinkPrefetch(); const [items, setItems] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [activeTask, setActiveTask] = useState(null); const [form, setForm] = useState({ ticker: '', companyName: '', sector: '' }); const loadWatchlist = useCallback(async () => { const options = watchlistQueryOptions(); if (!queryClient.getQueryData(options.queryKey)) { setLoading(true); } setError(null); try { const response = await queryClient.ensureQueryData(options); setItems(response.items); } catch (err) { setError(err instanceof Error ? err.message : 'Failed to load watchlist'); } finally { setLoading(false); } }, [queryClient]); useEffect(() => { if (!isPending && isAuthenticated) { void loadWatchlist(); } }, [isPending, isAuthenticated, loadWatchlist]); const polledTask = useTaskPoller({ taskId: activeTask?.id ?? null, onTerminalState: () => { setActiveTask(null); void queryClient.invalidateQueries({ queryKey: queryKeys.recentTasks(20) }); void queryClient.invalidateQueries({ queryKey: ['filings'] }); } }); const liveTask = polledTask ?? activeTask; const submit = async (event: React.FormEvent) => { event.preventDefault(); try { await upsertWatchlistItem({ ticker: form.ticker.toUpperCase(), companyName: form.companyName, sector: form.sector || undefined }); setForm({ ticker: '', companyName: '', sector: '' }); void queryClient.invalidateQueries({ queryKey: queryKeys.watchlist() }); await loadWatchlist(); } catch (err) { setError(err instanceof Error ? err.message : 'Failed to save watchlist item'); } }; const queueSync = async (ticker: string) => { try { const { task } = await queueFilingSync({ ticker, limit: 20 }); const latest = await queryClient.fetchQuery(taskQueryOptions(task.id)); setActiveTask(latest.task); void queryClient.invalidateQueries({ queryKey: queryKeys.recentTasks(20) }); } catch (err) { setError(err instanceof Error ? err.message : `Failed to queue sync for ${ticker}`); } }; if (isPending || !isAuthenticated) { return
Loading watchlist terminal...
; } return ( {liveTask ? (

{liveTask.task_type}

) : null}
{error ?

{error}

: null} {loading ? (

Loading watchlist...

) : items.length === 0 ? (

No symbols yet. Add one from the right panel.

) : (
{items.map((item) => (

{item.sector ?? 'Unclassified'}

{item.ticker}

{item.company_name}

prefetchResearchTicker(item.ticker)} onFocus={() => prefetchResearchTicker(item.ticker)} className="inline-flex items-center gap-1 text-xs text-[color:var(--accent)] hover:text-[color:var(--accent-strong)]" > Open stream prefetchResearchTicker(item.ticker)} onFocus={() => prefetchResearchTicker(item.ticker)} className="inline-flex items-center gap-1 text-xs text-[color:var(--accent)] hover:text-[color:var(--accent-strong)]" > Analyze
))}
)}
setForm((prev) => ({ ...prev, ticker: event.target.value.toUpperCase() }))} required />
setForm((prev) => ({ ...prev, companyName: event.target.value }))} required />
setForm((prev) => ({ ...prev, sector: event.target.value }))} />
); }