diff --git a/src/app/components/system-config/GeneralTab.tsx b/src/app/components/system-config/GeneralTab.tsx index 3951eb2..2ab9375 100644 --- a/src/app/components/system-config/GeneralTab.tsx +++ b/src/app/components/system-config/GeneralTab.tsx @@ -17,27 +17,36 @@ function BrandingUpload({ kind, label, currentSrc, + setDraft, }: { kind: "logo" | "favicon"; label: string; currentSrc: string; + setDraft: TabProps["setDraft"]; }) { const inputRef = useRef(null); const [preview, setPreview] = useState(null); const [busy, setBusy] = useState(false); + const fileField = kind === "logo" ? "logoFile" : "faviconFile"; const call = async (url: string, init: RequestInit, fallback: string) => { setBusy(true); try { const response = await fetch(url, init); const body = (await response.json().catch(() => null)) as - | { message?: string } + | { message?: string; file?: string } | null; if (!response.ok) throw new Error(body?.message || fallback); + // Update only this kind's file field in the draft; leave other unsaved + // edits intact. DELETE implies the file field is now null. + const nextFile = init.method === "DELETE" ? null : (body?.file ?? null); + setDraft((config) => ({ + ...config, + branding: { ...config.branding, [fileField]: nextFile }, + })); setPreview(null); if (inputRef.current) inputRef.current.value = ""; void globalMutate(PUBLIC_SYSTEM_CONFIG_KEY); - void globalMutate("/api/system/config"); } catch (error) { errorToast(`branding.${kind}`, error instanceof Error ? error.message : fallback); } finally { @@ -114,15 +123,16 @@ export function GeneralTab({ draft, setDraft }: TabProps) { } /> - + 0 ? `/api/system/branding/asset/favicon?v=${publicConfig.faviconVersion}` - : "/favicon.ico" + : "/icon.png" } + setDraft={setDraft} />

PNG, JPEG or SVG, up to 512KB. The favicon also accepts ICO. Changes