import { AlertTriangle, CheckCircle2, GitFork, Layers, Link2, Package } from "lucide-react" import { useGraph } from "@/services/api/hooks" import type { GraphNode } from "@/types" // A read-only diagnostic of how programs/deployments relate — repos (provenance), // `requires` edges (dependency), and the derived predicates functional?/fresh?. // Everything is computed server-side from git + config; nothing is stored. export function GraphPage() { const { data, isLoading, error } = useGraph() if (isLoading) { return
Loading…
} if (error || !data) { return
Failed to load graph.
} const monorepos = data.repos.filter((r) => r.programs.length > 1) const staleRepos = data.repos.filter((r) => r.fresh === false) const depEdges = data.edges.filter((e) => e.kind === "deployment") const unhealthy = data.nodes.filter((n) => !n.functional) const depended = data.nodes .filter((n) => n.depended_on_by > 0) .sort((a, b) => b.depended_on_by - a.depended_on_by) return (

Relationship Graph

Derived, never stored — repos from git, requires edges, and the{" "} functional? / fresh? predicates, computed on the fly.

{monorepos.length === 0 && (

No monorepos — every program has its own repo.

)} {monorepos.map((r) => (
{r.key} → {r.programs.join(", ")}
))} {staleRepos.length > 0 && (

{staleRepos.length} repo(s) not fresh: {staleRepos.map((r) => r.key).join(", ")}

)}
{depEdges.length === 0 ? (

None declared yet — front-end/back-end deps have no encoded edge.

) : ( depEdges.map((e, i) => (
{e.src} requires {e.dst} {e.bind && → ${e.bind}}
)) )}
{unhealthy.length === 0 ? (

All deployments functional.

) : ( unhealthy.map((n) => (
{n.name}{" "} unmet: {n.unmet.join(", ")}
)) )}
{depended.length > 0 && ( {depended.map((n: GraphNode) => (
{n.name} ← {n.depended_on_by} dependent(s)
))}
)}
) } function Stat({ label, value, sub, tone }: { label: string; value: number; sub?: string; tone?: "ok" | "warn" }) { const color = tone === "warn" ? "text-amber-400" : tone === "ok" ? "text-green-400" : "" return (
{value}
{label}
{sub &&
{sub}
}
) } function Card({ title, icon: Icon, note, children, }: { title: string icon: typeof Layers note?: string children: React.ReactNode }) { return (

{title}

{note &&

{note}

}
{children}
) } function FreshBadge({ fresh, behind, dirty }: { fresh: boolean | null; behind: number | null; dirty: boolean }) { if (fresh === null) return null if (fresh) return fresh const bits = [behind ? `${behind} behind` : null, dirty ? "dirty" : null].filter(Boolean) return {bits.join(" · ") || "stale"} }