import { useEffect, useMemo, useState } from 'react'; import { Button, Dialog, DialogActions, DialogContent, DialogTitle, TextField, Tooltip, Typography, } from '@mui/material'; import BoltOutlinedIcon from '@mui/icons-material/BoltOutlined'; import { useSessionStore } from '@/store/sessionStore'; import { apiFetch } from '@/utils/api'; const DEFAULT_CAP_USD = 5; function money(value: number | null | undefined): string { if (value === null || value === undefined) return 'uncapped'; if (value >= 100) return `$${value.toFixed(0)}`; return `$${value.toFixed(2).replace(/\.00$/, '')}`; } export default function YoloControl() { const { sessions, activeSessionId, updateSessionYolo } = useSessionStore(); const activeSession = useMemo( () => sessions.find((s) => s.id === activeSessionId) || null, [sessions, activeSessionId], ); const [dialogOpen, setDialogOpen] = useState(false); const [capInput, setCapInput] = useState(String(DEFAULT_CAP_USD)); const [busy, setBusy] = useState(false); const [error, setError] = useState(null); const enabled = Boolean(activeSession?.autoApprovalEnabled); const disabled = !activeSessionId || activeSession?.expired || busy; const remaining = activeSession?.autoApprovalRemainingUsd ?? null; const cap = activeSession?.autoApprovalCostCapUsd ?? null; useEffect(() => { if (!activeSession) return; setCapInput(String(activeSession.autoApprovalCostCapUsd ?? DEFAULT_CAP_USD)); }, [activeSession?.id, activeSession?.autoApprovalCostCapUsd]); // eslint-disable-line react-hooks/exhaustive-deps async function patchPolicy(nextEnabled: boolean, nextCap?: number) { if (!activeSessionId) return null; setBusy(true); setError(null); try { const body: Record = { enabled: nextEnabled }; if (nextCap !== undefined) body.cost_cap_usd = nextCap; const response = await apiFetch(`/api/session/${activeSessionId}/yolo`, { method: 'PATCH', body: JSON.stringify(body), }); if (!response.ok) { throw new Error(await response.text()); } const data = await response.json(); updateSessionYolo(activeSessionId, data); return data; } catch { setError('Could not update YOLO settings.'); return null; } finally { setBusy(false); } } const handleToggle = async () => { if (disabled) return; if (enabled) { await patchPolicy(false); return; } const nextCap = cap ?? DEFAULT_CAP_USD; const updated = await patchPolicy(true, nextCap); if (updated) { setCapInput(String(updated.cost_cap_usd ?? nextCap)); setDialogOpen(true); } }; const handleSaveCap = async () => { const parsed = Number(capInput); if (!Number.isFinite(parsed) || parsed < 0) { setError('Enter a non-negative dollar amount.'); return; } const updated = await patchPolicy(true, parsed); if (updated) setDialogOpen(false); }; return ( <> setDialogOpen(false)} maxWidth="xs" fullWidth> YOLO Budget Auto-approval is active for this session. Scheduled HF jobs still require approval. setCapInput(e.target.value)} inputProps={{ min: 0, step: 0.5 }} error={Boolean(error)} helperText={error || `Estimated spend: ${money(activeSession?.autoApprovalEstimatedSpendUsd ?? 0)} of ${money(cap)}`} /> ); }