"use client"; import Image from "next/image"; import { useEffect, useRef, useState, useTransition } from "react"; import { ImageIcon, Monitor, Moon, Palette, Sun, Trash2, Upload, } from "lucide-react"; import { Button, Card, CardContent, Input, Label, RadioGroup, RadioGroupItem, } from "poyraz-ui/atoms"; import { toast } from "poyraz-ui/molecules"; import { useTranslations } from "@/components/i18n/i18n-provider"; import { applyColorMode } from "@/components/theme/color-mode-sync"; import { isColorMode, type ColorMode } from "@/lib/color-mode"; import { removeAppearanceAssetAction, saveAppearanceSettingsAction, saveColorModeAction, } from "./actions"; type BrandingAsset = "darkLogo" | "favicon" | "lightLogo"; type AssetState = Record; type AppearanceSettingsFormProps = { initial: { colorMode: ColorMode; primaryColor: string; urls: AssetState; custom: Record; }; }; const themeOptions = [ { value: "light", icon: Sun }, { value: "dark", icon: Moon }, { value: "system", icon: Monitor }, ] as const; export function AppearanceSettingsForm({ initial }: AppearanceSettingsFormProps) { const t = useTranslations(); const [colorMode, setColorMode] = useState(initial.colorMode); const [savingTheme, startThemeTransition] = useTransition(); const [savingBrand, startBrandTransition] = useTransition(); const [primaryColor, setPrimaryColor] = useState(initial.primaryColor); const [pendingUrls, setPendingUrls] = useState({ lightLogo: "", darkLogo: "", favicon: "", }); const objectUrls = useRef>({}); useEffect(() => { const urls = objectUrls.current; return () => Object.values(urls).forEach((url) => url && URL.revokeObjectURL(url)); }, []); function changeColorMode(value: string) { if (!isColorMode(value) || value === colorMode || savingTheme) return; const previous = colorMode; setColorMode(value); applyColorMode(value); startThemeTransition(async () => { const result = await saveColorModeAction(value); if (result.errorKey) { setColorMode(previous); applyColorMode(previous); toast.error(t(result.errorKey)); return; } toast.success(t("settings.appearance.messages.themeSaved")); }); } function selectAsset(asset: BrandingAsset, file?: File) { const previous = objectUrls.current[asset]; if (previous) URL.revokeObjectURL(previous); const url = file ? URL.createObjectURL(file) : ""; objectUrls.current[asset] = url || undefined; setPendingUrls((current) => ({ ...current, [asset]: url })); } function saveBranding(formData: FormData) { startBrandTransition(async () => { const result = await saveAppearanceSettingsAction(formData); if (result.errorKey) { toast.error(t(result.errorKey)); return; } toast.success(t("settings.appearance.messages.brandSaved")); window.location.reload(); }); } function removeAsset(asset: BrandingAsset) { startBrandTransition(async () => { const result = await removeAppearanceAssetAction(asset); if (result.errorKey) { toast.error(t(result.errorKey)); return; } toast.success(t("settings.appearance.messages.assetRemoved")); window.location.reload(); }); } return (

{t("settings.appearance.theme.title")}

{t("settings.appearance.theme.description")}

{themeOptions.map((option) => { const Icon = option.icon; const selected = colorMode === option.value; return ( ); })}

{t("settings.appearance.brand.title")}

{t("settings.appearance.brand.description")}

setPrimaryColor(event.target.value.toUpperCase())} aria-label={t("settings.appearance.color.picker")} className="h-11 w-16 shrink-0 cursor-pointer p-1" /> setPrimaryColor(event.target.value.toUpperCase())} pattern="^#[0-9A-Fa-f]{6}$" maxLength={7} required className="font-mono uppercase" />

{t("settings.appearance.color.help")}

); } function AssetField({ asset, title, description, accept, currentUrl, pendingUrl, custom, tone, disabled, onSelect, onRemove, removeLabel, previewAlt, }: { asset: BrandingAsset; title: string; description?: string; accept: string; currentUrl: string; pendingUrl: string; custom: boolean; tone: "dark" | "light" | "neutral"; disabled: boolean; onSelect: (asset: BrandingAsset, file?: File) => void; onRemove: (asset: BrandingAsset) => void; removeLabel: string; previewAlt: string; }) { const previewUrl = pendingUrl || (custom ? currentUrl : ""); const toneClass = tone === "dark" ? "bg-neutral-950" : tone === "light" ? "bg-white" : "bg-muted/40"; return (
{description ?

{description}

: null}
onSelect(asset, event.target.files?.[0])} className="cursor-pointer" /> {custom ? ( ) : null}
{previewUrl ? ( {previewAlt} ) : (
); }