Project-Image-Uploader/frontend/src/Components/Pages/ModerationGroupImagesPage.js
matthias.lotz 91d6d06687 feat: Enable drag-and-drop reordering in ModerationGroupImagesPage
- Added PUT /api/admin/groups/:groupId/reorder endpoint
- Implemented handleReorder in ModerationGroupImagesPage
- Uses adminRequest API with proper error handling
- Same mobile touch support as ManagementPortalPage
2025-11-27 20:09:08 +01:00

158 lines
5.3 KiB
JavaScript

import React, { useState, useEffect, useCallback } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
// Services
import { adminGet, adminRequest } from '../../services/adminApi';
import { handleAdminError } from '../../services/adminErrorHandler';
import AdminSessionGate from '../AdminAuth/AdminSessionGate.jsx';
import { useAdminSession } from '../../contexts/AdminSessionContext.jsx';
// Components
import Navbar from '../ComponentUtils/Headers/Navbar';
import Footer from '../ComponentUtils/Footer';
import ImageDescriptionManager from '../ComponentUtils/ImageDescriptionManager';
import GroupMetadataEditor from '../ComponentUtils/GroupMetadataEditor';
import Loading from '../ComponentUtils/LoadingAnimation/Loading';
// UI
import Swal from 'sweetalert2';
/**
* ModerationGroupImagesPage - Admin page for moderating group images
*
* Uses modular components:
* - ImageDescriptionManager: Edit image descriptions with batch save
* - GroupMetadataEditor: Edit group metadata with save/discard
*/
const ModerationGroupImagesPage = () => {
const { groupId } = useParams();
const navigate = useNavigate();
const [group, setGroup] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const { isAuthenticated } = useAdminSession();
const loadGroup = useCallback(async () => {
if (!isAuthenticated) {
return;
}
try {
setLoading(true);
const data = await adminGet(`/api/admin/groups/${groupId}`);
// Transform data similar to ManagementPortalPage
const transformedData = {
...data,
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) {
await handleAdminError(e, 'Gruppe laden');
setError('Fehler beim Laden der Gruppe');
} finally {
setLoading(false);
}
}, [groupId, isAuthenticated]);
useEffect(() => {
if (!isAuthenticated) {
return;
}
loadGroup();
}, [isAuthenticated, loadGroup]);
const handleReorder = async (newOrder) => {
if (!group || !groupId) {
console.error('No groupId available for reordering');
return;
}
try {
const imageIds = newOrder.map(img => img.id);
// Use admin API
await adminRequest(`/api/admin/groups/${groupId}/reorder`, 'PUT', {
imageIds: imageIds
});
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);
await handleAdminError(error, 'Reihenfolge speichern');
}
};
const renderContent = () => {
if (loading) return <Loading />;
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 />
<div className="container" style={{ minHeight: '80vh', paddingTop: '20px', paddingBottom: '40px' }}>
{/* Image Descriptions Manager */}
<ImageDescriptionManager
images={group.images}
groupId={groupId}
onRefresh={loadGroup}
mode="moderate"
enableReordering={true}
onReorder={handleReorder}
/>
{/* Group Metadata Editor */}
<GroupMetadataEditor
initialMetadata={group.metadata}
groupId={groupId}
onRefresh={loadGroup}
mode="moderate"
/>
{/* Back Button */}
<div className="flex-center mt-4">
<button
className="btn btn-secondary"
onClick={() => navigate('/moderation')}
>
Zurück zur Übersicht
</button>
</div>
</div>
<div className="footerContainer"><Footer /></div>
</div>
);
};
return (
<AdminSessionGate>
{renderContent()}
</AdminSessionGate>
);
};
export default ModerationGroupImagesPage;