diff --git a/app/_admin/site-settings/actions.ts b/app/_admin/site-settings/actions.ts
index 8e7c3eb..43ad089 100644
--- a/app/_admin/site-settings/actions.ts
+++ b/app/_admin/site-settings/actions.ts
@@ -12,10 +12,16 @@ import {
SITE_SETTINGS_FAVICON_FIELD_KEY,
SITE_SETTINGS_LOGO_DARK_FIELD_KEY,
SITE_SETTINGS_LOGO_LIGHT_FIELD_KEY,
+ getSiteSettings,
updateSiteSettings,
} from "@/lib/app-config";
import { getAdminAppPath, toInternalAdminPath } from "@/lib/admin-routing";
-import { PAGE_TITLE_TOKEN, type SiteSettings } from "@/lib/site-settings";
+import {
+ PAGE_TITLE_TOKEN,
+ normalizeSiteDefaultLocale,
+ normalizeSitePrimaryColor,
+ type SiteSettings,
+} from "@/lib/site-settings";
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
import { replaceEntityMediaUsages } from "@/lib/media";
import { resolveMediaSelection } from "@/lib/media-service";
@@ -91,12 +97,17 @@ async function revalidateSiteSettingsPages() {
}
export async function saveSiteSettingsAction(formData: FormData) {
+ return saveSiteBrandSettingsAction(formData);
+}
+
+export async function saveSiteBrandSettingsAction(formData: FormData) {
await ensureAdmin();
const createdMediaAssetIds: string[] = [];
const uploadedPaths: string[] = [];
try {
+ const currentSettings = await getSiteSettings();
const siteLogoLightMedia = parseJsonObject(formData.get("siteLogoLightMedia"), "siteLogoLightMedia");
const siteLogoDarkMedia = parseJsonObject(formData.get("siteLogoDarkMedia"), "siteLogoDarkMedia");
const faviconMedia = parseJsonObject(formData.get("faviconMedia"), "faviconMedia");
@@ -106,41 +117,12 @@ export async function saveSiteSettingsAction(formData: FormData) {
);
const parsedSettings: SiteSettings = {
- locales: {
- ar: {
- siteName: String(formData.get("siteNameAr") ?? "").trim(),
- titleTemplate: String(formData.get("titleTemplateAr") ?? "").trim(),
- siteDescription: String(formData.get("siteDescriptionAr") ?? "").trim(),
- subhead: String(formData.get("subheadAr") ?? "").trim(),
- },
- en: {
- siteName: String(formData.get("siteNameEn") ?? "").trim(),
- titleTemplate: String(formData.get("titleTemplateEn") ?? "").trim(),
- siteDescription: String(formData.get("siteDescriptionEn") ?? "").trim(),
- subhead: String(formData.get("subheadEn") ?? "").trim(),
- },
- de: {
- siteName: String(formData.get("siteNameDe") ?? "").trim(),
- titleTemplate: String(formData.get("titleTemplateDe") ?? "").trim(),
- siteDescription: String(formData.get("siteDescriptionDe") ?? "").trim(),
- subhead: String(formData.get("subheadDe") ?? "").trim(),
- },
+ ...currentSettings,
+ brand: {
+ primaryColor: normalizeSitePrimaryColor(formData.get("primaryColor")),
},
};
- for (const locale of routing.locales) {
- if (!parsedSettings.locales[locale].siteName) {
- throw new Error(`Site Name fuer ${locale} ist erforderlich.`);
- }
-
- if (
- !parsedSettings.locales[locale].titleTemplate ||
- !parsedSettings.locales[locale].titleTemplate.includes(PAGE_TITLE_TOKEN)
- ) {
- throw new Error(`Title Template fuer ${locale} muss {pageTitle} enthalten.`);
- }
- }
-
const siteLogoLightSelection = siteLogoLightMedia
? await resolveMediaSelection({
media: mediaFieldInputSchema.parse(siteLogoLightMedia),
@@ -278,7 +260,7 @@ export async function saveSiteSettingsAction(formData: FormData) {
});
await revalidateSiteSettingsPages();
- redirect(withMessage(getAdminAppPath("/site-settings"), "success", "Einstellungen gespeichert."));
+ redirect(withMessage(getAdminAppPath("/site-settings/brand"), "success", "Einstellungen gespeichert."));
} catch (error) {
if (isRedirectError(error)) {
throw error;
@@ -291,6 +273,66 @@ export async function saveSiteSettingsAction(formData: FormData) {
? error.message
: "Einstellungen konnten nicht gespeichert werden.";
- redirect(withMessage(getAdminAppPath("/site-settings"), "error", message));
+ redirect(withMessage(getAdminAppPath("/site-settings/brand"), "error", message));
+ }
+}
+
+export async function saveSiteLocalizationSettingsAction(formData: FormData) {
+ await ensureAdmin();
+
+ try {
+ const currentSettings = await getSiteSettings();
+ const parsedSettings: SiteSettings = {
+ ...currentSettings,
+ defaultLocale: normalizeSiteDefaultLocale(formData.get("defaultLocale")),
+ locales: {
+ ar: {
+ siteName: String(formData.get("siteNameAr") ?? "").trim(),
+ titleTemplate: String(formData.get("titleTemplateAr") ?? "").trim(),
+ siteDescription: String(formData.get("siteDescriptionAr") ?? "").trim(),
+ subhead: String(formData.get("subheadAr") ?? "").trim(),
+ },
+ en: {
+ siteName: String(formData.get("siteNameEn") ?? "").trim(),
+ titleTemplate: String(formData.get("titleTemplateEn") ?? "").trim(),
+ siteDescription: String(formData.get("siteDescriptionEn") ?? "").trim(),
+ subhead: String(formData.get("subheadEn") ?? "").trim(),
+ },
+ de: {
+ siteName: String(formData.get("siteNameDe") ?? "").trim(),
+ titleTemplate: String(formData.get("titleTemplateDe") ?? "").trim(),
+ siteDescription: String(formData.get("siteDescriptionDe") ?? "").trim(),
+ subhead: String(formData.get("subheadDe") ?? "").trim(),
+ },
+ },
+ };
+
+ for (const locale of routing.locales) {
+ if (!parsedSettings.locales[locale].siteName) {
+ throw new Error(`Site Name fuer ${locale} ist erforderlich.`);
+ }
+
+ if (
+ !parsedSettings.locales[locale].titleTemplate ||
+ !parsedSettings.locales[locale].titleTemplate.includes(PAGE_TITLE_TOKEN)
+ ) {
+ throw new Error(`Title Template fuer ${locale} muss {pageTitle} enthalten.`);
+ }
+ }
+
+ await updateSiteSettings(parsedSettings);
+ await revalidateSiteSettingsPages();
+ redirect(withMessage(getAdminAppPath("/site-settings/localization"), "success", "Einstellungen gespeichert."));
+ } catch (error) {
+ if (isRedirectError(error)) {
+ throw error;
+ }
+
+ const message =
+ error instanceof Error
+ ? error.message
+ : "Einstellungen konnten nicht gespeichert werden.";
+
+ redirect(withMessage(getAdminAppPath("/site-settings/localization"), "error", message));
}
}
diff --git a/app/_admin/site-settings/brand/page.tsx b/app/_admin/site-settings/brand/page.tsx
new file mode 100644
index 0000000..c951fcf
--- /dev/null
+++ b/app/_admin/site-settings/brand/page.tsx
@@ -0,0 +1,73 @@
+import { MediaKind } from "@prisma/client";
+import { redirect } from "next/navigation";
+
+import { MotionFade } from "@/components/motion-fade";
+import { SiteSettingsForm } from "@/components/admin/site-settings-form";
+import { AdminDashboardShell } from "@/components/admin/admin-dashboard-shell";
+import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
+import { getAdminAppPath } from "@/lib/admin-routing";
+import {
+ getSiteSettings,
+ getSiteSettingsMediaBindings,
+} from "@/lib/app-config";
+import { getMediaOptions } from "@/lib/media";
+
+import { saveSiteBrandSettingsAction } from "../actions";
+
+export const dynamic = "force-dynamic";
+
+const copy = {
+ title: "Brand",
+ subtitle: "Primaerfarbe, Logos, Favicon und OG Assets verwalten.",
+ overview: "Uebersicht",
+ maintenance: "Wartungsmodus",
+ uiKit: "UI Kit",
+ media: "Media",
+ siteSettings: "Settings",
+ brandSettings: "Brand",
+ localizationSettings: "Localization",
+ smtp: "SMTP",
+ portfolio: "Portfolio",
+ logout: "Ausloggen",
+ backToSite: "Zur Website",
+};
+
+export default async function AdminSiteBrandSettingsPage() {
+ if (!(await isAdminAuthenticated())) {
+ redirect(getAdminAppPath("/"));
+ }
+
+ async function logoutAction() {
+ "use server";
+
+ await clearAdminSessionCookie();
+ redirect(getAdminAppPath("/"));
+ }
+
+ const [siteSettings, mediaBindings, mediaOptions] = await Promise.all([
+ getSiteSettings(),
+ getSiteSettingsMediaBindings(),
+ getMediaOptions({ kind: MediaKind.IMAGE }),
+ ]);
+
+ return (
+
+
+
+
+
+ );
+}
diff --git a/app/_admin/site-settings/localization/page.tsx b/app/_admin/site-settings/localization/page.tsx
new file mode 100644
index 0000000..ed7a144
--- /dev/null
+++ b/app/_admin/site-settings/localization/page.tsx
@@ -0,0 +1,67 @@
+import { redirect } from "next/navigation";
+
+import { MotionFade } from "@/components/motion-fade";
+import { SiteSettingsForm } from "@/components/admin/site-settings-form";
+import { AdminDashboardShell } from "@/components/admin/admin-dashboard-shell";
+import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
+import { getAdminAppPath } from "@/lib/admin-routing";
+import {
+ getDefaultSiteSettingsMediaBindings,
+ getSiteSettings,
+} from "@/lib/app-config";
+
+import { saveSiteLocalizationSettingsAction } from "../actions";
+
+export const dynamic = "force-dynamic";
+
+const copy = {
+ title: "Localization",
+ subtitle: "Seitennamen, Titelvorlagen und Standardsprache verwalten.",
+ overview: "Uebersicht",
+ maintenance: "Wartungsmodus",
+ uiKit: "UI Kit",
+ media: "Media",
+ siteSettings: "Settings",
+ brandSettings: "Brand",
+ localizationSettings: "Localization",
+ smtp: "SMTP",
+ portfolio: "Portfolio",
+ logout: "Ausloggen",
+ backToSite: "Zur Website",
+};
+
+export default async function AdminSiteLocalizationSettingsPage() {
+ if (!(await isAdminAuthenticated())) {
+ redirect(getAdminAppPath("/"));
+ }
+
+ async function logoutAction() {
+ "use server";
+
+ await clearAdminSessionCookie();
+ redirect(getAdminAppPath("/"));
+ }
+
+ const siteSettings = await getSiteSettings();
+
+ return (
+
+
+
+
+
+ );
+}
diff --git a/app/_admin/site-settings/page.tsx b/app/_admin/site-settings/page.tsx
index bd3a15b..ef8b144 100644
--- a/app/_admin/site-settings/page.tsx
+++ b/app/_admin/site-settings/page.tsx
@@ -1,71 +1,9 @@
-import { MediaKind } from "@prisma/client";
import { redirect } from "next/navigation";
-import { MotionFade } from "@/components/motion-fade";
-import { SiteSettingsForm } from "@/components/admin/site-settings-form";
-import { AdminDashboardShell } from "@/components/admin/admin-dashboard-shell";
-import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
import { getAdminAppPath } from "@/lib/admin-routing";
-import {
- getSiteSettings,
- getSiteSettingsMediaBindings,
-} from "@/lib/app-config";
-import { getMediaOptions } from "@/lib/media";
-
-import { saveSiteSettingsAction } from "./actions";
export const dynamic = "force-dynamic";
-const copy = {
- title: "Settings",
- subtitle: "Globale Titel, Copy und Brand Assets verwalten.",
- overview: "Uebersicht",
- maintenance: "Wartungsmodus",
- uiKit: "UI Kit",
- media: "Media",
- siteSettings: "Settings",
- smtp: "SMTP",
- portfolio: "Portfolio",
- logout: "Ausloggen",
- backToSite: "Zur Website",
-};
-
-export default async function AdminSiteSettingsPage() {
- if (!(await isAdminAuthenticated())) {
- redirect(getAdminAppPath("/"));
- }
-
- async function logoutAction() {
- "use server";
-
- await clearAdminSessionCookie();
- redirect(getAdminAppPath("/"));
- }
-
- const [siteSettings, mediaBindings, mediaOptions] = await Promise.all([
- getSiteSettings(),
- getSiteSettingsMediaBindings(),
- getMediaOptions({ kind: MediaKind.IMAGE }),
- ]);
-
- return (
-
-
-
-
-
-
-
- );
+export default function AdminSiteSettingsPage() {
+ redirect(getAdminAppPath("/site-settings/brand"));
}
diff --git a/app/admin-internal/site-settings/brand/page.tsx b/app/admin-internal/site-settings/brand/page.tsx
new file mode 100644
index 0000000..24e48bd
--- /dev/null
+++ b/app/admin-internal/site-settings/brand/page.tsx
@@ -0,0 +1 @@
+export { default } from "../../../_admin/site-settings/brand/page";
diff --git a/app/admin-internal/site-settings/localization/page.tsx b/app/admin-internal/site-settings/localization/page.tsx
new file mode 100644
index 0000000..357ac53
--- /dev/null
+++ b/app/admin-internal/site-settings/localization/page.tsx
@@ -0,0 +1 @@
+export { default } from "../../../_admin/site-settings/localization/page";
diff --git a/app/api/site/default-locale/route.ts b/app/api/site/default-locale/route.ts
new file mode 100644
index 0000000..ed7e4fe
--- /dev/null
+++ b/app/api/site/default-locale/route.ts
@@ -0,0 +1,20 @@
+import { NextResponse } from "next/server";
+
+import { getSiteSettings } from "@/lib/app-config";
+
+export const dynamic = "force-dynamic";
+
+export async function GET() {
+ const settings = await getSiteSettings();
+
+ return NextResponse.json(
+ {
+ defaultLocale: settings.defaultLocale,
+ },
+ {
+ headers: {
+ "Cache-Control": "no-store, max-age=0",
+ },
+ },
+ );
+}
diff --git a/app/layout.tsx b/app/layout.tsx
index f88ce41..dfdafeb 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -7,8 +7,10 @@ import { QueryToastBridge } from "@/components/admin/query-toast-bridge";
import { SoundProvider } from "@/components/sound-provider";
import { ThemeProvider } from "@/components/theme-provider";
import { Toaster } from "@/components/ui/toaster";
+import { getSiteSettings } from "@/lib/app-config";
import { buildAppMetadata } from "@/lib/metadata";
import { getDirection } from "@/lib/locale";
+import { buildSiteThemeStyleText } from "@/lib/site-theme";
import "./globals.css";
const museo = localFont({
@@ -82,10 +84,16 @@ export default async function RootLayout({
}>) {
noStore();
- const locale = await getLocale().catch(() => "de");
+ const [locale, siteSettings] = await Promise.all([
+ getLocale().catch(() => "de"),
+ getSiteSettings(),
+ ]);
return (
+
+
+
diff --git a/app/root/site-settings/brand/page.tsx b/app/root/site-settings/brand/page.tsx
new file mode 100644
index 0000000..24e48bd
--- /dev/null
+++ b/app/root/site-settings/brand/page.tsx
@@ -0,0 +1 @@
+export { default } from "../../../_admin/site-settings/brand/page";
diff --git a/app/root/site-settings/localization/page.tsx b/app/root/site-settings/localization/page.tsx
new file mode 100644
index 0000000..357ac53
--- /dev/null
+++ b/app/root/site-settings/localization/page.tsx
@@ -0,0 +1 @@
+export { default } from "../../../_admin/site-settings/localization/page";
diff --git a/components/admin/admin-dashboard-shell.tsx b/components/admin/admin-dashboard-shell.tsx
index c0ed0a8..8b08ab0 100644
--- a/components/admin/admin-dashboard-shell.tsx
+++ b/components/admin/admin-dashboard-shell.tsx
@@ -3,8 +3,10 @@ import {
FolderKanban,
Globe2,
ImageIcon,
+ Languages,
LayoutDashboard,
LogOut,
+ Palette,
PlusSquare,
ShieldAlert,
SwatchBook,
@@ -34,6 +36,8 @@ type AdminDashboardCopy = {
portfolio: string;
media: string;
siteSettings: string;
+ brandSettings?: string;
+ localizationSettings?: string;
marquee?: string;
smtp?: string;
contactProtection?: string;
@@ -46,6 +50,7 @@ type AdminDashboardShellProps = {
active: "overview" | "maintenance" | "ui-kit" | "portfolio" | "media" | "site-settings" | "smtp" | "marquee";
smtpChild?: "settings" | "contact-protection";
portfolioChild?: "overview" | "projects" | "new-project" | "categories";
+ siteSettingsChild?: "brand" | "localization";
logoutAction: () => Promise;
headerTitle: string;
headerDescription: string;
@@ -60,6 +65,7 @@ export async function AdminDashboardShell({
active,
smtpChild,
portfolioChild,
+ siteSettingsChild,
logoutAction,
headerTitle,
headerDescription,
@@ -72,7 +78,7 @@ export async function AdminDashboardShell({
getSiteSettingsMediaBindings(),
getMaintenanceMode(),
]);
- const sidebarItems = getAdminNavigation(copy, active, smtpChild, portfolioChild);
+ const sidebarItems = getAdminNavigation(copy, active, smtpChild, portfolioChild, siteSettingsChild);
const normalizedSidebarItems = sidebarItems.filter(
(item) =>
item.href !== getAdminAppPath("/maintenance") &&
@@ -91,7 +97,11 @@ export async function AdminDashboardShell({
: active === "ui-kit"
? SwatchBook
: active === "site-settings"
- ? Globe2
+ ? siteSettingsChild === "localization"
+ ? Languages
+ : siteSettingsChild === "brand"
+ ? Palette
+ : Globe2
: active === "marquee"
? Type
: active === "smtp"
diff --git a/components/admin/site-settings-form.tsx b/components/admin/site-settings-form.tsx
index bdbca8d..792a60c 100644
--- a/components/admin/site-settings-form.tsx
+++ b/components/admin/site-settings-form.tsx
@@ -3,7 +3,17 @@
/* eslint-disable @next/next/no-img-element */
import { MediaKind } from "@prisma/client";
-import { Check, FileText, ImageIcon, Search, Type, Upload } from "lucide-react";
+import {
+ Check,
+ FileText,
+ Globe2,
+ ImageIcon,
+ Languages,
+ Palette,
+ Search,
+ Type,
+ Upload,
+} from "lucide-react";
import { useEffect, useId, useMemo, useRef, useState } from "react";
import { AppCard } from "@/components/ui/app-card";
@@ -17,18 +27,27 @@ import {
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/components/ui/select";
import { Table, TableBody, TableCell, TableRow } from "@/components/ui/table";
import type { AppLocale } from "@/lib/locale";
import type { MediaOption } from "@/lib/media";
-import { cn } from "@/lib/utils";
+import { buildSiteThemeTokens } from "@/lib/site-theme";
import {
PAGE_TITLE_TOKEN,
SITE_NAME_TOKEN,
type SiteSettings,
type SiteSettingsMediaBindings,
} from "@/lib/site-settings";
+import { cn } from "@/lib/utils";
type SiteSettingsFormProps = {
+ mode: "brand" | "localization";
action: (formData: FormData) => Promise;
initialSettings: SiteSettings;
initialBindings: SiteSettingsMediaBindings;
@@ -49,24 +68,9 @@ const localeFields: Array<{
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",
- },
+ { 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(
@@ -105,18 +109,20 @@ function getMediaPreviewUrl(
return fallbackUrl ?? "";
}
-function buildPreviewTitle(
- titleTemplate: string,
- sampleTitle: string,
- siteName: string,
-) {
+function buildPreviewTitle(titleTemplate: string, sampleTitle: string, siteName: string) {
return titleTemplate.includes(PAGE_TITLE_TOKEN)
- ? titleTemplate
- .replace(PAGE_TITLE_TOKEN, sampleTitle)
- .replaceAll(SITE_NAME_TOKEN, siteName)
+ ? titleTemplate.replace(PAGE_TITLE_TOKEN, sampleTitle).replaceAll(SITE_NAME_TOKEN, siteName)
: `${sampleTitle} | ${siteName}`;
}
+function BadgeLike({ children }: { children: string }) {
+ return (
+
+ {children}
+
+ );
+}
+
function MediaLibraryModal({
open,
onOpenChange,
@@ -367,15 +373,140 @@ function SiteSettingsMediaRow({
);
}
-function BadgeLike({ children }: { children: string }) {
+function LocalizedFieldsSection({
+ settings,
+ setSettings,
+}: {
+ settings: SiteSettings;
+ setSettings: React.Dispatch>;
+}) {
return (
-
- {children}
-
+
+
+
Localized Titles And Copy
+
+ Nutze {PAGE_TITLE_TOKEN} und {SITE_NAME_TOKEN} fuer die globalen
+ Titelvorlagen pro Sprache.
+
+
+
+
+ {localeFields.map((locale) => (
+
+ {locale.label}
+
+
+
+
+
+
+ setSettings((current) => ({
+ ...current,
+ locales: {
+ ...current.locales,
+ [locale.key]: {
+ ...current.locales[locale.key],
+ siteName: event.target.value,
+ },
+ },
+ }))
+ }
+ placeholder="Site Name"
+ className="pl-8"
+ />
+
+
+
+
+
+
+
+
+ setSettings((current) => ({
+ ...current,
+ locales: {
+ ...current.locales,
+ [locale.key]: {
+ ...current.locales[locale.key],
+ titleTemplate: event.target.value,
+ },
+ },
+ }))
+ }
+ placeholder="Title Template"
+ className="pl-8"
+ />
+
+
+
+
+
+
+
+
+ setSettings((current) => ({
+ ...current,
+ locales: {
+ ...current.locales,
+ [locale.key]: {
+ ...current.locales[locale.key],
+ siteDescription: event.target.value,
+ },
+ },
+ }))
+ }
+ placeholder="Site Description"
+ className="pl-8"
+ />
+
+
+
+
+
+
+
+
+ setSettings((current) => ({
+ ...current,
+ locales: {
+ ...current.locales,
+ [locale.key]: {
+ ...current.locales[locale.key],
+ subhead: event.target.value,
+ },
+ },
+ }))
+ }
+ placeholder="Subhead"
+ className="pl-8"
+ />
+
+
+
+ ))}
+
+
);
}
export function SiteSettingsForm({
+ mode,
action,
initialSettings,
initialBindings,
@@ -383,353 +514,326 @@ export function SiteSettingsForm({
}: SiteSettingsFormProps) {
const [settings, setSettings] = useState(initialSettings);
const [siteLogoLight, setSiteLogoLight] = useState(
- createImageFieldState(
- initialBindings.siteLogoLight?.assetId,
- initialBindings.siteLogoLight?.url,
- "Site Logo Light",
- ),
+ createImageFieldState(initialBindings.siteLogoLight?.assetId, initialBindings.siteLogoLight?.url, "Site Logo Light"),
);
const [siteLogoDark, setSiteLogoDark] = useState(
- createImageFieldState(
- initialBindings.siteLogoDark?.assetId,
- initialBindings.siteLogoDark?.url,
- "Site Logo Dark",
- ),
+ createImageFieldState(initialBindings.siteLogoDark?.assetId, initialBindings.siteLogoDark?.url, "Site Logo Dark"),
);
const [favicon, setFavicon] = useState(
- createImageFieldState(
- initialBindings.favicon?.assetId,
- initialBindings.favicon?.url,
- "Favicon",
- ),
+ createImageFieldState(initialBindings.favicon?.assetId, initialBindings.favicon?.url, "Favicon"),
);
const [defaultOgImage, setDefaultOgImage] = useState(
- createImageFieldState(
- initialBindings.defaultOgImage?.assetId,
- initialBindings.defaultOgImage?.url,
- "Default OG Image",
- ),
+ createImageFieldState(initialBindings.defaultOgImage?.assetId, initialBindings.defaultOgImage?.url, "Default OG Image"),
);
- const siteLogoLightPreviewUrl = getMediaPreviewUrl(
- siteLogoLight,
- mediaOptions,
- initialBindings.siteLogoLight?.url,
- );
- const siteLogoDarkPreviewUrl = getMediaPreviewUrl(
- siteLogoDark,
- mediaOptions,
- initialBindings.siteLogoDark?.url,
- );
+ const themeTokens = useMemo(() => buildSiteThemeTokens(settings.brand.primaryColor), [settings.brand.primaryColor]);
+ const siteLogoLightPreviewUrl = getMediaPreviewUrl(siteLogoLight, mediaOptions, initialBindings.siteLogoLight?.url);
+ const siteLogoDarkPreviewUrl = getMediaPreviewUrl(siteLogoDark, mediaOptions, initialBindings.siteLogoDark?.url);
const faviconPreviewUrl = getMediaPreviewUrl(favicon, mediaOptions, initialBindings.favicon?.url);
- const defaultOgImagePreviewUrl = getMediaPreviewUrl(
- defaultOgImage,
- mediaOptions,
- initialBindings.defaultOgImage?.url,
- );
+ const defaultOgImagePreviewUrl = getMediaPreviewUrl(defaultOgImage, mediaOptions, initialBindings.defaultOgImage?.url);
+ const routePreview = settings.defaultLocale === "de" ? "/about" : `/${settings.defaultLocale}/about`;
return (