import { useCallback, useEffect, useState } from "react"; import type { FreeLabelGraphData, FreeLabelSummary } from "../lib/api.js"; import { fetchFreeLabelGraph, fetchFreeLabels } from "../lib/api.js"; import { formatCount } from "../lib/format.js"; import { navigate } from "../lib/router.js"; import { AppLink } from "./AppLink.js"; import { FreeLabelGraph } from "./ui/FreeLabelGraph.js"; const GRAPH_TOP = 300; type State = | { status: "loading" } | { status: "error"; message: string } | { status: "ready"; graph: FreeLabelGraphData; labels: readonly FreeLabelSummary[] }; /** Approved free-label co-occurrence view. */ export function GraphPage(): React.JSX.Element { const [state, setState] = useState({ status: "loading" }); useEffect(() => { let cancelled = false; void Promise.all([fetchFreeLabelGraph(GRAPH_TOP), fetchFreeLabels()]).then( ([graph, labels]) => { if (!cancelled) setState({ status: "ready", graph, labels }); }, (error: unknown) => { if (!cancelled) setState({ status: "error", message: error instanceof Error ? error.message : "failed to load", }); }, ); return (): void => { cancelled = true; }; }, []); const openFreeLabel = useCallback((name: string): void => { navigate(`/graph/${encodeURIComponent(name)}`); }, []); if (state.status === "loading") return

Loading…

; if (state.status === "error") return

{state.message}

; if (state.labels.length === 0) return

No approved free labels yet.

; return (

Free-label graph

Approved labels linked by unit co-occurrence.

{state.graph.links.length.toLocaleString()} co-occurrence edges

All approved free labels

    {[...state.labels] .sort((a, b) => b.post_count - a.post_count || a.name.localeCompare(b.name)) .map((label) => (
  • {label.name} {formatCount(label.post_count)}
  • ))}
); }