'use client'; import { useEffect, useState } from 'react'; import { apiFetch } from '@/lib/api'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/input'; interface AdminOrg { id: string; slug: string; name: string; plan: string; monthlyCallQuota: number; callsThisPeriod: number; periodStartsAt: string; suspended: boolean; suspendedReason: string | null; createdAt: string; memberCount: number; serverCount: number; } export default function AdminOrgsPage() { const [orgs, setOrgs] = useState(null); const [search, setSearch] = useState(''); async function reload() { const r = await apiFetch<{ orgs: AdminOrg[] }>('/v1/admin/orgs'); setOrgs(r.orgs); } useEffect(() => { reload(); }, []); async function changePlan(o: AdminOrg) { const plan = prompt( `Change plan for "${o.name}". Current: ${o.plan}. Pick one of: hobby, pro, team, enterprise`, o.plan, ); if (!plan || !['hobby', 'pro', 'team', 'enterprise'].includes(plan)) return; await apiFetch(`/v1/admin/orgs/${o.id}`, { method: 'PATCH', body: JSON.stringify({ plan }), }); reload(); } async function setQuota(o: AdminOrg) { const next = prompt(`New monthly call quota for "${o.name}":`, String(o.monthlyCallQuota)); if (!next) return; const parsed = Number(next); if (!Number.isFinite(parsed) || parsed < 0) { alert('Invalid number'); return; } await apiFetch(`/v1/admin/orgs/${o.id}`, { method: 'PATCH', body: JSON.stringify({ monthlyCallQuota: parsed }), }); reload(); } async function toggleSuspend(o: AdminOrg) { if (o.suspended) { if (!confirm(`Lift suspension on "${o.name}"?`)) return; await apiFetch(`/v1/admin/orgs/${o.id}`, { method: 'PATCH', body: JSON.stringify({ suspended: false, suspendedReason: null }), }); } else { const reason = prompt( `Suspend "${o.name}"? This pauses ALL their MCP servers. Reason (visible in audit log):`, '', ); if (reason === null) return; await apiFetch(`/v1/admin/orgs/${o.id}`, { method: 'PATCH', body: JSON.stringify({ suspended: true, suspendedReason: reason || 'Suspended by admin' }), }); } reload(); } const filtered = orgs?.filter((o) => search ? o.name.toLowerCase().includes(search.toLowerCase()) || o.slug.toLowerCase().includes(search.toLowerCase()) : true, ); return (

Organizations

Plan management, quota overrides, suspension.

setSearch(e.target.value)} placeholder="Search by name or slug…" className="w-80" />
{!filtered && (

Loading…

)} {filtered && filtered.length === 0 && (

No matches.

)} {filtered && filtered.length > 0 && ( {filtered.map((o) => ( ))}
Name Plan Members Servers Calls / quota Status Actions
{o.name}
{o.slug}
{o.plan} {o.memberCount} {o.serverCount} {o.callsThisPeriod.toLocaleString()} / {o.monthlyCallQuota.toLocaleString()} {o.suspended ? ( suspended ) : ( active )}
)}
); }