import { useEffect } from 'react'; import { useParams, useSearchParams } from 'react-router-dom'; import { Badge } from '@/components/ui/badge'; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { ScrollArea } from "@/components/ui/scroll-area" import { WorkflowVisualizer } from '@/components/workflow/WorkflowVisualizer'; import { useWorkflowStore } from '@/stores/useWorkflowStore'; import { TaskStatus, schemas } from '@/api/schema.gen'; import { Terminal, Loader2, Sparkles, CheckCircle2 } from 'lucide-react'; import { Button } from '@/components/ui/button'; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; import { FinancialTable } from '@/components/report/FinancialTable'; import { useAnalysisTemplates } from "@/hooks/useConfig" export function ReportPage() { const { id } = useParams(); const [searchParams] = useSearchParams(); const symbol = searchParams.get('symbol'); const market = searchParams.get('market'); const templateId = searchParams.get('templateId'); const { initialize, handleEvent, status, tasks, dag, activeTab, setActiveTab } = useWorkflowStore(); const { data: templates } = useAnalysisTemplates(); const templateName = templates && templateId ? templates[templateId]?.name : templateId; // SSE Connection Logic useEffect(() => { if (!id) return; initialize(id); // Connect to real backend SSE const eventSource = new EventSource(`/api/v1/workflow/events/${id}`); eventSource.onmessage = (event) => { try { const parsedEvent = JSON.parse(event.data); handleEvent(parsedEvent); } catch (e) { console.error("Failed to parse SSE event:", e); } }; eventSource.onerror = (err) => { console.error("SSE Connection Error:", err); // Optional: Retry logic or error state update // eventSource.close(); }; return () => { eventSource.close(); }; }, [id, initialize, handleEvent]); // Combine logs from all tasks for the "Global Log" view const allLogs = Object.entries(tasks).flatMap(([taskId, state]) => state.logs.map(log => ({ taskId, log })) ); const tabNodes = dag?.nodes.filter(n => n.type === schemas.TaskType.enum.Analysis) || []; return (
{/* Header Area */}

{symbol} {market}

Request ID: {id} {templateName && Template: {templateName}}
{/* Main Content Grid */}
{/* Left Col: Visualizer & Logs (4 cols) */}
Workflow Status Real-time Logs
{allLogs.length === 0 && Waiting for logs...} {allLogs.map((entry, i) => (
[{entry.taskId}]{" "} {entry.log}
))}
{/* Right Col: Detail Tabs (8 cols) */}
Overview Fundamental Data {tabNodes.map(node => ( {node.name} ))}
{/* Content Area */}

Analysis In Progress

Select a task tab above or click a node in the graph to view details.

{tabNodes.map(node => (
{tasks[node.id]?.content ? ( {tasks[node.id].content || ''} ) : (
{tasks[node.id]?.status === schemas.TaskStatus.enum.Pending &&

Waiting to start...

} {tasks[node.id]?.status === schemas.TaskStatus.enum.Running && !tasks[node.id]?.content && }
)} {tasks[node.id]?.status === schemas.TaskStatus.enum.Running && ( )}
))}
); } function WorkflowStatusBadge({ status }: { status: string }) { const variants: Record = { 'IDLE': 'outline', 'CONNECTING': 'secondary', 'RUNNING': 'default', 'COMPLETED': 'default', 'ERROR': 'destructive' }; let colorClass = ""; if (status === 'RUNNING') colorClass = "bg-blue-500 hover:bg-blue-600 border-transparent"; if (status === 'COMPLETED') colorClass = "bg-green-600 hover:bg-green-600 border-transparent"; return {status}; } function TaskStatusIndicator({ status }: { status: TaskStatus }) { switch (status) { case schemas.TaskStatus.enum.Running: return ; case schemas.TaskStatus.enum.Completed: return ; case schemas.TaskStatus.enum.Failed: return
; default: return null; } }