import { useState } from "react"; import { createFileRoute, useNavigate } from "@tanstack/react-router"; import { AlertCircle } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Skeleton } from "@/components/Skeleton"; import { toast } from "sonner"; import { trpc } from "@/lib/trpc"; import { InitiativeHeader } from "@/components/InitiativeHeader"; import { ContentTab } from "@/components/editor/ContentTab"; import { ExecutionTab } from "@/components/ExecutionTab"; import { useSubscriptionWithErrorHandling } from "@/hooks"; export const Route = createFileRoute("/initiatives/$id")({ component: InitiativeDetailPage, }); type Tab = "content" | "execution"; function InitiativeDetailPage() { const { id } = Route.useParams(); const navigate = useNavigate(); const [activeTab, setActiveTab] = useState("content"); // Live updates: keep subscriptions at page level so they work across both tabs const utils = trpc.useUtils(); // Task updates subscription with robust error handling useSubscriptionWithErrorHandling( () => trpc.onTaskUpdate.useSubscription(undefined), { onData: () => { void utils.listPhases.invalidate(); void utils.listTasks.invalidate(); void utils.listPlans.invalidate(); }, onError: (error) => { toast.error("Live updates disconnected. Refresh to reconnect.", { id: "sub-error", duration: Infinity, }); console.error('Task updates subscription error:', error); }, onStarted: () => toast.dismiss("sub-error"), autoReconnect: true, maxReconnectAttempts: 5, } ); // Agent updates subscription with robust error handling useSubscriptionWithErrorHandling( () => trpc.onAgentUpdate.useSubscription(undefined), { onData: () => { void utils.listAgents.invalidate(); }, onError: (error) => { toast.error("Live updates disconnected. Refresh to reconnect.", { id: "sub-error", duration: Infinity, }); console.error('Agent updates subscription error:', error); }, onStarted: () => toast.dismiss("sub-error"), autoReconnect: true, maxReconnectAttempts: 5, } ); // Page updates subscription with robust error handling useSubscriptionWithErrorHandling( () => trpc.onPageUpdate.useSubscription(undefined), { onData: () => { void utils.listPages.invalidate(); void utils.getPage.invalidate(); void utils.getRootPage.invalidate(); }, onError: (error) => { toast.error("Live updates disconnected. Refresh to reconnect.", { id: "sub-error", duration: Infinity, }); console.error('Page updates subscription error:', error); }, onStarted: () => toast.dismiss("sub-error"), autoReconnect: true, maxReconnectAttempts: 5, } ); // tRPC queries const initiativeQuery = trpc.getInitiative.useQuery({ id }); const phasesQuery = trpc.listPhases.useQuery( { initiativeId: id }, { enabled: !!initiativeQuery.data }, ); // Loading state if (initiativeQuery.isLoading) { return (
); } // Error state if (initiativeQuery.isError) { return (

{initiativeQuery.error.message.includes("not found") ? "Initiative not found" : `Failed to load initiative: ${initiativeQuery.error.message}`}

); } const initiative = initiativeQuery.data; if (!initiative) return null; const serializedInitiative = { id: initiative.id, name: initiative.name, status: initiative.status, }; const projects = (initiative as { projects?: Array<{ id: string; name: string; url: string }> }).projects; const phases = phasesQuery.data ?? []; return (
{/* Header */} navigate({ to: "/initiatives" })} /> {/* Tab bar */}
{/* Tab content */} {activeTab === "content" && } {activeTab === "execution" && ( )}
); }