import { useEffect, useState } from 'react'; import { useSelector } from 'react-redux'; import adminService, { type QuotaScope } from '../api/services/adminService'; import { Button } from '../components/ui/button'; import { Input } from '../components/ui/input'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '../components/ui/select'; import { selectToken } from '../preferences/preferenceSlice'; import { fmtNumber } from './AdminUI'; import { fmtUsd, formToPolicy, isEmptyForm, policyToForm, usagePercent, type Budget, type BudgetMode, type QuotaForm, type QuotaPolicy, } from './quotaUtils'; const MODES: { value: BudgetMode; label: string }[] = [ { value: 'inherit', label: 'Not set here' }, { value: 'limit', label: 'Limit' }, { value: 'unlimited', label: 'Unlimited' }, ]; export function UsageBar({ label, budget, kind, caption, }: { label: string; budget: Budget; kind: 'tokens' | 'cost'; caption?: string; }) { const fmt = (n: number) => (kind === 'cost' ? fmtUsd(n) : fmtNumber(n)); const percent = usagePercent(budget.used, budget.limit); const tone = percent >= 100 ? 'bg-red-500' : percent >= 80 ? 'bg-amber-500' : 'bg-[#7D54D1]'; return (
{label} {fmt(budget.used)} {budget.limit === null ? ' ยท no limit' : ` of ${fmt(budget.limit)}`}
{budget.limit !== null ? (
) : null} {caption ? (

{caption}

) : null}
); } function BudgetField({ label, hint, mode, value, step, onMode, onValue, }: { label: string; hint: string; mode: BudgetMode; value: string; step: string; onMode: (mode: BudgetMode) => void; onValue: (value: string) => void; }) { return (

{label}

{mode === 'limit' ? ( onValue(e.target.value)} className="flex-1" /> ) : null}
); } /** * Edits the ``all``-bucket policy of one subject. Saving a form with neither * budget set removes the policy, since a policy without an opinion is not stored. */ export default function QuotaEditor({ scope, subjectId, policy, inheritHint, onSaved, }: { scope: QuotaScope; subjectId: string | null; policy: QuotaPolicy | null; inheritHint: string; onSaved: () => void; }) { const token = useSelector(selectToken); const [form, setForm] = useState(() => policyToForm(policy)); const [busy, setBusy] = useState(false); const [error, setError] = useState(null); useEffect(() => { setForm(policyToForm(policy)); setError(null); }, [policy, scope, subjectId]); const patch = (fields: Partial) => setForm((prev) => ({ ...prev, ...fields })); const submit = async (request: () => Promise) => { setBusy(true); setError(null); try { const res = await request(); const json = await res.json().catch(() => ({})); if (res.ok && json.success !== false) onSaved(); else setError(json.message || 'Could not save the quota.'); } catch { setError('Could not save the quota.'); } finally { setBusy(false); } }; const remove = () => submit(() => adminService.deleteQuota(scope, subjectId, 'all', token)); const save = () => { if (isEmptyForm(form)) { if (policy) remove(); return; } const result = formToPolicy(form); if (!result.ok) { setError(result.error); return; } submit(() => adminService.setQuota(scope, subjectId, result.policy, token)); }; return (

{inheritHint}

patch({ tokenMode })} onValue={(tokenLimit) => patch({ tokenLimit })} /> patch({ costMode })} onValue={(costLimit) => patch({ costLimit })} />

Note

patch({ note: e.target.value })} className="mt-1" />
{error ? (

{error}

) : null}
{policy ? ( ) : null}
); }