diff --git a/backend/migrations/core/077_add_default_photo_sort.js b/backend/migrations/core/077_add_default_photo_sort.js new file mode 100644 index 00000000..c6468903 --- /dev/null +++ b/backend/migrations/core/077_add_default_photo_sort.js @@ -0,0 +1,17 @@ +exports.up = async function(knex) { + const hasColumn = await knex.schema.hasColumn('events', 'default_photo_sort'); + if (!hasColumn) { + await knex.schema.alterTable('events', (table) => { + table.string('default_photo_sort', 50).defaultTo('upload_date_desc'); + }); + } +}; + +exports.down = async function(knex) { + const hasColumn = await knex.schema.hasColumn('events', 'default_photo_sort'); + if (hasColumn) { + await knex.schema.alterTable('events', (table) => { + table.dropColumn('default_photo_sort'); + }); + } +}; diff --git a/backend/src/routes/adminEvents.js b/backend/src/routes/adminEvents.js index c76f538e..b04bbc7e 100644 --- a/backend/src/routes/adminEvents.js +++ b/backend/src/routes/adminEvents.js @@ -259,7 +259,12 @@ router.post('/', adminAuth, requirePermission('events.create'), [ body('hero_image_anchor').optional().custom(validateHeroImageAnchor), // Client access settings (#172) body('client_access_enabled').optional().isBoolean(), - body('client_password').optional().isString() + body('client_password').optional().isString(), + body('default_photo_sort').optional().isIn([ + 'upload_date_desc', 'upload_date_asc', + 'capture_date_desc', 'capture_date_asc', + 'filename_asc', 'filename_desc' + ]) ], async (req, res) => { try { logger.debug('Create event request body', { body: req.body }); @@ -314,7 +319,9 @@ router.post('/', adminAuth, requirePermission('events.create'), [ client_access_enabled = false, client_password = null, // Draft mode - is_draft = true + is_draft = true, + // Default photo sort + default_photo_sort = 'upload_date_desc' } = req.body; const customerName = getCustomerNameFromPayload(req.body); @@ -483,6 +490,7 @@ router.post('/', adminAuth, requirePermission('events.create'), [ hero_image_anchor: hero_image_anchor || 'center', photo_cap: photo_cap || null, is_draft: formatBoolean(parseBooleanInput(is_draft, true)), + default_photo_sort: default_photo_sort || 'upload_date_desc', // Client access (#172) client_access_enabled: formatBoolean(client_access_enabled), ...(client_access_enabled && client_password ? { @@ -856,7 +864,12 @@ router.put('/:id', adminAuth, requirePermission('events.edit'), requireEventOwne // Client access settings (#172) body('client_access_enabled').optional().isBoolean(), body('client_password').optional().isString(), - body('regenerate_client_token').optional().isBoolean() + body('regenerate_client_token').optional().isBoolean(), + body('default_photo_sort').optional().isIn([ + 'upload_date_desc', 'upload_date_asc', + 'capture_date_desc', 'capture_date_asc', + 'filename_asc', 'filename_desc' + ]) ], async (req, res) => { try { const errors = validationResult(req); diff --git a/backend/src/routes/gallery.js b/backend/src/routes/gallery.js index 3072d370..ebaa35c2 100644 --- a/backend/src/routes/gallery.js +++ b/backend/src/routes/gallery.js @@ -123,7 +123,8 @@ router.get('/:slug/info', async (req, res) => { 'header_style', 'hero_divider_style', 'hero_image_anchor', - 'is_draft' + 'is_draft', + 'default_photo_sort' ) .first(); @@ -182,7 +183,8 @@ router.get('/:slug/info', async (req, res) => { hero_logo_url: event.hero_logo_url || null, header_style: event.header_style || 'standard', hero_divider_style: event.hero_divider_style || 'wave', - hero_image_anchor: event.hero_image_anchor || 'center' + hero_image_anchor: event.hero_image_anchor || 'center', + default_photo_sort: event.default_photo_sort || 'upload_date_desc' }); } catch (error) { console.error('Error fetching gallery info:', error); @@ -397,6 +399,7 @@ router.get('/:slug/photos', verifyGalleryAccess, async (req, res) => { header_style: req.event.header_style || 'standard', hero_divider_style: req.event.hero_divider_style || 'wave', hero_image_anchor: req.event.hero_image_anchor || 'center', + default_photo_sort: req.event.default_photo_sort || 'upload_date_desc', ...protectionSettings }, categories: categories, diff --git a/frontend/src/components/gallery/GallerySidebar.tsx b/frontend/src/components/gallery/GallerySidebar.tsx index 9936ca7f..43e9bd13 100644 --- a/frontend/src/components/gallery/GallerySidebar.tsx +++ b/frontend/src/components/gallery/GallerySidebar.tsx @@ -356,7 +356,7 @@ export const GallerySidebar: React.FC = ({ @@ -101,6 +102,17 @@ export const PhotoFilterBar: React.FC = ({ > {t('gallery.sortByDate')} +