import { getTranslations } from "next-intl/server"; import { applyPreset, saveTheme } from "@/actions/admin-theme"; import { siteSettings } from "@/lib/services/site-settings"; import { FONTS, PRESETS } from "@/lib/theme-presets"; export const dynamic = "force-dynamic"; export const metadata = { title: "Theme" }; const COLOR_FIELDS: { key: string; label: string; def: string }[] = [ { key: "color_primary", label: "Primary", def: "#f59e0b" }, { key: "color_accent", label: "Accent", def: "#10b981" }, { key: "border_color", label: "Border (golden)", def: "#eeb425" }, { key: "color_background", label: "Background", def: "#f8fafc" }, { key: "color_surface", label: "Surface / cards", def: "#ffffff" }, { key: "color_dropdown", label: "Dropdown", def: "#ffffff" }, { key: "color_navbar", label: "Navbar", def: "#ffffff" }, { key: "color_navbar_text", label: "Navbar text", def: "#1e293b" }, { key: "color_text", label: "Text", def: "#0f172a" }, { key: "color_text_muted", label: "Muted text", def: "#64748b" }, { key: "color_success", label: "Success", def: "#16a34a" }, { key: "color_warning", label: "Warning", def: "#eab308" }, { key: "color_error", label: "Error", def: "#ef4444" }, { key: "color_info", label: "Info", def: "#0ea5e9" }, ]; const BUTTON_FIELDS: { key: string; label: string; def: string }[] = [ { key: "button_primary_color", label: "Primary button", def: "#f59e0b" }, { key: "button_text_color", label: "Button text", def: "#1e293b" }, { key: "button_secondary_color", label: "Secondary button", def: "#22c55e" }, { key: "button_secondary_text_color", label: "Secondary text", def: "#ffffff" }, { key: "button_danger_color", label: "Danger button", def: "#ef4444" }, { key: "button_danger_text_color", label: "Danger text", def: "#ffffff" }, { key: "button_outline_color", label: "Outline button", def: "#eeb425" }, { key: "button_outline_text_color", label: "Outline text", def: "#1a1a2e" }, { key: "link_color", label: "Link", def: "#eeb425" }, { key: "link_hover_color", label: "Link hover", def: "#cf9d15" }, ]; const GRADIENT_FIELDS: { key: string; label: string; def: string }[] = [ { key: "gradient_from", label: "Gradient from", def: "#f59e0b" }, { key: "gradient_to", label: "Gradient to", def: "#10b981" }, ]; const HEADINGS: { key: string; label: string; def: string }[] = [ { key: "size_heading_h1", label: "H1", def: "30" }, { key: "size_heading_h2", label: "H2", def: "22" }, { key: "size_heading_h3", label: "H3", def: "18" }, ]; const ALL_COLORS = [...COLOR_FIELDS, ...BUTTON_FIELDS, ...GRADIENT_FIELDS]; function toHex(value: string | null, def: string): string { return value && /^#[0-9a-fA-F]{6}$/.test(value) ? value : def; } function colorGrid(fields: { key: string; label: string; def: string }[], current: Record) { return (
{fields.map((f) => ( ))}
); } export default async function AdminTheme({ searchParams, }: { searchParams: Promise<{ saved?: string; preset?: string }>; }) { const t = await getTranslations("pages.admin.theme"); const sp = await searchParams; const current = Object.fromEntries( await Promise.all( ALL_COLORS.map(async (f) => [f.key, (await siteSettings.get(f.key, f.def)) ?? f.def] as const), ), ) as Record; const radius = (await siteSettings.get("border_radius", "12")) ?? "12"; const fontKey = (await siteSettings.get("font_family", "nunito")) ?? "nunito"; const headings = Object.fromEntries( await Promise.all( HEADINGS.map(async (h) => [h.key, (await siteSettings.get(h.key, h.def)) ?? h.def] as const), ), ) as Record; const customCss = (await siteSettings.get("custom_css", "")) ?? ""; return (
🎨

{t("title")}

{t.rich("subtitle", { code: (chunks) => {chunks} })}

{sp.saved ? (
{t("saved")}
) : null} {sp.preset ? (
{t("presetApplied", { preset: sp.preset })}
) : null}

{t("presetsTitle")}

{Object.entries(PRESETS).map(([name, palette]) => (
))}

{t("colours")}

{colorGrid(COLOR_FIELDS, current)}

{t("buttonsLinks")}

{colorGrid(BUTTON_FIELDS, current)}

Gradient mix

Control the card header and hero background gradients.

{colorGrid(GRADIENT_FIELDS, current)}

{t("typographyLayout")}

{HEADINGS.map((h) => ( ))}

{t("cssTitle")}

{t("cssSubtitle")}