- Replace @material-ui/core -> @mui/material - Replace @material-ui/icons -> @mui/icons-material - Switch makeStyles imports to @mui/styles (compat) - Add @mui/material, @mui/icons-material, @mui/styles, @emotion/react, @emotion/styled to package.json Notes: Kept makeStyles via @mui/styles for incremental migration; next: replace makeStyles usage with sx/styled where needed.
162 lines
5.8 KiB
JavaScript
162 lines
5.8 KiB
JavaScript
import React, { useState, useEffect } from 'react';
|
|
import { useParams, useNavigate } from 'react-router-dom';
|
|
import { Button, Container } from '@mui/material';
|
|
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 ImageGallery from '../ComponentUtils/ImageGallery';
|
|
import DescriptionInput from '../ComponentUtils/MultiUpload/DescriptionInput';
|
|
|
|
|
|
|
|
|
|
const ModerationGroupImagesPage = () => {
|
|
const { groupId } = useParams();
|
|
const navigate = useNavigate();
|
|
const [group, setGroup] = useState(null);
|
|
const [loading, setLoading] = useState(true);
|
|
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
|
|
}, [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);
|
|
|
|
// 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 {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const handleSave = async () => {
|
|
if (!group) return;
|
|
setSaving(true);
|
|
try {
|
|
// Use metadata state (controlled by DescriptionInput) as source of truth
|
|
const payload = {
|
|
title: metadata.title,
|
|
description: metadata.description,
|
|
year: metadata.year,
|
|
name: metadata.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');
|
|
}
|
|
|
|
Swal.fire({ icon: 'success', title: 'Gruppe erfolgreich aktualisiert', timer: 1500, showConfirmButton: false });
|
|
navigate('/moderation');
|
|
} catch (e) {
|
|
console.error(e);
|
|
Swal.fire({ icon: 'error', title: 'Fehler beim Speichern', text: 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
|
|
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);
|
|
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));
|
|
};
|
|
|
|
// Note: approve/delete group actions are intentionally removed from this page
|
|
|
|
if (loading) return <div className="moderation-loading">Lade Gruppe...</div>;
|
|
if (error) return <div className="moderation-error">{error}</div>;
|
|
if (!group) return <div className="moderation-error">Gruppe nicht gefunden</div>;
|
|
|
|
return (
|
|
<div className="allContainer">
|
|
<Navbar />
|
|
|
|
<Container maxWidth="lg" className="page-container">
|
|
<ImageGallery
|
|
items={selectedImages}
|
|
onDelete={handleRemoveImage}
|
|
mode="preview"
|
|
showActions={true}
|
|
/>
|
|
|
|
{selectedImages.length > 0 && (
|
|
<>
|
|
<DescriptionInput metadata={metadata} onMetadataChange={setMetadata} />
|
|
|
|
<div className="action-buttons">
|
|
<Button className="btn btn-secondary" onClick={() => navigate('/moderation')}>↩ Zurück</Button>
|
|
<Button className="primary-button" onClick={handleSave} disabled={saving}>{saving ? 'Speichern...' : 'Speichern'}</Button>
|
|
</div>
|
|
</>
|
|
)}
|
|
|
|
</Container>
|
|
|
|
<div className="footerContainer"><Footer /></div>
|
|
</div>
|
|
);
|
|
|
|
};
|
|
|
|
export default ModerationGroupImagesPage;
|