frontend: hide radio-bot pool controls from non-admins (server-26#213 item 4)

GET /tokens and PUT /tokens/{id}/prefer became platform-admin only in #216
(server-26#204 item 6). Three frontend surfaces still offered them to
operators:

- systems page "Preferred Bot Token" panel: now rendered for admins only.
  No non-admin listing endpoint was added: the write behind the picker is
  admin-only too, so a list would only feed a control that 403s on save.
  The panel also no longer turns a fetch error into "No tokens in pool."
- /tokens page: admin-only (operators are sent to /network).
- /network hub: the Bot Tokens card is shown to admins only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Logan Cusano
2026-10-04 19:57:14 -04:00
co-authored by Claude Opus 5.5
parent dc1df72c86
commit e574bd924a
3 changed files with 32 additions and 14 deletions
+7 -5
View File
@@ -15,7 +15,9 @@ interface TokenRecord {
}
export default function TokensPage() {
const { isAdmin, isOperator, loading: authLoading } = useAuth();
// Platform admin only: every /tokens route is require_admin_token since
// server-26#204 item 6, so an operator here only ever saw a failed fetch.
const { isAdmin, loading: authLoading } = useAuth();
const router = useRouter();
const [tokens, setTokens] = useState<TokenRecord[]>([]);
const [loading, setLoading] = useState(true);
@@ -26,8 +28,8 @@ export default function TokensPage() {
const [error, setError] = useState<string | null>(null);
useEffect(() => {
if (!authLoading && !isAdmin && !isOperator) router.replace("/");
}, [authLoading, isAdmin, isOperator, router]);
if (!authLoading && !isAdmin) router.replace("/network");
}, [authLoading, isAdmin, router]);
const refresh = useCallback(async () => {
try {
@@ -38,7 +40,7 @@ export default function TokensPage() {
}
}, []);
useEffect(() => { refresh(); }, [refresh]);
useEffect(() => { if (isAdmin) refresh(); }, [isAdmin, refresh]);
async function handleAdd(e: React.FormEvent) {
e.preventDefault();
@@ -67,7 +69,7 @@ export default function TokensPage() {
}
}
if (authLoading || (!isAdmin && !isOperator)) return null;
if (authLoading || !isAdmin) return null;
return (
<div className="space-y-6">