Spaces:
Runtime error
Runtime error
File size: 3,580 Bytes
cd8bd0a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | "use client";
import { useEffect, useState } from "react";
import { useTranslations } from "next-intl";
import { Button, Card } from "@/shared/components";
export default function UsageTokenBufferTab() {
const t = useTranslations("settings");
const tc = useTranslations("common");
const [usageTokenBuffer, setUsageTokenBuffer] = useState<number | null>(null);
const [bufferInput, setBufferInput] = useState("");
const [loading, setLoading] = useState(true);
const [saving, setSaving] = useState(false);
useEffect(() => {
let cancelled = false;
const loadSettings = async () => {
setLoading(true);
try {
const res = await fetch("/api/settings", { cache: "no-store" });
if (!res.ok) return;
const data = await res.json();
const buffer = typeof data.usageTokenBuffer === "number" ? data.usageTokenBuffer : 2000;
if (!cancelled) {
setUsageTokenBuffer(buffer);
setBufferInput(String(buffer));
}
} catch {
// Keep the input disabled if settings cannot be loaded.
} finally {
if (!cancelled) setLoading(false);
}
};
void loadSettings();
return () => {
cancelled = true;
};
}, []);
const updateUsageTokenBuffer = async () => {
const nextValue = Number.parseInt(bufferInput, 10);
if (!Number.isFinite(nextValue) || nextValue < 0 || nextValue > 50000) return;
setSaving(true);
try {
const res = await fetch("/api/settings", {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ usageTokenBuffer: nextValue }),
});
if (res.ok) {
setUsageTokenBuffer(nextValue);
}
} catch (err) {
console.error("Failed to update usageTokenBuffer:", err);
} finally {
setSaving(false);
}
};
const parsedInput = Number.parseInt(bufferInput, 10);
const isDirty = usageTokenBuffer !== null && parsedInput !== usageTokenBuffer;
return (
<Card>
<div className="flex items-center gap-3 mb-4">
<div className="p-2 rounded-lg bg-sky-500/10 text-sky-500">
<span className="material-symbols-outlined text-[20px]" aria-hidden="true">
pin
</span>
</div>
<div>
<h3 className="text-lg font-semibold">{t("storageUsageTokenBuffer")}</h3>
<p className="text-sm text-text-muted">{t("storageUsageTokenBufferDesc")}</p>
</div>
</div>
<div className="flex flex-wrap items-center gap-3">
<input
type="number"
min={0}
max={50000}
value={bufferInput}
onChange={(e) => setBufferInput(e.target.value)}
onKeyDown={(e) => {
if (e.key === "Enter") void updateUsageTokenBuffer();
}}
className="h-10 w-36 rounded-lg border border-border bg-surface px-3 text-sm text-text-main focus:outline-none focus:border-primary"
disabled={loading}
/>
<Button
size="sm"
variant="primary"
onClick={updateUsageTokenBuffer}
disabled={saving || loading || !isDirty}
>
{saving ? tc("saving") : tc("save")}
</Button>
{usageTokenBuffer !== null && isDirty && (
<span className="text-xs text-text-muted">
{t("storageUsageTokenBufferCurrent", { value: usageTokenBuffer })}
</span>
)}
</div>
<p className="mt-3 text-xs text-text-muted">{t("storageUsageTokenBufferHint")}</p>
</Card>
);
}
|