import { useState, useCallback } from "preact/hooks"; import { useT } from "../../../shared/i18n/context"; import { useQuotaSettings } from "../../../shared/hooks/use-quota-settings"; import { useSettings } from "../../../shared/hooks/use-settings"; export function QuotaSettings() { const t = useT(); const settings = useSettings(); const qs = useQuotaSettings(settings.apiKey); const [draftInterval, setDraftInterval] = useState(null); const [draftPrimary, setDraftPrimary] = useState(null); const [draftSecondary, setDraftSecondary] = useState(null); const [draftSkip, setDraftSkip] = useState(null); const [collapsed, setCollapsed] = useState(true); const currentInterval = qs.data?.refresh_interval_minutes ?? 5; const currentPrimary = qs.data?.warning_thresholds.primary ?? [80, 90]; const currentSecondary = qs.data?.warning_thresholds.secondary ?? [80, 90]; const currentSkip = qs.data?.skip_exhausted ?? true; const displayInterval = draftInterval ?? String(currentInterval); const displayPrimary = draftPrimary ?? currentPrimary.join(", "); const displaySecondary = draftSecondary ?? currentSecondary.join(", "); const displaySkip = draftSkip ?? currentSkip; const isDirty = draftInterval !== null || draftPrimary !== null || draftSecondary !== null || draftSkip !== null; const parseThresholds = (str: string): number[] | null => { if (!str.trim()) return []; const parts = str.split(",").map((s) => s.trim()).filter(Boolean); const nums = parts.map(Number); if (nums.some((n) => isNaN(n) || !Number.isInteger(n) || n < 1 || n > 100)) return null; return nums.sort((a, b) => a - b); }; const handleSave = useCallback(async () => { const patch: Record = {}; if (draftInterval !== null) { const val = parseInt(draftInterval, 10); if (isNaN(val) || val < 1) return; patch.refresh_interval_minutes = val; } if (draftPrimary !== null || draftSecondary !== null) { const thresholds: Record = {}; if (draftPrimary !== null) { const parsed = parseThresholds(draftPrimary); if (!parsed) return; thresholds.primary = parsed; } if (draftSecondary !== null) { const parsed = parseThresholds(draftSecondary); if (!parsed) return; thresholds.secondary = parsed; } patch.warning_thresholds = thresholds; } if (draftSkip !== null) { patch.skip_exhausted = draftSkip; } await qs.save(patch); setDraftInterval(null); setDraftPrimary(null); setDraftSecondary(null); setDraftSkip(null); }, [draftInterval, draftPrimary, draftSecondary, draftSkip, qs]); const inputCls = "w-full px-3 py-2 bg-white dark:bg-bg-dark border border-gray-200 dark:border-border-dark rounded-lg text-[0.78rem] font-mono text-slate-700 dark:text-text-main outline-none focus:ring-1 focus:ring-primary"; return (
{!collapsed && (
{/* Refresh interval */}

{t("quotaRefreshIntervalHint")}

setDraftInterval((e.target as HTMLInputElement).value)} /> {t("minutes")}
{/* Primary thresholds */}

{t("quotaThresholdsHint")}

setDraftPrimary((e.target as HTMLInputElement).value)} placeholder="80, 90" />
{/* Secondary thresholds */}
setDraftSecondary((e.target as HTMLInputElement).value)} placeholder="80, 90" />
{/* Skip exhausted */}
setDraftSkip((e.target as HTMLInputElement).checked)} class="w-4 h-4 rounded border-gray-300 dark:border-border-dark text-primary focus:ring-primary cursor-pointer" />
{/* Save button + status */}
{qs.saved && ( {t("quotaSaved")} )} {qs.error && ( {qs.error} )}
)}
); }