"use client"; import { useState } from "react"; /* The switches and the demo reset. Each control posts one action and reloads, so what the page * shows is always what the server holds — the row, with the environment's override on top. */ type Sw = { signupsDisabled: boolean; demoDisabled: boolean; plansLive: boolean; signupsByEnv: boolean; demoByEnv: boolean; plansByEnv: boolean; updatedAt: string | null }; export default function Controls({ sw, owner, demoAgoMin }: { sw: Sw; owner: boolean; demoAgoMin: number | null }) { const [err, setErr] = useState(""); const [busy, setBusy] = useState(""); async function act(action: string, extra: Record = {}) { setBusy(action + (extra.key || "")); setErr(""); try { const r = await fetch("/api/ops/controls", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ action, ...extra }) }); const j = await r.json().catch(() => ({})); if (!r.ok) { setErr(j.error || "That didn’t work."); return; } window.location.reload(); } catch { setErr("No connection — check the network and try again."); } finally { setBusy(""); } } const row = (label: string, state: string, locked: boolean, button: React.ReactNode) => (
{label}
{state}{locked ? " · locked by the environment on the box" : ""}
{button}
); return (
{err &&
{err}
} {row("Sign-ups", sw.signupsDisabled || sw.signupsByEnv ? "Closed — the create-account form is hidden and the endpoint refuses" : "Open", sw.signupsByEnv, )} {row("Demo", sw.demoDisabled || sw.demoByEnv ? "Out of service — /demo says so and the entry endpoint answers 404" : `In service${demoAgoMin === null ? "" : ` · rebuilt ${demoAgoMin} min ago`}`, sw.demoByEnv, )} {row("Plans", sw.plansLive || sw.plansByEnv ? "Live — new sign-ups start on Hosted Small and Settings shows the Plan tab" : "Off — every new facility is grandfathered: free, with everything, for good", sw.plansByEnv, )}
Reset the demo now
Drops and rebuilds the shared facility, as the twenty-minute clock does. Visitors mid-session see fresh data.
{!owner &&
Switches are an owner’s to flip.
}
); }