"use client"; /* eslint-disable @next/next/no-img-element */ import { MediaKind } from "@prisma/client"; import { ImageIcon, Search, Type } from "lucide-react"; import { useState } from "react"; import { MediaFieldPicker, type MediaFieldState } from "@/components/root/media-field-picker"; import { AppCard } from "@/components/ui/app-card"; import { CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { PAGE_TITLE_TOKEN, SITE_NAME_TOKEN, type SiteSettings, type SiteSettingsMediaBindings, } from "@/lib/site-settings"; import type { AppLocale } from "@/lib/locale"; import type { MediaOption } from "@/lib/media"; type SiteSettingsFormProps = { action: (formData: FormData) => Promise; initialSettings: SiteSettings; initialBindings: SiteSettingsMediaBindings; mediaOptions: MediaOption[]; }; const localeFields: Array<{ key: AppLocale; label: string; suffix: "Ar" | "En" | "De"; sampleTitle: string; }> = [ { key: "ar", label: "Arabic", suffix: "Ar", sampleTitle: "Home", }, { key: "en", label: "English", suffix: "En", sampleTitle: "About", }, { key: "de", label: "Deutsch", suffix: "De", sampleTitle: "Portfolio", }, ]; function createImageFieldState( assetId: string | null | undefined, url: string | null | undefined, label: string, ): MediaFieldState { if (assetId) { return { mode: "library", assetId, url: "", label, kind: MediaKind.IMAGE, isCleared: false, }; } if (url) { return { mode: "external", assetId: "", url, label, kind: MediaKind.IMAGE, isCleared: false, }; } return { mode: "upload", assetId: "", url: "", label, kind: MediaKind.IMAGE, isCleared: false, }; } function getMediaPreviewUrl( value: MediaFieldState, options: MediaOption[], fallbackUrl: string | null | undefined, ) { if (value.isCleared) { return ""; } if (value.mode === "external") { return value.url || fallbackUrl || ""; } if (value.mode === "library") { return options.find((option) => option.id === value.assetId)?.url ?? fallbackUrl ?? ""; } return fallbackUrl ?? ""; } export function SiteSettingsForm({ action, initialSettings, initialBindings, mediaOptions, }: SiteSettingsFormProps) { const [settings, setSettings] = useState(initialSettings); const [favicon, setFavicon] = useState( createImageFieldState( initialBindings.favicon?.assetId, initialBindings.favicon?.url, "Favicon", ), ); const [defaultOgImage, setDefaultOgImage] = useState( createImageFieldState( initialBindings.defaultOgImage?.assetId, initialBindings.defaultOgImage?.url, "Default OG Image", ), ); const faviconPreviewUrl = getMediaPreviewUrl(favicon, mediaOptions, initialBindings.favicon?.url); const defaultOgImagePreviewUrl = getMediaPreviewUrl( defaultOgImage, mediaOptions, initialBindings.defaultOgImage?.url, ); return (
SEO Basics Diese Einstellungen bilden die globale Basis fuer Seitentitel, Description und Vorschau in Suchmaschinen.

Nutze {" "} {PAGE_TITLE_TOKEN} {" "} fuer den aktuellen Seitentitel. und {" "} {SITE_NAME_TOKEN} {" "} fuer den Namen der aktuellen Sprache.

Beispiel: {" "} {PAGE_TITLE_TOKEN} | {SITE_NAME_TOKEN}

Localized Titles And Copy Diese Werte gelten als globale Standards pro Sprache und werden verwendet, wenn eine Seite keinen eigenen Fallback hat. {localeFields.map((locale) => (

{locale.label}

Globale Standardwerte fuer diese Sprache.

setSettings((current) => ({ ...current, locales: { ...current.locales, [locale.key]: { ...current.locales[locale.key], siteName: event.target.value, }, }, })) } />
setSettings((current) => ({ ...current, locales: { ...current.locales, [locale.key]: { ...current.locales[locale.key], titleTemplate: event.target.value, }, }, })) } placeholder="{pageTitle} | {siteName}" />
setSettings((current) => ({ ...current, locales: { ...current.locales, [locale.key]: { ...current.locales[locale.key], siteDescription: event.target.value, }, }, })) } />
setSettings((current) => ({ ...current, locales: { ...current.locales, [locale.key]: { ...current.locales[locale.key], subhead: event.target.value, }, }, })) } />
))}
Preview Images Favicon erscheint im Browser. Das Default OG Bild wird fuer Social Sharing genutzt, wenn eine Seite kein eigenes Bild liefert.
Preview Suchmaschine und Social Sharing Vorschau.
{localeFields.map((locale) => { const localeSettings = settings.locales[locale.key]; const previewTitle = localeSettings.titleTemplate.includes(PAGE_TITLE_TOKEN) ? localeSettings.titleTemplate .replace(PAGE_TITLE_TOKEN, locale.sampleTitle) .replaceAll(SITE_NAME_TOKEN, localeSettings.siteName) : `${locale.sampleTitle} | ${localeSettings.siteName}`; return (

{locale.label}

Example page title: {locale.sampleTitle}

{previewTitle}

{localeSettings.siteDescription || "No description"}

{localeSettings.subhead || "No subhead"}

); })}
Search Preview
{faviconPreviewUrl ? ( Favicon ) : (
)}

{settings.locales.de.titleTemplate.includes(PAGE_TITLE_TOKEN) ? settings.locales.de.titleTemplate .replace(PAGE_TITLE_TOKEN, "About") .replaceAll(SITE_NAME_TOKEN, settings.locales.de.siteName) : `About | ${settings.locales.de.siteName}`}

{settings.locales.de.siteDescription || "No description"}

{settings.locales.de.subhead || "No subhead"}

Social Preview
{defaultOgImagePreviewUrl ? ( Default OG ) : (
No OG image selected
)}

{settings.locales.en.titleTemplate.includes(PAGE_TITLE_TOKEN) ? settings.locales.en.titleTemplate .replace(PAGE_TITLE_TOKEN, "Portfolio") .replaceAll(SITE_NAME_TOKEN, settings.locales.en.siteName) : `Portfolio | ${settings.locales.en.siteName}`}

{settings.locales.en.siteDescription || "No description"}

); }