import { Link } from "@tanstack/react-router"; import { motion } from "framer-motion"; import { Activity, Boxes, ChevronRight, CircleDot, LayoutDashboard } from "lucide-react"; import { useState } from "react"; import { useQueueStatus, useWorkspaces } from "@/api/queries"; import type { components } from "@/api/schema.d.ts"; import { ErrorAlert } from "@/components/shared/ErrorAlert"; import { PageLoader } from "@/components/shared/LoadingSpinner"; import { Body, Muted, PageTitle, SectionHeading } from "@/components/ui/typography"; import { COLOR } from "@/lib/constants"; import { formatCount } from "@/lib/utils"; type QueueStatus = components["schemas"]["QueueStatus"]; // ─── Per-workspace queue row ───────────────────────────────────────────────── function WorkspaceQueueRow({ workspaceId }: { workspaceId: string }) { const { data, isLoading } = useQueueStatus(workspaceId); const pending = data?.pending_work_units ?? 0; const active = data?.in_progress_work_units ?? 0; const done = data?.completed_work_units ?? 0; const total = data?.total_work_units ?? 0; const isActive = active > 0 || pending > 0; return ( {workspaceId} {isLoading ? ( ) : (
{isActive ? ( ) : ( )} {isActive ? `${formatCount(pending + active)} pending` : "Idle"}
)} {( [ { val: total, color: "var(--text-2)" }, { val: done, color: COLOR.success }, { val: active, color: COLOR.warning }, { val: pending, color: "var(--text-3)" }, ] as Array<{ val: number; color: string }> ).map(({ val, color }, i) => ( {isLoading ? "—" : formatCount(val)} ))} ); } // ─── Aggregate banner ───────────────────────────────────────────────────────── // Each workspace row already called useQueueStatus — TanStack Query deduplicates // the fetches so calling the same hooks here just reads from cache. function GlobalQueueBanner({ workspaces }: { workspaces: Array<{ id: string }> }) { const statuses = workspaces.map((ws) => { const { data } = useQueueStatus(ws.id); return data as QueueStatus | undefined; }); const totalPending = statuses.reduce((s, d) => s + (d?.pending_work_units ?? 0), 0); const totalActive = statuses.reduce((s, d) => s + (d?.in_progress_work_units ?? 0), 0); const totalDone = statuses.reduce((s, d) => s + (d?.completed_work_units ?? 0), 0); const allLoaded = statuses.every((d) => d !== undefined); return (
{( [ { label: "Workspaces", value: workspaces.length, color: "var(--text-1)", always: true }, { label: "Total done", value: totalDone, color: COLOR.success, always: false }, { label: "Active", value: totalActive, color: COLOR.warning, always: false }, { label: "Pending", value: totalPending, color: totalPending > 0 ? COLOR.warning : "var(--text-3)", always: false, }, ] as Array<{ label: string; value: number; color: string; always: boolean }> ).map(({ label, value, color, always }) => (
{allLoaded || always ? formatCount(value) : "—"}
{label}
))}
); } // ─── Main dashboard ─────────────────────────────────────────────────────────── export function Dashboard() { const [page] = useState(1); const { data, isLoading, error } = useWorkspaces(page, 50); const workspaces = (data as { items?: Array<{ id: string; created_at?: string }> } | undefined)?.items ?? []; const total = (data as { total?: number } | undefined)?.total ?? 0; return (
Dashboard {total > 0 && ( {total} workspace{total !== 1 ? "s" : ""} )}
Overview of your Honcho instance
{isLoading && } {!isLoading && workspaces.length > 0 && (
{/* Aggregate stat row */} {/* Per-workspace queue table */}
Queue Status all workspaces · updates every 10s
{["Workspace", "Status", "Total", "Done", "Active", "Pending"].map((h) => ( ))} {workspaces.map((ws) => ( ))}
{h}
{total > workspaces.length && (

Showing {workspaces.length} of {total} workspaces.{" "} View all →

)}
)} {!isLoading && workspaces.length === 0 && (
No workspaces found.
)}
); }