// Copyright 2026 SZL Holdings // SPDX-License-Identifier: Apache-2.0 // // App.tsx — the a11oy operator console SPA. Five working routes, each backed by // a real a11oy `serve` HTTP endpoint via A11oyClient. Self-contained: the only // dependencies are react, react-dom, and wouter. It does not import the legacy // 471-import surface (web/src/App.tsx); that surface is tracked separately as // engineering debt and is left in place per Operating Principle #10 (annotate, // do not silently delete). // // The a11oy base URL is read from VITE_A11OY_BASE_URL (default same origin). // // Authored for SZL Holdings. Signed-off per repository DCO. import { useEffect, useState } from "react"; import { Link, Route, Router, Switch, useRoute } from "wouter"; import { A11oyClient, CONSOLE_ROUTES, type OperationalReceipt } from "./a11oyClient.ts"; // The SPA is deployed at /console/ (Vite base=/console/). Wouter must strip // that prefix from browser pathnames before matching routes. const ROUTER_BASE = "/console"; const BASE = (import.meta.env?.VITE_A11OY_BASE_URL as string | undefined) ?? ""; const client = new A11oyClient(BASE || window.location.origin); function useAsync(fn: () => Promise, deps: unknown[] = []): { data: T | null; error: string | null; loading: boolean } { const [data, setData] = useState(null); const [error, setError] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { let live = true; setLoading(true); fn() .then((d) => { if (live) { setData(d); setError(null); } }) .catch((e) => { if (live) setError(String(e)); }) .finally(() => { if (live) setLoading(false); }); return () => { live = false; }; // eslint-disable-next-line react-hooks/exhaustive-deps }, deps); return { data, error, loading }; } function HealthRoute() { const health = useAsync(() => client.health(), []); const ready = useAsync(() => client.readiness(), []); return (

Health

{health.loading ?

loading…

: health.error ?

{health.error}

: (
{JSON.stringify(health.data, null, 2)}
)}

Readiness

{ready.loading ?

loading…

: ready.error ?

{ready.error}

: (
{JSON.stringify(ready.data, null, 2)}
)}
); } function LedgerRoute() { const page = useAsync(() => client.ledger(20), []); return (

Proof Ledger

{page.loading ?

loading…

: page.error ?

{page.error}

: ( <>

{page.data?.count} of {page.data?.total} receipts

    {page.data?.receipts.map((r: OperationalReceipt, i) => (
  • {String(r.receipt_id ?? r.merkle_root ?? `#${i}`)}
  • ))}
)}
); } function ReceiptRoute() { const [, params] = useRoute("/receipt/:hash"); const hash = params?.hash ?? ""; const out = useAsync(() => client.receipt(hash), [hash]); return (

Receipt {hash}

{out.loading ?

loading…

: out.error ?

{out.error}

: (
{JSON.stringify(out.data?.receipt, null, 2)}
)}
); } function VerifyRoute() { const [text, setText] = useState("[]"); const [result, setResult] = useState(""); const run = async () => { try { const ledger = JSON.parse(text); const r = await client.verify(ledger); setResult(JSON.stringify(r, null, 2)); } catch (e) { setResult(String(e)); } }; return (

Verify

Paste a receipt ledger array; POSTs to /v1/verify.