import { useMemo, useState } from "react"; import { Link, useSearchParams } from "react-router-dom"; import { useQuery } from "@tanstack/react-query"; import { Plus, Search, HardDrive } from "lucide-react"; import { Button, Input } from "@/components/ui"; import { StacksTable } from "@/components/stacks/StacksTable"; import { RestoreButton } from "@/components/stacks/BackupRestore"; import { AgentStacksSection } from "@/components/stacks/AgentStacksSection"; import { stacksApi } from "@/api/stacks"; import { systemApi } from "@/api/system"; import { agentsApi } from "@/api/agents"; import { useAuthStore } from "@/store/auth"; import { useStackActions } from "@/hooks/useStackActions"; type SortKey = "name" | "status" | "updated"; type StatusFilter = "all" | "running" | "stopped" | "attention"; const STATUS_FILTERS: Record = { running: "running", stopped: "stopped", attention: "attention", }; export function Stacks() { const isAdmin = useAuthStore((s) => s.user?.role === "admin"); const { busyId, start, stop, restart, updateImages } = useStackActions(); // The dashboard explore bar deep-links here with ?q= / ?filter=. const [params] = useSearchParams(); const [q, setQ] = useState(params.get("q") ?? ""); const [statusFilter, setStatusFilter] = useState( STATUS_FILTERS[params.get("filter") ?? ""] ?? "all" ); const [sort, setSort] = useState("name"); const { data, isLoading } = useQuery({ queryKey: ["stacks"], queryFn: stacksApi.list, refetchInterval: 5000, }); const stats = useQuery({ queryKey: ["stack-stats"], queryFn: stacksApi.stats, refetchInterval: 5000, }); const updates = useQuery({ queryKey: ["stack-updates"], queryFn: stacksApi.updates, refetchInterval: 60000, }); const info = useQuery({ queryKey: ["system"], queryFn: systemApi.info, refetchInterval: 5000, }); const agents = useQuery({ queryKey: ["agents"], queryFn: () => agentsApi.list(), refetchInterval: 15000, }); const hasAgents = (agents.data?.length ?? 0) > 0; const filtered = useMemo(() => { let list = (data ?? []).filter( (s) => s.name.toLowerCase().includes(q.toLowerCase()) || s.id.toLowerCase().includes(q.toLowerCase()) ); if (statusFilter !== "all") { list = list.filter((s) => statusFilter === "attention" ? s.status === "error" || s.status === "partial" : s.status === statusFilter ); } list = [...list].sort((a, b) => { if (sort === "name") return a.name.localeCompare(b.name); if (sort === "status") return a.status.localeCompare(b.status); return b.updated_at.localeCompare(a.updated_at); }); return list; }, [data, q, statusFilter, sort]); return (
setQ(e.target.value)} />
{isAdmin && } {isAdmin && ( )}
{hasAgents && (

This host

)}
{agents.data?.map((agent) => ( ))}
); }