"use client"; import { createFileRoute, useNavigate } from "@tanstack/react-router"; import { useTRPC } from "@/integrations/trpc/react"; import { useQuery } from "@tanstack/react-query"; import { useCallback } from "react"; import Header from "@/components/Header"; import { Button } from "@bullstudio/ui/components/button"; import { Skeleton } from "@bullstudio/ui/components/skeleton"; import { Badge } from "@bullstudio/ui/components/badge"; import { ArrowLeft, RefreshCw, AlertTriangle, Workflow, GitBranch, CheckCircle, XCircle, } from "lucide-react"; import { z } from "zod"; import { FlowGraph } from "@/components/flows/FlowGraph"; import type { FlowNode } from "@bullstudio/connect-types"; const searchSchema = z.object({ queueName: z.string(), }); export const Route = createFileRoute("/flows/$flowId")({ component: FlowDetailPage, validateSearch: searchSchema, }); function checkHasActiveJobs(node: FlowNode): boolean { const activeStates = ["active", "waiting", "delayed", "waiting-children"]; if (activeStates.includes(node.status)) return true; if (!node.children) return false; return node.children.some(checkHasActiveJobs); } function FlowDetailPage() { const { flowId } = Route.useParams(); const { queueName } = Route.useSearch(); const navigate = useNavigate(); const trpc = useTRPC(); const { data: flowTree, isLoading, refetch, isFetching, } = useQuery( trpc.flows.get.queryOptions( { queueName, flowId }, { refetchInterval(query) { const data = query.state.data; if (!data) return false; return checkHasActiveJobs(data.root) ? 2000 : false; }, } ) ); const goBack = useCallback(() => { navigate({ to: "/flows" }); }, [navigate]); const handleNodeClick = useCallback( (jobId: string, jobQueueName: string) => { navigate({ to: "/jobs/$jobId", params: { jobId }, search: { queueName: jobQueueName }, }); }, [navigate] ); if (isLoading) { return (
The flow may have been removed or the ID is incorrect.