feat: decouple hero header from gallery layouts (#158)

- Add separate header_style setting (hero/standard/minimal/none) that can
  be combined with any layout type (grid/masonry/carousel/timeline/mosaic)
- Create HeroHeader and HeroDivider components for reusable hero section
- Add hero_divider_style setting (wave/straight/angle/curve/none)
- Add database migration for header_style and hero_divider_style columns
- Remove deprecated HeroGalleryLayout component
- Fix various TypeScript errors across the codebase:
  - Add missing type properties (css_template_id, updatedAt, justified settings)
  - Fix null handling for event_date and expires_at fields
  - Fix translation function calls and i18n config
  - Remove unused imports and variables
This commit is contained in:
Paul Nothaft
2026-02-01 22:44:28 +01:00
parent 397d33a95a
commit 7b8d8bd92b
35 changed files with 907 additions and 533 deletions
+15 -4
View File
@@ -193,7 +193,10 @@ router.post('/', adminAuth, requirePermission('events.create'), [
// Hero logo settings
body('hero_logo_visible').optional().isBoolean(),
body('hero_logo_size').optional().isIn(['small', 'medium', 'large', 'xlarge']),
body('hero_logo_position').optional().isIn(['top', 'center', 'bottom'])
body('hero_logo_position').optional().isIn(['top', 'center', 'bottom']),
// Header style settings (decoupled from layout)
body('header_style').optional().isIn(['hero', 'standard', 'minimal', 'none']),
body('hero_divider_style').optional().isIn(['wave', 'straight', 'angle', 'curve', 'none'])
], async (req, res) => {
try {
logger.debug('Create event request body', { body: req.body });
@@ -236,7 +239,10 @@ router.post('/', adminAuth, requirePermission('events.create'), [
// Hero logo settings
hero_logo_visible = true,
hero_logo_size = 'medium',
hero_logo_position = 'top'
hero_logo_position = 'top',
// Header style settings
header_style = 'standard',
hero_divider_style = 'wave'
} = req.body;
const customerName = getCustomerNameFromPayload(req.body);
@@ -377,7 +383,9 @@ router.post('/', adminAuth, requirePermission('events.create'), [
css_template_id: css_template_id || null,
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'
hero_logo_position: hero_logo_position || 'top',
header_style: header_style || 'standard',
hero_divider_style: hero_divider_style || 'wave'
}).returning('id');
// Handle both PostgreSQL (returns array of objects) and SQLite (returns array of IDs)
@@ -658,7 +666,10 @@ router.put('/:id', adminAuth, requirePermission('events.edit'), [
// Hero logo settings
body('hero_logo_visible').optional().isBoolean(),
body('hero_logo_size').optional().isIn(['small', 'medium', 'large', 'xlarge']),
body('hero_logo_position').optional().isIn(['top', 'center', 'bottom'])
body('hero_logo_position').optional().isIn(['top', 'center', 'bottom']),
// Header style settings (decoupled from layout)
body('header_style').optional().isIn(['hero', 'standard', 'minimal', 'none']),
body('hero_divider_style').optional().isIn(['wave', 'straight', 'angle', 'curve', 'none'])
], async (req, res) => {
try {
const errors = validationResult(req);
+1 -3
View File
@@ -647,9 +647,7 @@ router.post('/:eventId/photos/bulk-update', adminAuth, requirePermission('photos
}
// Prepare update data
const updateData = {
updated_at: new Date()
};
const updateData = {};
if (updates.category_id !== undefined) {
// Handle type-based categories ('individual' or 'collage')
+8 -2
View File
@@ -119,7 +119,9 @@ router.get('/:slug/info', async (req, res) => {
'hero_logo_visible',
'hero_logo_size',
'hero_logo_position',
'hero_logo_url'
'hero_logo_url',
'header_style',
'hero_divider_style'
)
.first();
@@ -170,7 +172,9 @@ router.get('/:slug/info', async (req, res) => {
hero_logo_visible: event.hero_logo_visible !== false && event.hero_logo_visible !== 0 && event.hero_logo_visible !== '0',
hero_logo_size: event.hero_logo_size || 'medium',
hero_logo_position: event.hero_logo_position || 'top',
hero_logo_url: event.hero_logo_url || null
hero_logo_url: event.hero_logo_url || null,
header_style: event.header_style || 'standard',
hero_divider_style: event.hero_divider_style || 'wave'
});
} catch (error) {
console.error('Error fetching gallery info:', error);
@@ -344,6 +348,8 @@ router.get('/:slug/photos', verifyGalleryAccess, async (req, res) => {
hero_logo_size: req.event.hero_logo_size || 'medium',
hero_logo_position: req.event.hero_logo_position || 'top',
hero_logo_url: req.event.hero_logo_url || null,
header_style: req.event.header_style || 'standard',
hero_divider_style: req.event.hero_divider_style || 'wave',
...protectionSettings
},
categories: categories,