fix: sync header_style DB column with theme editor selections (#158)

The frontend never sent header_style/hero_divider_style as separate
fields when creating or updating events, so the database columns always
kept their default value of 'standard' — making the hero header
impossible to enable through the admin UI.

- Extract headerStyle/heroDividerStyle from theme config and include in
  create and update payloads (CreateEventPage, EventDetailsPage)
- Add backend fallback to extract values from color_theme JSON when not
  explicitly provided, ensuring older clients stay in sync
This commit is contained in:
Paul Nothaft
2026-02-03 17:12:56 +01:00
parent 6c30e2c2ed
commit a19e7c40a2
4 changed files with 56 additions and 2 deletions
+8
View File
@@ -86,6 +86,14 @@ docs/*_PLAN.md
docs/test-*.md
docs/feature-*.md
# Scaffolding documentation (local development reference)
docs/DATABASE_SCHEMA.md
docs/BACKEND_SERVICES.md
docs/API_ROUTES.md
docs/FRONTEND_ARCHITECTURE.md
docs/DEVELOPER_ONBOARDING.md
docs/ENVIRONMENT_VARIABLES.md
# Local backup directory (from testing)
backup/
+43 -2
View File
@@ -365,6 +365,26 @@ router.post('/', adminAuth, requirePermission('events.create'), [
await fs.mkdir(path.join(eventPath, 'collages'), { recursive: true });
await fs.mkdir(path.join(eventPath, 'individual'), { recursive: true });
// Sync header_style / hero_divider_style from color_theme JSON when not
// explicitly provided in the request body (#158).
let effectiveHeaderStyle = header_style;
let effectiveDividerStyle = hero_divider_style;
if (color_theme && (!req.body.header_style || !req.body.hero_divider_style)) {
try {
if (typeof color_theme === 'string' && color_theme.startsWith('{')) {
const parsed = JSON.parse(color_theme);
if (!req.body.header_style && parsed.headerStyle) {
effectiveHeaderStyle = parsed.headerStyle;
}
if (!req.body.hero_divider_style && parsed.heroDividerStyle) {
effectiveDividerStyle = parsed.heroDividerStyle;
}
}
} catch (_) {
// color_theme is not JSON nothing to extract
}
}
// Insert into database
const insertResult = await db('events').insert({
slug,
@@ -394,8 +414,8 @@ router.post('/', adminAuth, requirePermission('events.create'), [
hero_logo_visible: formatBoolean(hero_logo_visible !== undefined ? hero_logo_visible : true),
hero_logo_size: hero_logo_size || 'medium',
hero_logo_position: hero_logo_position || 'top',
header_style: header_style || 'standard',
hero_divider_style: hero_divider_style || 'wave',
header_style: effectiveHeaderStyle || 'standard',
hero_divider_style: effectiveDividerStyle || 'wave',
hero_image_anchor: hero_image_anchor || 'center'
}).returning('id');
@@ -810,6 +830,27 @@ router.put('/:id', adminAuth, requirePermission('events.edit'), [
updates.hero_logo_visible = formatBoolean(updates.hero_logo_visible);
}
// Sync header_style / hero_divider_style from color_theme JSON when not
// explicitly provided in the request body (#158). This ensures the
// database columns stay in sync even if the frontend only sends the
// serialised theme object.
if (updates.color_theme && !Object.prototype.hasOwnProperty.call(updates, 'header_style')) {
try {
const themeStr = typeof updates.color_theme === 'string' ? updates.color_theme : '';
if (themeStr.startsWith('{')) {
const parsed = JSON.parse(themeStr);
if (parsed.headerStyle) {
updates.header_style = parsed.headerStyle;
}
if (parsed.heroDividerStyle && !Object.prototype.hasOwnProperty.call(updates, 'hero_divider_style')) {
updates.hero_divider_style = parsed.heroDividerStyle;
}
}
} catch (_) {
// color_theme is not JSON (e.g. preset name) nothing to extract
}
}
// Update event
await db('events')
.where('id', id)
@@ -292,6 +292,8 @@ export const CreateEventPage: React.FC = () => {
password: formData.require_password ? formData.password : undefined,
welcome_message: formData.welcome_message || '',
color_theme: JSON.stringify(formData.theme_config),
header_style: formData.theme_config.headerStyle || 'standard',
hero_divider_style: formData.theme_config.heroDividerStyle || 'wave',
expiration_days: requireExpiration ? formData.expires_in_days : undefined,
allow_user_uploads: formData.allow_user_uploads,
upload_category_id: formData.upload_category_id,
@@ -550,6 +550,9 @@ export const EventDetailsPage: React.FC = () => {
hero_logo_position: editForm.hero_logo_position,
// Hero image anchor position (#162)
hero_image_anchor: editForm.hero_image_anchor,
// Header style settings (decoupled from layout, #158)
header_style: currentTheme?.headerStyle || 'standard',
hero_divider_style: currentTheme?.heroDividerStyle || 'wave',
};
// Only include fields that have defined values