diff --git a/ERWEITERUNG.md b/ERWEITERUNG.md index eb0994f..ee8cd02 100644 --- a/ERWEITERUNG.md +++ b/ERWEITERUNG.md @@ -466,87 +466,3 @@ function SlideshowPage() { --- -## 🚀 Deployment-Überlegungen - -### Datei-Größe Limits -```javascript -// Backend-Konfiguration erweitern -app.use(fileUpload({ - limits: { - fileSize: 50 * 1024 * 1024, // 50MB pro Datei - files: 20 // Max 20 Dateien pro Upload - }, -})); -``` - -### Speicher-Management -- **Cleanup-Job**: Alte Upload-Gruppen nach X Tagen löschen -- **Komprimierung**: Automatische Bildkomprimierung für große Dateien -- **CDN-Integration**: Für bessere Performance bei vielen Bildern - -### Sicherheit -- **File-Type Validation**: Nur erlaubte Bildformate -- **Virus-Scanning**: Optional für Produktionsumgebung -- **Rate Limiting**: Upload-Beschränkungen pro IP/User - ---- - -## 📈 Erweiterungs-Möglichkeiten (Zukunft) - -### Erweiterte Features -- **Benutzer-Accounts**: Upload-Gruppen Benutzern zuordnen -- **Tagging-System**: Bilder mit Tags versehen -- **Sharing**: Upload-Gruppen per Link teilen -- **Export**: Slideshow als Video oder PDF exportieren - -### Slideshow-Features -- **Autoplay**: Automatischer Bildwechsel -- **Übergangs-Effekte**: Fade, Slide, etc. -- **Hintergrundmusik**: Audio-Upload für Slideshows -- **Vollbild-Modus**: Immersive Slideshow-Erfahrung - -### Admin-Features -- **Upload-Statistiken**: Dashboard mit Nutzungsmetriken -- **Content-Moderation**: Gemeldete Inhalte prüfen -- **Bulk-Operations**: Mehrere Gruppen gleichzeitig verwalten - ---- - -## ⚡ Quick-Start Implementierung - -Für einen schnellen Proof-of-Concept (2-3 Stunden): - -1. **Backend**: Erweitere `/upload` Route für Array-Handling -2. **Frontend**: Ändere bestehende Dropzone auf `multiple: true` -3. **Einfache Galerie**: Zeige alle Bilder einer "Session" an -4. **Basis-Slideshow**: Einfache Vor/Zurück-Navigation - -Dies würde eine funktionale Basis schaffen, die später ausgebaut werden kann. - ---- - -## 🎯 Erfolgskriterien - -### Must-Have -- ✅ Mehrere Bilder gleichzeitig auswählen -- ✅ Beschreibungstext hinzufügen -- ✅ Upload als zusammengehörige Gruppe -- ✅ Einfache Slideshow-Anzeige -- ✅ Mobile-Kompatibilität - -### Nice-to-Have -- 🎨 Drag & Drop Reihenfolge ändern -- 📊 Upload-Progress mit Details -- 🖼️ Thumbnail-Navigation in Slideshow -- 💾 Auto-Save der Beschreibung -- 🔄 Batch-Operations (alle entfernen, etc.) - -### Future Features -- 👤 User-Management -- 🏷️ Tagging-System -- 📤 Export-Funktionen -- 🎵 Audio-Integration - ---- - -**Fazit**: Die Erweiterung ist gut machbar und baut logisch auf der bestehenden Architektur auf. Der modulare Ansatz ermöglicht schrittweise Implementierung und spätere Erweiterungen. \ No newline at end of file diff --git a/ERWEITERUNG2.md b/ERWEITERUNG2.md new file mode 100644 index 0000000..9c3fe2a --- /dev/null +++ b/ERWEITERUNG2.md @@ -0,0 +1,57 @@ +# Erweiterung 2: Zusätzliche Funktionen +## Backend +[ ] Erweiterung der API um die Funktion bestehende Daten zu editieren/aktualisieren +[ ] Integration eines Benachrichtigungssystems (E-Mail, Push-Benachrichtigungen) wenn eine neue Slideshow hochgeladen wurde +[ ] Implementierung eines Logging-Systems zur Nachverfolgung von Änderungen und Aktivitäten + +# Frontend +[ ] Erweiterung der Benutzeroberfläche um eine Editierfunktion für bestehende Einträge in ModerationPage.js + [ ] In der angezeigten Gruppen soll statt Bilder ansehen Gruppe editieren stehen + [ ] Diese bestehende Ansicht (Bilder ansehen) soll als eigene Seite implementiert werden + + +## 🚀 Deployment-Überlegungen + + +### Speicher-Management +- **Komprimierung**: Automatische Bildkomprimierung für große Dateien + +### Sicherheit +- **File-Type Validation**: Nur erlaubte Bildformate +- **Virus-Scanning**: Optional für Produktionsumgebung + + +--- + +## 📈 Erweiterungs-Möglichkeiten (Zukunft) + +### Erweiterte Features +- **Benutzer-Accounts**: Upload-Gruppen Benutzern zuordnen +- **Export**: Slideshow als Video oder PDF exportieren + + +### Admin-Features +- **Bulk-Operations**: Mehrere Gruppen gleichzeitig verwalten (zum Beispiel löschen) + +--- + + +## 🎯 Erfolgskriterien + +### Must-Have +- ✅ Mobile-Kompatibilität + +### Nice-to-Have +- 🎨 Drag & Drop Reihenfolge ändern +- 📊 Upload-Progress mit Details +- 🖼️ Thumbnail-Navigation in Slideshow +- 🔄 Batch-Operations (alle entfernen, etc.) + +### Future Features +- 👤 User-Management +- 🏷️ Tagging-System +- 📤 Export-Funktionen +- 🎵 Audio-Integration + +--- + diff --git a/backend/src/routes/groups.js b/backend/src/routes/groups.js index bb14033..73f7177 100644 --- a/backend/src/routes/groups.js +++ b/backend/src/routes/groups.js @@ -137,6 +137,53 @@ router.patch('/groups/:groupId/approve', async (req, res) => { } }); +// Gruppe bearbeiten (Metadaten aktualisieren) +router.patch('/groups/:groupId', async (req, res) => { + try { + const { groupId } = req.params; + + // Erlaubte Felder zum Aktualisieren + const allowed = ['year', 'title', 'description', 'name']; + const updates = {}; + + for (const field of allowed) { + if (req.body[field] !== undefined) { + updates[field] = req.body[field]; + } + } + + if (Object.keys(updates).length === 0) { + return res.status(400).json({ + error: 'Invalid request', + message: 'Keine gültigen Felder zum Aktualisieren angegeben' + }); + } + + const updated = await GroupRepository.updateGroup(groupId, updates); + + if (!updated) { + return res.status(404).json({ + error: 'Group not found', + message: `Gruppe mit ID ${groupId} wurde nicht gefunden` + }); + } + + res.json({ + success: true, + message: 'Gruppe erfolgreich aktualisiert', + groupId: groupId, + updates: updates + }); + } catch (error) { + console.error('Error updating group:', error); + res.status(500).json({ + error: 'Internal server error', + message: 'Fehler beim Aktualisieren der Gruppe', + details: error.message + }); + } +}); + // Einzelnes Bild löschen router.delete('/groups/:groupId/images/:imageId', async (req, res) => { try { diff --git a/frontend/src/App.js b/frontend/src/App.js index 311ad25..788e5ed 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -6,6 +6,7 @@ import UploadedImage from './Components/Pages/UploadedImagePage'; import MultiUploadPage from './Components/Pages/MultiUploadPage'; import SlideshowPage from './Components/Pages/SlideshowPage'; import GroupsOverviewPage from './Components/Pages/GroupsOverviewPage'; +import GroupImagesPage from './Components/Pages/GroupImagesPage'; import ModerationPage from './Components/Pages/ModerationPage'; import FZF from './Components/Pages/404Page.js' @@ -16,7 +17,8 @@ function App() { - + + diff --git a/frontend/src/Components/Pages/GroupImagesPage.js b/frontend/src/Components/Pages/GroupImagesPage.js new file mode 100644 index 0000000..9d51f4f --- /dev/null +++ b/frontend/src/Components/Pages/GroupImagesPage.js @@ -0,0 +1,124 @@ +import React, { useState, useEffect } from 'react'; +import { useParams, useHistory } from 'react-router-dom'; +import './Css/ModerationPage.css'; + +const GroupImagesPage = () => { + const { groupId } = useParams(); + const history = useHistory(); + const [group, setGroup] = useState(null); + const [loading, setLoading] = useState(true); + const [saving, setSaving] = useState(false); + const [error, setError] = useState(null); + + useEffect(() => { + loadGroup(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [groupId]); + + const loadGroup = async () => { + try { + setLoading(true); + const res = await fetch(`/moderation/groups/${groupId}`); + if (!res.ok) throw new Error('Nicht gefunden'); + const data = await res.json(); + setGroup(data); + } catch (e) { + setError('Fehler beim Laden der Gruppe'); + } finally { + setLoading(false); + } + }; + + const handleChange = (field, value) => { + setGroup({ ...group, [field]: value }); + }; + + const handleSave = async () => { + if (!group) return; + setSaving(true); + try { + const payload = { + title: group.title, + description: group.description, + year: group.year, + name: group.name + }; + + const res = await fetch(`/groups/${groupId}`, { + method: 'PATCH', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(payload) + }); + + if (!res.ok) { + const body = await res.json().catch(() => ({})); + throw new Error(body.message || 'Speichern fehlgeschlagen'); + } + + alert('Gruppe erfolgreich aktualisiert'); + history.push('/moderation'); + } catch (e) { + console.error(e); + alert('Fehler beim Speichern: ' + e.message); + } finally { + setSaving(false); + } + }; + + const handleDeleteImage = async (imageId) => { + if (!window.confirm('Bild wirklich löschen?')) return; + try { + 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'); + } catch (e) { + console.error(e); + alert('Fehler beim Löschen des Bildes'); + } + }; + + if (loading) return
Lade Gruppe...
; + if (error) return
{error}
; + if (!group) return
Gruppe nicht gefunden
; + + return ( +
+

Gruppe bearbeiten

+ +
+ + handleChange('title', e.target.value)} /> + + +