fix(photos): category changes now persist and display correctly (#77)

- Backend PATCH /photos/:photoId now returns updated photo object
- Photo listing now joins with photo_categories table to get actual
  category name and slug instead of hardcoding based on photo.type
- Frontend service now properly returns AdminPhoto from update response

Fixes #77
This commit is contained in:
Paul Nothaft
2026-01-07 17:31:19 +01:00
parent 892e47d017
commit d9da98c355
2 changed files with 17 additions and 7 deletions
+14 -5
View File
@@ -518,7 +518,15 @@ router.patch('/:eventId/photos/:photoId', adminAuth, requirePermission('photos.e
.where({ id: photoId, event_id: eventId }) .where({ id: photoId, event_id: eventId })
.update(updateData); .update(updateData);
res.json({ message: 'Photo updated successfully' }); // Fetch and return the updated photo
const updatedPhoto = await db('photos')
.where({ id: photoId, event_id: eventId })
.first();
res.json({
message: 'Photo updated successfully',
photo: updatedPhoto
});
} catch (error) { } catch (error) {
console.error('Error updating photo:', error); console.error('Error updating photo:', error);
res.status(500).json({ error: 'Failed to update photo' }); res.status(500).json({ error: 'Failed to update photo' });
@@ -691,7 +699,8 @@ router.get('/:eventId/photos', adminAuth, requirePermission('photos.view'), asyn
let query = db('photos') let query = db('photos')
.where({ 'photos.event_id': eventId }) .where({ 'photos.event_id': eventId })
.select('photos.*'); .leftJoin('photo_categories', 'photos.category_id', 'photo_categories.id')
.select('photos.*', 'photo_categories.name as pc_name', 'photo_categories.slug as pc_slug');
// Filter by type (individual/collage) - category_id maps to type // Filter by type (individual/collage) - category_id maps to type
if (category_id !== undefined) { if (category_id !== undefined) {
@@ -748,9 +757,9 @@ router.get('/:eventId/photos', adminAuth, requirePermission('photos.view'), asyn
// Always expose a thumbnail URL; backend will generate on demand if missing // Always expose a thumbnail URL; backend will generate on demand if missing
thumbnail_url: `/admin/photos/${eventId}/thumbnail/${photo.id}`, thumbnail_url: `/admin/photos/${eventId}/thumbnail/${photo.id}`,
type: photo.type, type: photo.type,
category_id: photo.type, category_id: photo.category_id || photo.type,
category_name: photo.type === 'individual' ? 'Individual Photos' : 'Collages', category_name: photo.pc_name || (photo.type === 'individual' ? 'Individual Photos' : 'Collages'),
category_slug: photo.type, category_slug: photo.pc_slug || photo.type,
size: photo.size_bytes, size: photo.size_bytes,
uploaded_at: photo.uploaded_at, uploaded_at: photo.uploaded_at,
// Feedback data // Feedback data
+3 -2
View File
@@ -66,8 +66,9 @@ class PhotosService {
await api.post(`/admin/events/${eventId}/photos/bulk-delete`, { photoIds }); await api.post(`/admin/events/${eventId}/photos/bulk-delete`, { photoIds });
} }
async updatePhotoCategory(eventId: number, photoId: number, categoryId: number | null): Promise<void> { async updatePhotoCategory(eventId: number, photoId: number, categoryId: number | string | null): Promise<AdminPhoto> {
await api.patch(`/admin/events/${eventId}/photos/${photoId}`, { category_id: categoryId }); const response = await api.patch(`/admin/events/${eventId}/photos/${photoId}`, { category_id: categoryId });
return response.data.photo;
} }
async updatePhotosCategory(eventId: number, photoIds: number[], categoryId: number | null): Promise<void> { async updatePhotosCategory(eventId: number, photoIds: number[], categoryId: number | null): Promise<void> {