Files
EpicNext-Cms/src/app/admin/theme-builder/page.tsx
T
openhands adf0658916
CI / check (push) Failing after 29s
CI / release (push) Skipped
CI / deploy (push) Skipped
feat: extend theme builder with block visibility, layout, effects, media, and custom CSS tabs
- Add theme-blocks.ts registry with 24 themeable blocks across 4 categories
- Extend resolver to resolve blocks, layout, effects, media, and custom CSS per scope
- Add data-theme-block attributes to all site layout blocks
- Extend ScopedThemeVars to generate CSS for block visibility, layout vars, effect vars, media vars, and custom CSS
- Rewrite admin UI with 6 tabs: Colors, Blocks, Layout, Effects, Media, Custom
- Extend server actions to save/load all new setting types
- No database migration needed - uses existing theme_scope_values table with prefixed keys
2026-08-31 17:44:29 +02:00

93 lines
2.6 KiB
TypeScript

import { redirect } from "next/navigation";
import { getThemeBuilderData } from "@/actions/theme-builder";
import { canAccess, getAdminContext, PERMS } from "@/lib/permissions";
import { getFullScopeValues } from "@/lib/theme-resolver";
import { ScopeForm } from "./scope-form";
import { ScopeTree } from "./scope-tree";
import { ThemeEditor } from "./theme-editor";
export const metadata = { title: "Theme Builder" };
export default async function ThemeBuilderPage({
searchParams,
}: {
searchParams: Promise<Record<string, string | undefined>>;
}) {
const ctx = await getAdminContext();
if (!ctx || !canAccess(ctx.permissions, PERMS.SETTINGS_VIEW)) {
redirect("/admin");
}
const sp = await searchParams;
const { scopes, tree, moduleIds } = await getThemeBuilderData();
const selectedId = sp.scope ? Number(sp.scope) : (scopes[0]?.id ?? null);
const values = selectedId
? await getFullScopeValues(selectedId)
: {
light: {},
dark: {},
blocks: {},
layout: {},
effects: {},
media: {},
custom: {},
};
return (
<div>
<h1 className="admin-section-title mb-4">Theme Builder</h1>
<p className="text-xs text-[var(--admin-text-muted)] mb-6">
Create and manage scoped themes for different parts of your site. Themes
inherit from parent scopes — only override what you need.
</p>
{sp.created ? (
<div className="mb-4 p-3 rounded-lg theme-status-success text-xs">
Scope created.
</div>
) : null}
{sp.updated ? (
<div className="mb-4 p-3 rounded-lg theme-status-success text-xs">
Scope updated.
</div>
) : null}
{sp.deleted ? (
<div className="mb-4 p-3 rounded-lg theme-status-success text-xs">
Scope deleted.
</div>
) : null}
{sp.saved ? (
<div className="mb-4 p-3 rounded-lg theme-status-success text-xs">
Theme values saved.
</div>
) : null}
{sp.duplicated ? (
<div className="mb-4 p-3 rounded-lg theme-status-success text-xs">
Scope duplicated.
</div>
) : null}
<div className="grid grid-cols-1 lg:grid-cols-12 gap-6">
<div className="lg:col-span-3">
<ScopeTree tree={tree} selectedId={selectedId} />
<div className="mt-4">
<ScopeForm moduleIds={moduleIds} scopeCount={scopes.length} />
</div>
</div>
<div className="lg:col-span-9">
{selectedId ? (
<ThemeEditor scopeId={selectedId} scopes={scopes} values={values} />
) : (
<div className="admin-card p-8 text-center text-[var(--admin-text-muted)]">
Select or create a scope to start editing.
</div>
)}
</div>
</div>
</div>
);
}