"use client"; import { useCallback, useEffect, useMemo, useState } from "react"; import { useRouter } from "next/navigation"; import { createClient } from "@/lib/supabase/client"; import { formatMessage } from "@/lib/i18n/shared"; import { DEFAULT_COLORS, faviconUrl, logoUrl } from "@/lib/types"; import { useDict } from "@/components/i18n/LocaleProvider"; type AssetKind = "logo" | "favicon"; const ASSET_COLUMN: Record = { logo: "logo_path", favicon: "favicon_path", }; export default function DesignEditor() { const supabase = useMemo(() => createClient(), []); const router = useRouter(); const dict = useDict(); const colorLabels: Record = { primary: dict.designEditor.colorPrimary, primaryDark: dict.designEditor.colorPrimaryDark, accent: dict.designEditor.colorAccent, headerText: dict.designEditor.colorHeaderText, rowBg: dict.designEditor.colorRowBg, rowBgAlt: dict.designEditor.colorRowBgAlt, addBtn: dict.designEditor.colorAddBtn, deleteBtn: dict.designEditor.colorDeleteBtn, }; const [colors, setColors] = useState>(DEFAULT_COLORS); const [logoPath, setLogoPath] = useState(null); const [faviconPath, setFaviconPath] = useState(null); const [error, setError] = useState(null); const [info, setInfo] = useState(null); const [busy, setBusy] = useState(false); const load = useCallback(async () => { const { data, error } = await supabase .from("settings") .select("colors, logo_path, favicon_path") .eq("id", 1) .maybeSingle(); if (error) { setError(formatMessage(dict.designEditor.loadError, { message: error.message })); return; } setColors({ ...DEFAULT_COLORS, ...(data?.colors ?? {}) }); setLogoPath(data?.logo_path ?? null); setFaviconPath(data?.favicon_path ?? null); }, [supabase, dict]); useEffect(() => { load(); }, [load]); async function saveColors() { setBusy(true); setError(null); setInfo(null); const { error } = await supabase .from("settings") .update({ colors }) .eq("id", 1); if (error) setError(formatMessage(dict.designEditor.saveError, { message: error.message })); else { setInfo(dict.designEditor.colorsSaved); router.refresh(); } setBusy(false); } async function resetColors() { setColors(DEFAULT_COLORS); } async function uploadAsset(kind: AssetKind, e: React.ChangeEvent) { const file = e.target.files?.[0]; e.target.value = ""; if (!file) return; if (!/^image\//.test(file.type)) { setError(dict.designEditor.invalidImage); return; } setBusy(true); setError(null); setInfo(null); const oldPath = kind === "logo" ? logoPath : faviconPath; const ext = file.name.split(".").pop()?.toLowerCase() ?? "png"; const path = `${kind}-${Date.now()}.${ext}`; const { error: uploadError } = await supabase.storage .from("branding") .upload(path, file, { contentType: file.type }); if (uploadError) { setError(formatMessage(dict.designEditor.uploadError, { message: uploadError.message })); setBusy(false); return; } const { error: settingsError } = await supabase .from("settings") .update({ [ASSET_COLUMN[kind]]: path }) .eq("id", 1); if (settingsError) { setError(formatMessage(dict.designEditor.saveError, { message: settingsError.message })); setBusy(false); return; } if (oldPath) await supabase.storage.from("branding").remove([oldPath]); if (kind === "logo") setLogoPath(path); else setFaviconPath(path); setInfo(kind === "logo" ? dict.designEditor.logoUpdated : dict.designEditor.faviconUpdated); router.refresh(); setBusy(false); } async function removeAsset(kind: AssetKind) { const path = kind === "logo" ? logoPath : faviconPath; if (!path) return; if ( !window.confirm( kind === "logo" ? dict.designEditor.confirmRemoveLogo : dict.designEditor.confirmRemoveFavicon ) ) return; setBusy(true); setError(null); const { error } = await supabase .from("settings") .update({ [ASSET_COLUMN[kind]]: null }) .eq("id", 1); if (error) { setError(formatMessage(dict.designEditor.saveError, { message: error.message })); } else { await supabase.storage.from("branding").remove([path]); if (kind === "logo") setLogoPath(null); else setFaviconPath(null); setInfo(kind === "logo" ? dict.designEditor.logoRemoved : dict.designEditor.faviconRemoved); router.refresh(); } setBusy(false); } const currentLogo = logoUrl(logoPath); const currentFavicon = faviconUrl(faviconPath); return (

{dict.designEditor.title}

{error && (

{error}

)} {info &&

{info}

}

{dict.designEditor.logoTitle}

{dict.designEditor.logoHint}

{currentLogo ? ( // eslint-disable-next-line @next/next/no-img-element Logo ) : (

{dict.designEditor.logoNone}

)}
{currentLogo && ( )}

{dict.designEditor.faviconTitle}

{dict.designEditor.faviconHint}

{currentFavicon ? ( // eslint-disable-next-line @next/next/no-img-element Favicon ) : (

{dict.designEditor.faviconNone}

)}
{currentFavicon && ( )}

{dict.designEditor.colorsTitle}

{Object.entries(colorLabels).map(([key, label]) => ( ))}
); }