From 1ae7a64b4bf099730a3f3efeda8e2ddb2bbe3024 Mon Sep 17 00:00:00 2001 From: Lukas May Date: Fri, 6 Mar 2026 21:30:43 +0100 Subject: [PATCH] refactor: replace InboxPage with redirect to /hq Converts /inbox from a 270-line interactive page to a minimal TanStack Router redirect route. Bookmarked or externally linked /inbox URLs now redirect cleanly to /hq instead of 404-ing. InboxList and InboxDetailPanel components are preserved for reuse in the HQ page (Phase 569ZNKArI1OYRolaOZLhB). Co-Authored-By: Claude Sonnet 4.6 --- apps/web/src/routes/inbox.tsx | 270 +--------------------------------- 1 file changed, 4 insertions(+), 266 deletions(-) diff --git a/apps/web/src/routes/inbox.tsx b/apps/web/src/routes/inbox.tsx index 50e6399..56bb42f 100644 --- a/apps/web/src/routes/inbox.tsx +++ b/apps/web/src/routes/inbox.tsx @@ -1,269 +1,7 @@ -import { useState } from "react"; -import { createFileRoute } from "@tanstack/react-router"; -import { motion } from "motion/react"; -import { AlertCircle, MessageSquare } from "lucide-react"; -import { Button } from "@/components/ui/button"; -import { Card } from "@/components/ui/card"; -import { Skeleton } from "@/components/Skeleton"; -import { toast } from "sonner"; -import { trpc } from "@/lib/trpc"; -import { InboxList } from "@/components/InboxList"; -import { InboxDetailPanel } from "@/components/InboxDetailPanel"; -import { useLiveUpdates } from "@/hooks"; +import { createFileRoute, redirect } from "@tanstack/react-router"; export const Route = createFileRoute("/inbox")({ - component: InboxPage, + beforeLoad: () => { + throw redirect({ to: "/hq" }); + }, }); - -function InboxPage() { - const [selectedAgentId, setSelectedAgentId] = useState(null); - - // Single SSE stream for live updates - useLiveUpdates([ - { prefix: 'agent:', invalidate: ['listWaitingAgents', 'listMessages'] }, - ]); - - const utils = trpc.useUtils(); - - // Data fetching - const agentsQuery = trpc.listWaitingAgents.useQuery(); - const messagesQuery = trpc.listMessages.useQuery({}); - const questionsQuery = trpc.getAgentQuestions.useQuery( - { id: selectedAgentId! }, - { enabled: !!selectedAgentId } - ); - - // Mutations - const resumeAgentMutation = trpc.resumeAgent.useMutation({ - onSuccess: () => { - setSelectedAgentId(null); - toast.success("Answer submitted"); - }, - onError: () => { - toast.error("Failed to submit answer"); - }, - }); - - const dismissQuestionsMutation = trpc.stopAgent.useMutation({ - onSuccess: () => { - setSelectedAgentId(null); - toast.success("Questions dismissed"); - }, - onError: () => { - toast.error("Failed to dismiss questions"); - }, - }); - - const respondToMessageMutation = trpc.respondToMessage.useMutation({ - onSuccess: () => { - setSelectedAgentId(null); - toast.success("Response sent"); - }, - onError: () => { - toast.error("Failed to send response"); - }, - }); - - // Find selected agent info - const agents = agentsQuery.data ?? []; - const messages = messagesQuery.data ?? []; - const selectedAgent = selectedAgentId - ? agents.find((a) => a.id === selectedAgentId) ?? null - : null; - - // Find the latest message for the selected agent - const selectedMessage = selectedAgentId - ? messages - .filter((m) => m.senderId === selectedAgentId) - .sort( - (a, b) => - new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime() - )[0] ?? null - : null; - - const pendingQuestions = questionsQuery.data ?? null; - - // Handlers - function handleRefresh() { - void utils.listWaitingAgents.invalidate(); - void utils.listMessages.invalidate(); - } - - function handleSubmitAnswers(answers: Record) { - if (!selectedAgentId) return; - resumeAgentMutation.mutate({ id: selectedAgentId, answers }); - } - - function handleDismissQuestions() { - if (!selectedAgentId) return; - dismissQuestionsMutation.mutate({ id: selectedAgentId }); - } - - function handleDismiss() { - if (!selectedMessage) return; - respondToMessageMutation.mutate({ - id: selectedMessage.id, - response: "Acknowledged", - }); - } - - // Loading state - if (agentsQuery.isLoading && messagesQuery.isLoading) { - return ( -
- {/* Skeleton header */} -
-
- - -
- -
- {/* Skeleton message rows */} -
- {Array.from({ length: 4 }).map((_, i) => ( - -
-
-
- - -
- -
- -
-
- ))} -
-
- ); - } - - // Error state - if (agentsQuery.isError || messagesQuery.isError) { - const errorMessage = - agentsQuery.error?.message ?? messagesQuery.error?.message ?? "Unknown error"; - return ( -
- -

- Failed to load inbox: {errorMessage} -

- -
- ); - } - - // Serialize agents for InboxList (convert Date to string for wire format) - const serializedAgents = agents.map((a) => ({ - id: a.id, - name: a.name, - status: a.status, - taskId: a.taskId, - updatedAt: String(a.updatedAt), - })); - - // Serialize messages for InboxList - const serializedMessages = messages.map((m) => ({ - id: m.id, - senderId: m.senderId, - content: m.content, - requiresResponse: m.requiresResponse, - status: m.status, - createdAt: String(m.createdAt), - })); - - return ( - -
- {/* Left: Inbox List -- hidden on mobile when agent selected */} - - - - - {/* Right: Detail Panel */} - {selectedAgent && ( - ({ - id: q.id, - question: q.question, - options: q.options, - multiSelect: q.multiSelect, - })) - : null - } - isLoadingQuestions={questionsQuery.isLoading} - questionsError={ - questionsQuery.isError ? questionsQuery.error.message : null - } - onBack={() => setSelectedAgentId(null)} - onSubmitAnswers={handleSubmitAnswers} - onDismissQuestions={handleDismissQuestions} - onDismissMessage={handleDismiss} - isSubmitting={resumeAgentMutation.isPending} - isDismissingQuestions={dismissQuestionsMutation.isPending} - isDismissingMessage={respondToMessageMutation.isPending} - submitError={ - resumeAgentMutation.isError - ? resumeAgentMutation.error.message - : null - } - dismissMessageError={ - respondToMessageMutation.isError - ? respondToMessageMutation.error.message - : null - } - /> - )} - - {/* Empty detail panel placeholder */} - {!selectedAgent && ( -
- -
-

No message selected

-

Select an agent from the inbox to view details

-
-
- )} -
-
- ); -}