diff --git a/docker-compose.yml b/docker-compose.yml
index 7513e24..b9a820d 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -27,7 +27,7 @@ services:
networks:
- image-uploader-internal
volumes:
- - app-data:/usr/src/app/data
+ - app-data:/usr/src/app/src/data
volumes:
app-data:
diff --git a/frontend/src/Components/ComponentUtils/MultiUpload/ImagePreviewGallery.js b/frontend/src/Components/ComponentUtils/MultiUpload/ImagePreviewGallery.js
index 96e9b7d..d0a21cf 100644
--- a/frontend/src/Components/ComponentUtils/MultiUpload/ImagePreviewGallery.js
+++ b/frontend/src/Components/ComponentUtils/MultiUpload/ImagePreviewGallery.js
@@ -103,7 +103,11 @@ function ImagePreviewGallery({ images, onRemoveImage, onReorderImages }) {
@@ -128,8 +132,8 @@ function ImagePreviewGallery({ images, onRemoveImage, onReorderImages }) {
{index + 1}
-
- {image.name} • {formatFileSize(image.size)}
+
+ {image.name || image.originalName || 'Bild'}{image.size ? ' • ' + formatFileSize(image.size) : ''}
diff --git a/frontend/src/Components/Pages/GroupImagesPage.js b/frontend/src/Components/Pages/GroupImagesPage.js
index 9d51f4f..08a9af8 100644
--- a/frontend/src/Components/Pages/GroupImagesPage.js
+++ b/frontend/src/Components/Pages/GroupImagesPage.js
@@ -1,8 +1,57 @@
import React, { useState, useEffect } from 'react';
import { useParams, useHistory } from 'react-router-dom';
-import './Css/ModerationPage.css';
+import { makeStyles } from '@material-ui/core/styles';
+import { Button, Card, CardContent, Typography, Container } from '@material-ui/core';
+import Swal from 'sweetalert2/dist/sweetalert2.js';
+import 'sweetalert2/src/sweetalert2.scss';
+
+// Components
+import Navbar from '../ComponentUtils/Headers/Navbar';
+import Footer from '../ComponentUtils/Footer';
+import ImagePreviewGallery from '../ComponentUtils/MultiUpload/ImagePreviewGallery';
+import DescriptionInput from '../ComponentUtils/MultiUpload/DescriptionInput';
+
+import '../ComponentUtils/Css/Background.css';
+
+const useStyles = makeStyles({
+ container: {
+ paddingTop: '20px',
+ paddingBottom: '40px',
+ minHeight: '80vh'
+ },
+ card: {
+ borderRadius: '12px',
+ boxShadow: '0 4px 12px rgba(0, 0, 0, 0.1)',
+ padding: '20px',
+ marginBottom: '20px'
+ },
+ headerText: {
+ fontFamily: 'roboto',
+ fontWeight: '400',
+ fontSize: '28px',
+ textAlign: 'center',
+ marginBottom: '10px',
+ color: '#333333'
+ },
+ subheaderText: {
+ fontFamily: 'roboto',
+ fontWeight: '300',
+ fontSize: '16px',
+ color: '#666666',
+ textAlign: 'center',
+ marginBottom: '30px'
+ },
+ actionButtons: {
+ display: 'flex',
+ gap: '15px',
+ justifyContent: 'center',
+ marginTop: '20px',
+ flexWrap: 'wrap'
+ }
+});
const GroupImagesPage = () => {
+ const classes = useStyles();
const { groupId } = useParams();
const history = useHistory();
const [group, setGroup] = useState(null);
@@ -10,6 +59,10 @@ const GroupImagesPage = () => {
const [saving, setSaving] = useState(false);
const [error, setError] = useState(null);
+ // selectedImages will hold objects compatible with ImagePreviewGallery
+ const [selectedImages, setSelectedImages] = useState([]);
+ const [metadata, setMetadata] = useState({ year: new Date().getFullYear(), title: '', description: '', name: '' });
+
useEffect(() => {
loadGroup();
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -22,6 +75,24 @@ const GroupImagesPage = () => {
if (!res.ok) throw new Error('Nicht gefunden');
const data = await res.json();
setGroup(data);
+
+ // Map group's images to preview-friendly objects
+ if (data.images && data.images.length > 0) {
+ const mapped = data.images.map(img => ({
+ remoteUrl: `/download/${img.fileName}`,
+ originalName: img.originalName || img.fileName,
+ id: img.id
+ }));
+ setSelectedImages(mapped);
+ }
+
+ // populate metadata from group
+ setMetadata({
+ year: data.year || new Date().getFullYear(),
+ title: data.title || '',
+ description: data.description || '',
+ name: data.name || ''
+ });
} catch (e) {
setError('Fehler beim Laden der Gruppe');
} finally {
@@ -31,6 +102,7 @@ const GroupImagesPage = () => {
const handleChange = (field, value) => {
setGroup({ ...group, [field]: value });
+ setMetadata(prev => ({ ...prev, [field]: value }));
};
const handleSave = async () => {
@@ -55,11 +127,11 @@ const GroupImagesPage = () => {
throw new Error(body.message || 'Speichern fehlgeschlagen');
}
- alert('Gruppe erfolgreich aktualisiert');
+ Swal.fire({ icon: 'success', title: 'Gruppe erfolgreich aktualisiert', timer: 1500, showConfirmButton: false });
history.push('/moderation');
} catch (e) {
console.error(e);
- alert('Fehler beim Speichern: ' + e.message);
+ Swal.fire({ icon: 'error', title: 'Fehler beim Speichern', text: e.message });
} finally {
setSaving(false);
}
@@ -71,53 +143,59 @@ const GroupImagesPage = () => {
const res = await fetch(`/groups/${groupId}/images/${imageId}`, { method: 'DELETE' });
if (!res.ok) throw new Error('Löschen fehlgeschlagen');
// Aktualisiere lokale Ansicht
- setGroup({ ...group, images: group.images.filter(img => img.id !== imageId), imageCount: group.imageCount - 1 });
- alert('Bild gelöscht');
+ const newImages = group.images.filter(img => img.id !== imageId);
+ setGroup({ ...group, images: newImages, imageCount: (group.imageCount || 0) - 1 });
+ setSelectedImages(prev => prev.filter(img => img.id !== imageId));
+ Swal.fire({ icon: 'success', title: 'Bild gelöscht', timer: 1200, showConfirmButton: false });
} catch (e) {
console.error(e);
- alert('Fehler beim Löschen des Bildes');
+ Swal.fire({ icon: 'error', title: 'Fehler beim Löschen des Bildes' });
}
};
+ const handleRemoveImage = (indexToRemove) => {
+ // If it's a remote image mapped with id, call delete
+ const img = selectedImages[indexToRemove];
+ if (img && img.id) {
+ handleDeleteImage(img.id);
+ return;
+ }
+ setSelectedImages(prev => prev.filter((_, index) => index !== indexToRemove));
+ };
+
if (loading) return
Lade Gruppe...
;
if (error) return
{error}
;
if (!group) return
Gruppe nicht gefunden
;
return (
-
-
Gruppe bearbeiten
+
+
-
-
-
handleChange('title', e.target.value)} />
+
+
+
+ Gruppe bearbeiten
+ {group.title || ''}
-
-
+
+
+
-
- {group.images.map(image => (
-
-

-
- {image.originalName}
-
-
-
- ))}
-
-
+
+
);
};