From e048c68f40431f8c1b7b8c72b29cbc8bd96bea9d Mon Sep 17 00:00:00 2001 From: Jonathan Date: Mon, 13 Jul 2026 00:20:18 +0200 Subject: [PATCH] feat(web): Monitor settings tab --- web/src/app/settings/settings-form.tsx | 71 +++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 2 deletions(-) diff --git a/web/src/app/settings/settings-form.tsx b/web/src/app/settings/settings-form.tsx index 422ece2..e648207 100644 --- a/web/src/app/settings/settings-form.tsx +++ b/web/src/app/settings/settings-form.tsx @@ -12,8 +12,8 @@ type Loaded = { slskdApiKeySet: boolean; }; -type Tab = "Qobuz" | "Soulseek" | "Library" | "Discovery"; -const TABS: Tab[] = ["Qobuz", "Soulseek", "Library", "Discovery"]; +type Tab = "Qobuz" | "Soulseek" | "Library" | "Monitor" | "Discovery"; +const TABS: Tab[] = ["Qobuz", "Soulseek", "Library", "Monitor", "Discovery"]; const DISCOVERY_FIELDS: [string, string][] = [ ["discover.intervalHours", "Interval (hours)"], @@ -23,6 +23,13 @@ const DISCOVERY_FIELDS: [string, string][] = [ ["discover.minScore", "Min score"], ]; +const MONITOR_FIELDS: [string, string][] = [ + ["monitor.pollIntervalHours", "Poll interval (hours)"], + ["monitor.retryIntervalHours", "Retry interval (hours)"], + ["monitor.qualityCutoff", "Quality cutoff"], + ["monitor.upgradeWindowDays", "Upgrade window (days)"], +]; + export function SettingsForm() { const [tab, setTab] = useState("Qobuz"); const [qobuzEmail, setQobuzEmail] = useState(""); @@ -39,6 +46,8 @@ export function SettingsForm() { const [scanRequested, setScanRequested] = useState(false); const [discoverCfg, setDiscoverCfg] = useState>({}); const [discoverSaved, setDiscoverSaved] = useState(false); + const [monitorCfg, setMonitorCfg] = useState>({}); + const [monitorSaved, setMonitorSaved] = useState(false); useEffect(() => { fetch("/api/scan") @@ -83,6 +92,12 @@ export function SettingsForm() { .then((c: Record) => setDiscoverCfg(c)); }, []); + useEffect(() => { + fetch("/api/monitor/config") + .then((r) => r.json()) + .then((c: Record) => setMonitorCfg(c)); + }, []); + async function saveCredentials(e: React.FormEvent) { e.preventDefault(); await fetch("/api/config", { @@ -115,6 +130,21 @@ export function SettingsForm() { setTimeout(() => setDiscoverSaved(false), 1500); } + function setMon(key: string, value: string) { + setMonitorCfg((c) => ({ ...c, [key]: value })); + } + + async function saveMonitor(e: React.FormEvent) { + e.preventDefault(); + await fetch("/api/monitor/config", { + method: "PATCH", + headers: { "content-type": "application/json" }, + body: JSON.stringify(monitorCfg), + }); + setMonitorSaved(true); + setTimeout(() => setMonitorSaved(false), 1500); + } + const secretHint = (set: boolean) => (set ? ยท set : null); return ( @@ -205,6 +235,43 @@ export function SettingsForm() { ) : null} + {tab === "Monitor" ? ( +
+
+ +
+
+ +
+
+ {MONITOR_FIELDS.map(([key, label]) => ( + + ))} +
+
+ + {monitorSaved ? Saved : null} +
+
+ ) : null} + {tab === "Discovery" ? (