import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query" import { createFileRoute, Link } from "@tanstack/react-router" import { AlertCircle, Workflow } from "lucide-react" import { useState } from "react" import { type FlowSummary, FlowsService } from "@/client" import { DEFAULT_RANGE } from "@/components/Common/RangePicker" import { BrainView } from "@/components/Flow/BrainView" import { flowKeys, flowsQueryOptions } from "@/components/Flow/queries" import { HealthActivity } from "@/components/Health/HealthActivity" import { HealthOverview } from "@/components/Health/HealthOverview" import { Badge } from "@/components/ui/badge" import { Card } from "@/components/ui/card" import { Skeleton } from "@/components/ui/skeleton" import { Switch } from "@/components/ui/switch" import useCustomToast from "@/hooks/useCustomToast" export const Route = createFileRoute("/_layout/")({ component: Dashboard, head: () => ({ meta: [ { title: "Dashboard - Fluksio", }, ], }), }) /** Another tab can stop a flow, and the engine can fail one on its own. */ const REFRESH_INTERVAL = 10_000 function FlowRow({ flow }: { flow: FlowSummary }) { const queryClient = useQueryClient() const { showErrorToast } = useCustomToast() const enabled = flow.enabled ?? true const toggle = useMutation({ mutationFn: (next: boolean) => next ? FlowsService.startFlow({ name: flow.name }) : FlowsService.stopFlow({ name: flow.name }), onSuccess: () => { queryClient.invalidateQueries({ queryKey: flowKeys.all }) queryClient.invalidateQueries({ queryKey: flowKeys.detail(flow.name) }) }, onError: () => showErrorToast("The flow could not be started or stopped."), }) return (

{flow.title || flow.name}

{flow.node_count === 1 ? "1 node" : `${flow.node_count} nodes`} {flow.has_draft ? " · unpublished changes" : ""}

{(flow.error_count ?? 0) > 0 ? ( {flow.error_count} ) : null} {enabled ? (flow.paused ? "Paused" : "Running") : "Stopped"} toggle.mutate(next)} aria-label={`Run ${flow.title || flow.name}`} data-testid="flow-enabled-switch" />
) } /** * The one overview: what the engine is wired up as, what is running, and how * it has been doing. The brain and the health screens compose in here rather * than living at routes of their own. */ function Dashboard() { // The health block's window: one choice, read by the tiles, the flow table, // the charts and the lists under them. const [range, setRange] = useState(DEFAULT_RANGE) const { data, isPending } = useQuery({ ...flowsQueryOptions(), refetchInterval: REFRESH_INTERVAL, }) const flows = data?.data ?? [] return ( // `[&>*]:min-w-0`: a grid item's automatic minimum is its content, so one // long name or wide table widens the whole column and the page with it. // Every section here is free to shrink instead. See DESIGN-GUIDELINES.md // → Responsive.
{isPending ? (
) : flows.length === 0 ? (

Flows you build show up here, with what they are doing.

Go to flows
) : ( flows.map((flow) => ) )}
) }