From 07b436cc4df86fbab880bb8efba8d1771a0e38d4 Mon Sep 17 00:00:00 2001 From: "matthias.lotz" Date: Fri, 7 Nov 2025 23:20:50 +0100 Subject: [PATCH] feat: Complete image description feature implementation Features: - Add image description field (max 200 chars) for individual images - Replace 'Sort' button with 'Edit' button in image gallery cards - Enable edit mode with text fields for each image in moderation - Display descriptions in slideshow and public views - Integrate description saving with main save button Frontend changes: - ImageGalleryCard: Add edit mode UI with textarea and character counter - ModerationGroupImagesPage: Integrate description editing into main save flow - Fix keyboard event propagation in textarea (spacebar issue) - Remove separate 'Save Descriptions' button - Add ESLint fixes for useCallback dependencies Backend changes: - Fix route order: batch-description route must come before :imageId route - Ensure batch description update API works correctly Build optimizations: - Add .dockerignore to exclude development data (182MB reduction) - Fix Dockerfile: Remove non-existent frontend/conf directory - Reduce backend image size from 437MB to 247MB Fixes: - Fix route matching issue with batch-description endpoint - Prevent keyboard events from triggering drag-and-drop - Clean up unused functions and ESLint warnings --- .dockerignore | 21 ++++ backend/.dockerignore | 7 +- backend/src/routes/groups.js | 92 +++++++-------- docker/prod/frontend/Dockerfile | 1 - .../ComponentUtils/ImageGalleryCard.js | 3 + .../Pages/ModerationGroupImagesPage.js | 107 ++++++++---------- prod.sh | 3 +- 7 files changed, 124 insertions(+), 110 deletions(-) create mode 100644 .dockerignore diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..1ab5c0c --- /dev/null +++ b/.dockerignore @@ -0,0 +1,21 @@ +# Backend data (images, database) +backend/src/data/db/*.db +backend/src/data/db/*.db-* +backend/src/data/images/ +backend/src/data/previews/ +backend/src/data/groups/ + +# Node modules (will be installed in container) +backend/node_modules +frontend/node_modules + +# Build outputs +frontend/build + +# Dev files +.git +.gitignore +*.md +docs/ +test_photos/ +data-backup/ diff --git a/backend/.dockerignore b/backend/.dockerignore index 1125523..f185386 100644 --- a/backend/.dockerignore +++ b/backend/.dockerignore @@ -1,3 +1,8 @@ node_modules npm-debug.log -upload/ \ No newline at end of file +upload/ +src/data/db/*.db +src/data/db/*.db-* +src/data/images/ +src/data/previews/ +src/data/groups/ \ No newline at end of file diff --git a/backend/src/routes/groups.js b/backend/src/routes/groups.js index 86afe56..5523578 100644 --- a/backend/src/routes/groups.js +++ b/backend/src/routes/groups.js @@ -214,52 +214,7 @@ router.delete('/groups/:groupId/images/:imageId', async (req, res) => { } }); -// Einzelne Bildbeschreibung aktualisieren -router.patch('/groups/:groupId/images/:imageId', async (req, res) => { - try { - const { groupId, imageId } = req.params; - const { image_description } = req.body; - - // Validierung: Max 200 Zeichen - if (image_description && image_description.length > 200) { - return res.status(400).json({ - error: 'Invalid request', - message: 'Bildbeschreibung darf maximal 200 Zeichen lang sein' - }); - } - - const updated = await GroupRepository.updateImageDescription( - parseInt(imageId), - groupId, - image_description - ); - - if (!updated) { - return res.status(404).json({ - error: 'Image not found', - message: `Bild mit ID ${imageId} in Gruppe ${groupId} wurde nicht gefunden` - }); - } - - res.json({ - success: true, - message: 'Bildbeschreibung erfolgreich aktualisiert', - groupId: groupId, - imageId: parseInt(imageId), - imageDescription: image_description - }); - - } catch (error) { - console.error('Error updating image description:', error); - res.status(500).json({ - error: 'Internal server error', - message: 'Fehler beim Aktualisieren der Bildbeschreibung', - details: error.message - }); - } -}); - -// Batch-Update für mehrere Bildbeschreibungen +// Batch-Update für mehrere Bildbeschreibungen (MUSS VOR der einzelnen Route stehen!) router.patch('/groups/:groupId/images/batch-description', async (req, res) => { try { const { groupId } = req.params; @@ -308,6 +263,51 @@ router.patch('/groups/:groupId/images/batch-description', async (req, res) => { } }); +// Einzelne Bildbeschreibung aktualisieren +router.patch('/groups/:groupId/images/:imageId', async (req, res) => { + try { + const { groupId, imageId } = req.params; + const { image_description } = req.body; + + // Validierung: Max 200 Zeichen + if (image_description && image_description.length > 200) { + return res.status(400).json({ + error: 'Invalid request', + message: 'Bildbeschreibung darf maximal 200 Zeichen lang sein' + }); + } + + const updated = await GroupRepository.updateImageDescription( + parseInt(imageId), + groupId, + image_description + ); + + if (!updated) { + return res.status(404).json({ + error: 'Image not found', + message: `Bild mit ID ${imageId} in Gruppe ${groupId} wurde nicht gefunden` + }); + } + + res.json({ + success: true, + message: 'Bildbeschreibung erfolgreich aktualisiert', + groupId: groupId, + imageId: parseInt(imageId), + imageDescription: image_description + }); + + } catch (error) { + console.error('Error updating image description:', error); + res.status(500).json({ + error: 'Internal server error', + message: 'Fehler beim Aktualisieren der Bildbeschreibung', + details: error.message + }); + } +}); + // Gruppe löschen router.delete(endpoints.DELETE_GROUP, async (req, res) => { try { diff --git a/docker/prod/frontend/Dockerfile b/docker/prod/frontend/Dockerfile index d05afe3..21df38c 100644 --- a/docker/prod/frontend/Dockerfile +++ b/docker/prod/frontend/Dockerfile @@ -13,7 +13,6 @@ FROM nginx:stable-alpine # Nginx config RUN rm -rf /etc/nginx/conf.d COPY docker/prod/frontend/nginx.conf /etc/nginx/nginx.conf -COPY frontend/conf /etc/nginx # Copy htpasswd file for authentication COPY docker/prod/frontend/config/htpasswd /etc/nginx/.htpasswd diff --git a/frontend/src/Components/ComponentUtils/ImageGalleryCard.js b/frontend/src/Components/ComponentUtils/ImageGalleryCard.js index 8e5a83c..b0debd3 100644 --- a/frontend/src/Components/ComponentUtils/ImageGalleryCard.js +++ b/frontend/src/Components/ComponentUtils/ImageGalleryCard.js @@ -148,6 +148,9 @@ const ImageGalleryCard = ({