"use client";
// Settings › Discord — the admin page from design-canvas/discord-system-mockup.html
// ("DRB admin" view), server-26#213 item 2. Desired state lives here; nodes
// reconcile and report back. Platform admin only for now: every route behind
// it (GET /discord/admin/overview, /tokens, PUT /discord/guilds/{id}/link) is
// require_admin_token. The mockup's "Alert routes" tab, incident-forum /
// roles / plan columns and per-channel talkgroup chips have no backend yet and
// are left out rather than shown as dead controls.
import { useCallback, useEffect, useState } from "react";
import { useRouter } from "next/navigation";
import { useAuth } from "@/components/AuthProvider";
import { c2api } from "@/lib/c2api";
import { type DiscordAdminOverview, type RadioBot, parseC2Error, streamHealth } from "@/lib/discord";
import { ErrorBanner } from "@/components/ui/EmptyState";
import { SkeletonCard } from "@/components/ui/Skeleton";
import { StreamsPanel } from "@/components/discord/StreamsPanel";
import { ServersPanel } from "@/components/discord/ServersPanel";
import { RadioBotsPanel } from "@/components/discord/RadioBotsPanel";
import { Panel } from "@/components/discord/ui";
type Tab = "streams" | "guilds" | "bots" | "how";
const TABS: { key: Tab; label: string }[] = [
{ key: "streams", label: "Radio channels" },
{ key: "guilds", label: "Servers" },
{ key: "bots", label: "Radio bots" },
{ key: "how", label: "How it works" },
];
const POLL_MS = 10_000;
function Kpi({ k, v, s }: { k: string; v: string; s: string }) {
return (
{k}
{v}
{s}
);
}
function HowItWorks() {
const steps = [
["1 · Desired", "/join in Discord or this page writes a discord_streams doc."],
["2 · Lease", "C2 picks an online node on that system and a radio bot already in that server, in one transaction."],
["3 · Reconcile", "A retained MQTT message tells the node its full channel list (token ids, never token strings)."],
["4 · Voice", "The node logs in as the leased bot and plays its PulseAudio capture — never Icecast."],
["5 · Actual", "The node reports each channel's real state; this page and the Discord reply show it."],
];
return (