/** * @file MemberPolicySettings — Settings → AI tools → Tests on this server: * the admin's server-wide switch that allows or blocks members' Claude from * running test runners and container commands on this box * (`GET/PUT /api/ai-tools/member-policy`, `server/lib/member-policy.js`, * enforced by `scripts/member-guard.js`). Renders nothing outside server mode * with per-user accounts (S3), or for anyone but an admin. * @author Nguyễn Ngọc Trí Vĩ */ import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { api, type MemberPolicyState } from "../../lib/api"; import { toast } from "../../lib/toast"; import { usePermissions } from "../AuthGate"; import { SettingsSection, SettingsSwitchRow } from "./SettingsRow"; export function MemberPolicySettings() { const { t } = useTranslation("settings"); const { isAdmin, local } = usePermissions(); const [state, setState] = useState(null); const [busy, setBusy] = useState(false); useEffect(() => { if (local || !isAdmin) return; api.aiTools .memberPolicy() .then(setState) .catch(() => setState(null)); }, [local, isAdmin]); if (local || !isAdmin || !state?.available) return null; return ( { setBusy(true); try { setState(await api.aiTools.setMemberPolicy(on)); } catch (err) { toast.error(err); } finally { setBusy(false); } }} /> ); }