"use client"; import { useCallback, useEffect, useMemo, useState } from "react"; import { useRouter } from "next/navigation"; import { createClient } from "@/lib/supabase/client"; import { COLOR_LABELS, DEFAULT_COLORS, faviconUrl, logoUrl } from "@/lib/types"; 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 [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(`Laden fehlgeschlagen: ${error.message}`); return; } setColors({ ...DEFAULT_COLORS, ...(data?.colors ?? {}) }); setLogoPath(data?.logo_path ?? null); setFaviconPath(data?.favicon_path ?? null); }, [supabase]); 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(`Speichern fehlgeschlagen: ${error.message}`); else { setInfo("Farben gespeichert."); 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("Bitte eine Bilddatei auswählen (PNG, JPG, SVG, WebP, ICO)."); 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(`Upload fehlgeschlagen: ${uploadError.message}`); setBusy(false); return; } const { error: settingsError } = await supabase .from("settings") .update({ [ASSET_COLUMN[kind]]: path }) .eq("id", 1); if (settingsError) { setError(`Speichern fehlgeschlagen: ${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" ? "Logo aktualisiert." : "Favicon aktualisiert."); router.refresh(); setBusy(false); } async function removeAsset(kind: AssetKind) { const path = kind === "logo" ? logoPath : faviconPath; if (!path) return; if ( !window.confirm( `${kind === "logo" ? "Logo" : "Favicon"} wirklich entfernen?` ) ) return; setBusy(true); setError(null); const { error } = await supabase .from("settings") .update({ [ASSET_COLUMN[kind]]: null }) .eq("id", 1); if (error) { setError(`Speichern fehlgeschlagen: ${error.message}`); } else { await supabase.storage.from("branding").remove([path]); if (kind === "logo") setLogoPath(null); else setFaviconPath(null); setInfo(kind === "logo" ? "Logo entfernt." : "Favicon entfernt."); router.refresh(); } setBusy(false); } const currentLogo = logoUrl(logoPath); const currentFavicon = faviconUrl(faviconPath); return (

Design

{error && (

{error}

)} {info &&

{info}

}

Logo

Das Logo wird zentriert über der Speisekarte angezeigt.

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

Kein Logo hinterlegt.

)}
{currentLogo && ( )}

Favicon

Wird als Icon im Browser-Tab angezeigt. Ohne eigenes Favicon gilt das Standard-Icon der Anwendung.

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

Kein eigenes Favicon hinterlegt.

)}
{currentFavicon && ( )}

Farbgebung

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