diff --git a/frontend/src/Components/Pages/ManagementPortalPage.js b/frontend/src/Components/Pages/ManagementPortalPage.js
index be03046..706a9e4 100644
--- a/frontend/src/Components/Pages/ManagementPortalPage.js
+++ b/frontend/src/Components/Pages/ManagementPortalPage.js
@@ -1,727 +1,313 @@
-import React, { useState, useEffect, useCallback } from 'react';
+import React, { useState, useEffect } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
-import { Button, Container, Box, Typography, Paper } from '@mui/material';
-import Swal from 'sweetalert2/dist/sweetalert2.js';
-import 'sweetalert2/src/sweetalert2.scss';
-
-// Components
+import { Container, Card, CardContent, Typography, Box, Button } from '@mui/material';
+import Swal from 'sweetalert2';
import Navbar from '../ComponentUtils/Headers/Navbar';
import Footer from '../ComponentUtils/Footer';
-import ImageGallery from '../ComponentUtils/ImageGallery';
+import Loading from '../ComponentUtils/LoadingAnimation/Loading';
import ImageGalleryCard from '../ComponentUtils/ImageGalleryCard';
-import DescriptionInput from '../ComponentUtils/MultiUpload/DescriptionInput';
import ConsentBadges from '../ComponentUtils/ConsentBadges';
-import ConsentCheckboxes from '../ComponentUtils/MultiUpload/ConsentCheckboxes';
+import MultiImageDropzone from '../ComponentUtils/MultiUpload/MultiImageDropzone';
+import ImageDescriptionManager from '../ComponentUtils/ImageDescriptionManager';
+import GroupMetadataEditor from '../ComponentUtils/GroupMetadataEditor';
+import ConsentManager from '../ComponentUtils/ConsentManager';
+import DeleteGroupButton from '../ComponentUtils/DeleteGroupButton';
-// Icons
-import DeleteForeverIcon from '@mui/icons-material/DeleteForever';
-import CancelIcon from '@mui/icons-material/Cancel';
+/**
+ * ManagementPortalPage - Self-service management for uploaded groups
+ *
+ * Modulare Struktur mit individuellen Komponenten:
+ * - ImageDescriptionManager: Bildbeschreibungen bearbeiten
+ * - GroupMetadataEditor: Gruppenmetadaten bearbeiten
+ * - ConsentManager: Einwilligungen verwalten
+ * - DeleteGroupButton: Gruppe löschen
+ */
+function ManagementPortalPage() {
+ const { token } = useParams();
+ const navigate = useNavigate();
+
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+ const [group, setGroup] = useState(null);
-const ManagementPortalPage = () => {
- const { token } = useParams();
- const navigate = useNavigate();
-
- const [group, setGroup] = useState(null);
- const [loading, setLoading] = useState(true);
- const [saving, setSaving] = useState(false);
- const [error, setError] = useState(null);
-
- // State from ModerationGroupImagesPage
- const [selectedImages, setSelectedImages] = useState([]);
- const [metadata, setMetadata] = useState({
- year: new Date().getFullYear(),
- title: '',
- description: '',
- name: ''
- });
- const [imageDescriptions, setImageDescriptions] = useState({});
- const [isEditMode, setIsEditMode] = useState(false);
-
- // Pending consent changes (collected locally before saving)
- const [pendingConsentChanges, setPendingConsentChanges] = useState([]);
-
- // Current consents (for ConsentCheckboxes component - includes pending changes)
- const [currentConsents, setCurrentConsents] = useState({
- workshopConsent: false,
- socialMediaConsents: []
- });
-
- // All available social media platforms
- const [allPlatforms, setAllPlatforms] = useState([]);
+ // Load group data
+ const loadGroup = async () => {
+ try {
+ setLoading(true);
+ setError(null);
+
+ const res = await fetch(`/api/manage/${token}`);
+
+ if (res.status === 404) {
+ setError('Ungültiger oder abgelaufener Verwaltungslink');
+ return;
+ }
+
+ if (res.status === 429) {
+ setError('Zu viele Anfragen. Bitte versuchen Sie es später erneut.');
+ return;
+ }
+
+ if (!res.ok) {
+ throw new Error('Fehler beim Laden der Gruppe');
+ }
+
+ const response = await res.json();
+ const data = response.data || response;
+
+ // Transform data
+ const transformedData = {
+ ...data,
+ displayInWorkshop: data.displayInWorkshop || data.display_in_workshop,
+ consentTimestamp: data.consentTimestamp || data.consent_timestamp,
+ consents: {
+ workshopConsent: (data.displayInWorkshop === 1 || data.display_in_workshop === 1),
+ socialMediaConsents: (data.socialMediaConsents || [])
+ .filter(c => c.consented === 1 && c.revoked === 0)
+ .map(c => ({ platformId: c.platform_id, consented: true }))
+ },
+ metadata: {
+ year: data.year || new Date().getFullYear(),
+ title: data.title || '',
+ description: data.description || '',
+ name: data.name || ''
+ },
+ images: (data.images || []).map(img => ({
+ ...img,
+ remoteUrl: `/download/${img.fileName}`,
+ originalName: img.originalName || img.fileName,
+ id: img.id,
+ imageDescription: img.imageDescription || ''
+ }))
+ };
+
+ setGroup(transformedData);
+
+ } catch (e) {
+ console.error('Error loading group:', e);
+ setError('Fehler beim Laden der Gruppe');
+ } finally {
+ setLoading(false);
+ }
+ };
- useEffect(() => {
- loadGroup();
- loadAllPlatforms();
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [token]);
-
- // Reset pending changes when group is reloaded
- useEffect(() => {
- if (group) {
- setPendingConsentChanges([]);
- // Initialize currentConsents from group data
- const workshopStatus = group.consents?.workshopConsent || false;
- const socialMediaStatus = allPlatforms.map(platform => {
- const consent = group.consents?.socialMediaConsents?.find(c => c.platformId === platform.id);
- const isActive = consent ? (consent.consented && !consent.revoked) : false;
- return isActive ? { platformId: platform.id, consented: true } : null;
- }).filter(Boolean);
-
- setCurrentConsents({
- workshopConsent: workshopStatus,
- socialMediaConsents: socialMediaStatus
- });
- }
- }, [group, allPlatforms]);
-
- const loadAllPlatforms = async () => {
- try {
- const res = await fetch('/api/social-media/platforms');
- if (res.ok) {
- const data = await res.json();
- // Backend returns array directly, not wrapped in {platforms: [...]}
- setAllPlatforms(Array.isArray(data) ? data : []);
- }
- } catch (e) {
- console.error('Error loading platforms:', e);
- }
- };
+ useEffect(() => {
+ if (token) {
+ loadGroup();
+ }
+ }, [token]); // eslint-disable-line react-hooks/exhaustive-deps
- const loadGroup = useCallback(async () => {
- try {
- setLoading(true);
- setError(null);
-
- // Token validation + group data loading
- const res = await fetch(`/api/manage/${token}`);
-
- if (res.status === 404) {
- setError('Ungültiger oder abgelaufener Verwaltungslink');
- setLoading(false);
- return;
- }
-
- if (res.status === 429) {
- setError('Zu viele Anfragen. Bitte versuchen Sie es später erneut.');
- setLoading(false);
- return;
- }
-
- if (!res.ok) {
- throw new Error('Fehler beim Laden der Gruppe');
- }
-
- const response = await res.json();
- const data = response.data || response; // Handle both {data: ...} and direct response
-
- // Transform data to match expected structure for ConsentBadges and internal use
- const transformedData = {
- ...data,
- // Keep snake_case for ConsentBadges component compatibility
- display_in_workshop: data.displayInWorkshop,
- consent_timestamp: data.consentTimestamp,
- // Add transformed consents for our UI
- consents: {
- workshopConsent: data.displayInWorkshop === 1,
- socialMediaConsents: (data.socialMediaConsents || []).map(c => ({
- platformId: c.platform_id,
- platformName: c.platform_name,
- platformDisplayName: c.display_name,
- consented: c.consented === 1,
- revoked: c.revoked === 1
- }))
- }
- };
-
- setGroup(transformedData);
+ // Handle adding new images
+ const handleImagesSelected = async (newImages) => {
+ try {
+ const formData = new FormData();
+ newImages.forEach(file => {
+ formData.append('images', file);
+ });
- // Map images to preview-friendly objects (same as ModerationGroupImagesPage)
- if (data.images && data.images.length > 0) {
- const mapped = data.images.map(img => ({
- ...img,
- remoteUrl: `/download/${img.fileName}`,
- originalName: img.originalName || img.fileName,
- id: img.id
- }));
- setSelectedImages(mapped);
-
- // Initialize descriptions from server
- const descriptions = {};
- data.images.forEach(img => {
- if (img.imageDescription) {
- descriptions[img.id] = img.imageDescription;
- }
- });
- setImageDescriptions(descriptions);
- }
+ const res = await fetch(`/api/manage/${token}/images`, {
+ method: 'POST',
+ body: formData
+ });
- // Populate metadata from group
- setMetadata({
- year: data.year || new Date().getFullYear(),
- title: data.title || '',
- description: data.description || '',
- name: data.name || ''
- });
-
- } catch (e) {
- console.error('Error loading group:', e);
- setError('Fehler beim Laden der Gruppe');
- } finally {
- setLoading(false);
- }
- }, [token]);
+ if (!res.ok) {
+ const body = await res.json().catch(() => ({}));
+ throw new Error(body.error || 'Fehler beim Hochladen');
+ }
- // Handle metadata save
- const handleSaveMetadata = async () => {
- if (!group) return;
- setSaving(true);
-
- try {
- const payload = {
- title: metadata.title,
- description: metadata.description,
- year: metadata.year,
- name: metadata.name
- };
+ await Swal.fire({
+ icon: 'success',
+ title: 'Bilder hinzugefügt',
+ text: `${newImages.length} Bild(er) wurden erfolgreich hinzugefügt.`,
+ timer: 2000,
+ showConfirmButton: false
+ });
- const res = await fetch(`/api/manage/${token}/metadata`, {
- method: 'PUT',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify(payload)
- });
+ // Reload group data
+ await loadGroup();
- if (!res.ok) {
- const body = await res.json().catch(() => ({}));
- throw new Error(body.error || 'Fehler beim Speichern');
- }
+ } catch (error) {
+ console.error('Error adding images:', error);
+ Swal.fire({
+ icon: 'error',
+ title: 'Fehler',
+ text: error.message || 'Bilder konnten nicht hinzugefügt werden'
+ });
+ }
+ };
- await Swal.fire({
- icon: 'success',
- title: 'Metadaten gespeichert',
- text: 'Ihre Änderungen wurden gespeichert und müssen erneut moderiert werden.',
- timer: 3000,
- showConfirmButton: true
- });
-
- // Reload group to get updated approval status
- await loadGroup();
-
- } catch (error) {
- console.error('Error saving metadata:', error);
- Swal.fire({
- icon: 'error',
- title: 'Fehler',
- text: error.message || 'Metadaten konnten nicht gespeichert werden'
- });
- } finally {
- setSaving(false);
- }
- };
-
- // Handle image deletion
- const handleRemoveImage = async (imageId) => {
- const result = await Swal.fire({
- title: 'Bild löschen?',
- text: 'Möchten Sie dieses Bild wirklich löschen?',
- icon: 'warning',
- showCancelButton: true,
- confirmButtonColor: '#d33',
- cancelButtonColor: '#3085d6',
- confirmButtonText: 'Ja, löschen',
- cancelButtonText: 'Abbrechen'
- });
-
- if (!result.isConfirmed) return;
-
- try {
- const res = await fetch(`/api/manage/${token}/images/${imageId}`, {
- method: 'DELETE'
- });
-
- if (!res.ok) {
- const body = await res.json().catch(() => ({}));
- throw new Error(body.error || 'Fehler beim Löschen');
- }
-
- // Update local state
- setSelectedImages(prev => prev.filter(img => img.id !== imageId));
-
- Swal.fire({
- icon: 'success',
- title: 'Bild gelöscht',
- timer: 1500,
- showConfirmButton: false
- });
-
- // Reload to get updated group state
- await loadGroup();
-
- } catch (error) {
- console.error('Error deleting image:', error);
- Swal.fire({
- icon: 'error',
- title: 'Fehler',
- text: error.message || 'Bild konnte nicht gelöscht werden'
- });
- }
- };
-
- // Handle consent changes from ConsentCheckboxes component
- const handleConsentChange = (newConsents) => {
- setCurrentConsents(newConsents);
-
- if (!group) return;
-
- const changes = [];
-
- // Check workshop consent change
- const originalWorkshop = group.consents?.workshopConsent || false;
- if (newConsents.workshopConsent !== originalWorkshop) {
- changes.push({
- consentType: 'workshop',
- action: newConsents.workshopConsent ? 'restore' : 'revoke',
- platformId: null
- });
- }
-
- // Check social media consent changes
- allPlatforms.forEach(platform => {
- const consent = group.consents?.socialMediaConsents?.find(c => c.platformId === platform.id);
- const originalStatus = consent ? (consent.consented && !consent.revoked) : false;
- const newStatus = newConsents.socialMediaConsents?.some(c => c.platformId === platform.id) || false;
-
- if (newStatus !== originalStatus) {
- changes.push({
- consentType: 'social_media',
- action: newStatus ? 'restore' : 'revoke',
- platformId: platform.id
- });
- }
- });
-
- setPendingConsentChanges(changes);
- };
-
- // Handle consent revocation (collect locally, don't save yet)
- const handleRevokeConsent = (consentType, platformId = null) => {
- const change = { consentType, action: 'revoke', platformId };
- setPendingConsentChanges(prev => {
- // Remove any previous change for the same consent
- const filtered = prev.filter(c =>
- !(c.consentType === consentType && c.platformId === platformId)
- );
- return [...filtered, change];
- });
- };
-
- // Handle consent restoration (collect locally, don't save yet)
- const handleRestoreConsent = (consentType, platformId = null) => {
- const change = { consentType, action: 'restore', platformId };
- setPendingConsentChanges(prev => {
- // Remove any previous change for the same consent
- const filtered = prev.filter(c =>
- !(c.consentType === consentType && c.platformId === platformId)
- );
- return [...filtered, change];
- });
- };
-
- // Save all pending consent changes
- const handleSaveConsentChanges = async () => {
- if (pendingConsentChanges.length === 0) return;
-
- setSaving(true);
- try {
- // Send all changes to backend
- for (const change of pendingConsentChanges) {
- const payload = change.consentType === 'workshop'
- ? { consentType: 'workshop', action: change.action }
- : { consentType: 'social_media', action: change.action, platformId: change.platformId };
-
- const res = await fetch(`/api/manage/${token}/consents`, {
- method: 'PUT',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify(payload)
- });
-
- if (!res.ok) {
- const body = await res.json().catch(() => ({}));
- throw new Error(body.error || 'Fehler beim Speichern');
- }
- }
-
- await Swal.fire({
- icon: 'success',
- title: 'Änderungen gespeichert',
- text: 'Ihre Einwilligungsänderungen wurden erfolgreich gespeichert.',
- timer: 2000,
- showConfirmButton: false
- });
-
- // Reload group to get updated consent status
- await loadGroup();
-
- } catch (error) {
- console.error('Error saving consent changes:', error);
- Swal.fire({
- icon: 'error',
- title: 'Fehler',
- text: error.message || 'Änderungen konnten nicht gespeichert werden'
- });
- } finally {
- setSaving(false);
- }
- };
-
- // Helper: Get effective consent status considering pending changes
- const getEffectiveConsentStatus = (consentType, platformId = null) => {
- // Check if there's a pending change for this consent
- const pendingChange = pendingConsentChanges.find(c =>
- c.consentType === consentType && c.platformId === platformId
- );
-
- if (pendingChange) {
- return pendingChange.action === 'restore'; // true if restoring, false if revoking
- }
-
- // No pending change, return current status
- if (consentType === 'workshop') {
- return group?.consents?.workshopConsent || false;
- } else if (consentType === 'social_media') {
- const consent = group?.consents?.socialMediaConsents?.find(c => c.platformId === platformId);
- return consent ? (consent.consented && !consent.revoked) : false;
- }
-
- return false;
- };
-
- // Helper: Generate mailto link for revoked social media consents
- const getMailtoLink = () => {
- if (!group) return '';
-
- const revokedPlatforms = pendingConsentChanges
- .filter(c => c.consentType === 'social_media' && c.action === 'revoke')
- .map(c => {
- // Look up platform name in allPlatforms (works even if consent was never granted)
- const platform = allPlatforms.find(p => p.id === c.platformId);
- return platform?.display_name || 'Unbekannte Plattform';
- });
-
- if (revokedPlatforms.length === 0) return '';
-
- const subject = `Löschung Social Media Posts - Gruppe ${group.groupId}`;
- const body = `Hallo, ich habe die Einwilligung zur Veröffentlichung auf folgenden Plattformen widerrufen: ${revokedPlatforms.join(', ')}. Bitte löschen Sie die bereits veröffentlichten Beiträge meiner Gruppe ${group.groupId}. Vielen Dank`;
-
- return `mailto:it@hobbyhimmel.de?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(body)}`;
- };
-
- // Handle group deletion
- const handleDeleteGroup = async () => {
- const result = await Swal.fire({
- title: 'Gruppe komplett löschen?',
- html: `Achtung: Diese Aktion kann nicht rückgängig gemacht werden!
- Alle Bilder und Daten dieser Gruppe werden unwiderruflich gelöscht.`,
- icon: 'warning',
- showCancelButton: true,
- confirmButtonColor: '#d33',
- cancelButtonColor: '#3085d6',
- confirmButtonText: 'Ja, alles löschen',
- cancelButtonText: 'Abbrechen',
- input: 'checkbox',
- inputPlaceholder: 'Ich verstehe, dass diese Aktion unwiderruflich ist'
- });
-
- if (!result.isConfirmed || !result.value) {
- if (result.isConfirmed && !result.value) {
- Swal.fire({
- icon: 'info',
- title: 'Bestätigung erforderlich',
- text: 'Bitte bestätigen Sie das Kontrollkästchen, um fortzufahren.'
- });
- }
- return;
- }
-
- try {
- const res = await fetch(`/api/manage/${token}`, {
- method: 'DELETE'
- });
-
- if (!res.ok) {
- const body = await res.json().catch(() => ({}));
- throw new Error(body.error || 'Fehler beim Löschen');
- }
-
- await Swal.fire({
- icon: 'success',
- title: 'Gruppe gelöscht',
- text: 'Ihre Gruppe wurde erfolgreich gelöscht.',
- timer: 2000,
- showConfirmButton: false
- });
-
- // Redirect to home page
- navigate('/');
-
- } catch (error) {
- console.error('Error deleting group:', error);
- Swal.fire({
- icon: 'error',
- title: 'Fehler',
- text: error.message || 'Gruppe konnte nicht gelöscht werden'
- });
- }
- };
-
- // Handle edit mode toggle
- const handleEditMode = (enabled) => {
- setIsEditMode(enabled);
- };
-
- // Handle description changes
- const handleDescriptionChange = (imageId, description) => {
- setImageDescriptions(prev => ({
- ...prev,
- [imageId]: description.slice(0, 200)
- }));
- };
-
- if (loading) {
- return (
-
-
-
- Lade Ihre Gruppe...
-
-
-
- );
+ const handleReorder = async (newOrder) => {
+ if (!group || !group.groupId) {
+ console.error('No groupId available for reordering');
+ return;
}
- if (error) {
- return (
-
-
-
-
-
-
- Zugriff nicht möglich
-
-
- {error}
-
-
-
-
-
-
- );
- }
+ try {
+ const imageIds = newOrder.map(img => img.id);
+
+ const response = await fetch(`/api/groups/${group.groupId}/reorder`, {
+ method: 'PUT',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ imageIds: imageIds })
+ });
- if (!group) {
- return (
-
-
-
- Gruppe nicht gefunden
-
-
-
- );
- }
+ if (!response.ok) {
+ throw new Error('Reihenfolge konnte nicht gespeichert werden');
+ }
+ await Swal.fire({
+ icon: 'success',
+ title: 'Gespeichert',
+ text: 'Die neue Reihenfolge wurde gespeichert.',
+ timer: 1500,
+ showConfirmButton: false
+ });
+
+ await loadGroup();
+ } catch (error) {
+ console.error('Error reordering images:', error);
+ Swal.fire({
+ icon: 'error',
+ title: 'Fehler',
+ text: error.message || 'Reihenfolge konnte nicht gespeichert werden'
+ });
+ }
+ };
+
+ if (loading) {
return (
-
-
-
-
-
- {/* Header */}
-
- Mein Upload verwalten
-
-
- {/* Group Overview Card */}
-
-
-
- {/* Consent Badges */}
-
-
- Erteilte Einwilligungen:
-
-
-
-
-
- {/* Consent Management Section */}
- {group.consents && (
- <>
-
-
- {/* Save Changes Section (only if there are pending changes) */}
- {pendingConsentChanges.length > 0 && (
-
-
- ⚠️ Sie haben {pendingConsentChanges.length} ungespeicherte Änderung{pendingConsentChanges.length !== 1 ? 'en' : ''}
-
-
- {/* Show mailto link if social media consents are being revoked */}
- {getMailtoLink() && (
-
-
- Bereits veröffentlichte Social Media Beiträge löschen?
-
-
- Kontaktieren Sie uns für die Löschung bereits veröffentlichter Beiträge:
-
- {
- e.currentTarget.style.backgroundColor = '#e3f2fd';
- }}
- onMouseOut={(e) => {
- e.currentTarget.style.backgroundColor = 'transparent';
- }}
- >
- 📧 E-Mail an it@hobbyhimmel.de
-
-
- )}
-
-
-
-
- )}
- >
- )}
-
- {/* Image Gallery */}
-
-
- Ihre Bilder
-
-
-
-
- {/* Metadata Editor */}
- {selectedImages.length > 0 && (
-
-
- Metadaten bearbeiten
-
-
- Änderungen an Metadaten setzen die Freigabe zurück und müssen erneut moderiert werden.
-
-
-
-
-
-
-
- )}
-
- {/* Delete Group Section */}
-
-
- Gefährliche Aktionen
-
-
- Diese Aktion kann nicht rückgängig gemacht werden. Alle Bilder und Daten werden unwiderruflich gelöscht.
-
- }
- onClick={handleDeleteGroup}
- >
- Gruppe komplett löschen
-
-
-
-
-
-
-
+
+
+
+
+
+
+
);
-};
+ }
+
+ if (error) {
+ return (
+
+
+
+
+
+ {error}
+
+
+ {error}
+
+
+
+
+
+
+ );
+ }
+
+ return (
+
+
+
+
+
+
+
+ Mein Upload verwalten
+
+
+ Hier können Sie Ihre hochgeladenen Bilder verwalten, Metadaten bearbeiten und Einwilligungen ändern.
+
+
+ {/* Group Overview */}
+ {group && (
+
+
+
+
+
+ Erteilte Einwilligungen:
+
+
+
+
+ )}
+
+ {/* Add Images Dropzone */}
+
+
+ Weitere Bilder hinzufügen
+
+
+
+
+ {/* Image Descriptions Manager */}
+ {group && group.images && group.images.length > 0 && (
+
+
+
+ )}
+
+ {/* Group Metadata Editor */}
+ {group && (
+
+
+
+ )}
+
+ {/* Consent Manager */}
+ {group && (
+
+
+
+ )}
+
+ {/* Delete Group Button */}
+ {group && (
+
+
+
+ )}
+
+
+
+
+
+
+
+
+ );
+}
export default ManagementPortalPage;
+
diff --git a/frontend/src/Utils/batchUpload.js b/frontend/src/Utils/batchUpload.js
index d7e6bab..7e0ef30 100644
--- a/frontend/src/Utils/batchUpload.js
+++ b/frontend/src/Utils/batchUpload.js
@@ -1,5 +1,5 @@
// Batch-Upload Funktion für mehrere Bilder
-export const uploadImageBatch = async (images, metadata, descriptions = [], consents = null, onProgress) => {
+export const uploadImageBatch = async ({ images, metadata, imageDescriptions = {}, consents = null, onProgress }) => {
if (!images || images.length === 0) {
throw new Error('Keine Bilder zum Upload ausgewählt');
}
@@ -14,9 +14,13 @@ export const uploadImageBatch = async (images, metadata, descriptions = [], cons
// Füge Metadaten hinzu
formData.append('metadata', JSON.stringify(metadata || {}));
- // Füge Beschreibungen hinzu
- if (descriptions && descriptions.length > 0) {
- formData.append('descriptions', JSON.stringify(descriptions));
+ // Füge Beschreibungen hinzu (convert object to array format)
+ const descriptionsArray = Object.entries(imageDescriptions).map(([id, description]) => ({
+ imageId: id,
+ description
+ }));
+ if (descriptionsArray.length > 0) {
+ formData.append('descriptions', JSON.stringify(descriptionsArray));
}
// Füge Einwilligungen hinzu (GDPR)