Extend the theme editor: typography, button/link colours, custom CSS, more presets
Grew /admin/theme from colours-only to a full theme editor, all applied live via website_settings + ThemeVars: - Typography: body font (10 web-safe + Google options; Google fonts load via an injected <link>) and H1/H2/H3 sizes (globals.css now reads --size-heading-* vars). - Buttons & links: secondary/danger button colours + link/link-hover. - Custom CSS: a raw textarea injected after the theme variables (staff- trusted), for anything the controls don't cover. - Presets: 6 → 13 (added Galaxy, Royal, Cyberpunk, Neon, Coffee, Arctic, Christmas). ThemeVars now injects all the new vars + the font link. Verified live (prod, amx_test): saved font=mono / H1=44px / custom CSS → the public home reflected --font-family "Courier New", --size-heading-h1 44px and the injected rule; reverted the test settings. tsc 0, vitest 49/49, next build 0.
This commit is contained in:
1 parent
0cd4d06ff6
commit
5a4b6f27e9
5 files changed
+322
-52
No files matched your search
@@ -6,10 +6,19 @@ import { requireStaff } from "@/lib/admin/guard";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { siteSettings } from "@/lib/services/site-settings";
|
||||
import { logStaffActivity } from "@/lib/services/staff-activity";
|
||||
import { PRESETS, THEME_COLOR_KEYS } from "@/lib/theme-presets";
|
||||
import { FONTS, PRESETS, THEME_COLOR_KEYS } from "@/lib/theme-presets";
|
||||
|
||||
// Only hex/keyword colour values are accepted (matches ThemeVars' sanitiser).
|
||||
const COLOR_RE = /^[#a-zA-Z0-9(),.\s%-]+$/;
|
||||
// Extra colour settings beyond the preset palette (buttons + links).
|
||||
const EXTRA_COLOR_KEYS = [
|
||||
"button_secondary_color",
|
||||
"button_danger_color",
|
||||
"link_color",
|
||||
"link_hover_color",
|
||||
];
|
||||
const HEADING_KEYS = ["size_heading_h1", "size_heading_h2", "size_heading_h3"];
|
||||
const CUSTOM_CSS_MAX = 20000;
|
||||
|
||||
async function writeSetting(key: string, value: string): Promise<void> {
|
||||
await prisma.websiteSetting.upsert({
|
||||
@@ -23,18 +32,32 @@ export async function saveTheme(formData: FormData): Promise<void> {
|
||||
const staff = await requireStaff();
|
||||
|
||||
try {
|
||||
for (const key of THEME_COLOR_KEYS) {
|
||||
for (const key of [...THEME_COLOR_KEYS, ...EXTRA_COLOR_KEYS]) {
|
||||
const raw = String(formData.get(key) ?? "").trim();
|
||||
if (raw && COLOR_RE.test(raw)) await writeSetting(key, raw);
|
||||
}
|
||||
const radius = String(formData.get("border_radius") ?? "").trim();
|
||||
if (/^\d{1,3}$/.test(radius)) await writeSetting("border_radius", radius);
|
||||
|
||||
// Typography
|
||||
const font = String(formData.get("font_family") ?? "").trim();
|
||||
if (font in FONTS) await writeSetting("font_family", font);
|
||||
for (const key of HEADING_KEYS) {
|
||||
const v = String(formData.get(key) ?? "").trim();
|
||||
if (/^\d{1,3}$/.test(v)) await writeSetting(key, v);
|
||||
}
|
||||
|
||||
// Raw custom CSS (staff-trusted; length-capped, ThemeVars injects it as-is).
|
||||
if (formData.has("custom_css")) {
|
||||
const cssRaw = String(formData.get("custom_css") ?? "").slice(0, CUSTOM_CSS_MAX);
|
||||
await writeSetting("custom_css", cssRaw);
|
||||
}
|
||||
|
||||
siteSettings.reload();
|
||||
await logStaffActivity({
|
||||
staffId: staff.id,
|
||||
action: "theme_update",
|
||||
description: "Updated theme colours",
|
||||
description: "Updated theme settings",
|
||||
});
|
||||
revalidatePath("/", "layout");
|
||||
} catch {
|
||||
|
||||
Reference in new issue
Block a user