import React, { useEffect, useState, useRef } from 'react'; import { Card, Row, Col, Spin, Alert, Image, Layout, Switch, App } from 'antd'; import { LoadingOutlined } from '@ant-design/icons'; import { Link } from 'react-router-dom'; import { getClientProjects } from '../services/projectService'; import UserMenu from '../components/UserMenu'; import Sortable from 'sortablejs'; const backendUrl = process.env.REACT_APP_BACKEND || ''; export default function ClientProjects({ user, darkMode, onLogout, onToggleDarkMode }) { const { message } = App.useApp(); const [projects, setProjects] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [sortMode, setSortMode] = useState(false); const [saving, setSaving] = useState(false); const normalProjectsRef = useRef(null); const heProjectsRef = useRef(null); const normalSortableRef = useRef(null); const heSortableRef = useRef(null); useEffect(() => { const token = localStorage.getItem('jwt'); getClientProjects(token).then(res => { if (res.success) { setProjects(res.projects); } else { setError(res.error?.message || 'Fehler beim Laden der Projekte'); } }).catch(() => setError('Serverfehler')).finally(() => setLoading(false)); }, []); // SortableJS initialisieren/zerstören useEffect(() => { if (sortMode && normalProjectsRef.current && heProjectsRef.current) { // Zerstöre bestehende Instanzen falls vorhanden if (normalSortableRef.current) { try { normalSortableRef.current.destroy(); } catch (e) { // Ignore cleanup errors } normalSortableRef.current = null; } if (heSortableRef.current) { try { heSortableRef.current.destroy(); } catch (e) { // Ignore cleanup errors } heSortableRef.current = null; } // Initialisiere SortableJS für beide Bereiche normalSortableRef.current = Sortable.create(normalProjectsRef.current, { animation: 150, ghostClass: 'sortable-ghost', chosenClass: 'sortable-chosen', dragClass: 'sortable-drag', onEnd: handleSortEnd }); heSortableRef.current = Sortable.create(heProjectsRef.current, { animation: 150, ghostClass: 'sortable-ghost', chosenClass: 'sortable-chosen', dragClass: 'sortable-drag', onEnd: handleSortEnd }); } else { // Zerstöre SortableJS Instanzen sicher if (normalSortableRef.current) { try { normalSortableRef.current.destroy(); } catch (e) { // Ignore cleanup errors } normalSortableRef.current = null; } if (heSortableRef.current) { try { heSortableRef.current.destroy(); } catch (e) { // Ignore cleanup errors } heSortableRef.current = null; } } return () => { // Cleanup beim Unmount if (normalSortableRef.current) { try { normalSortableRef.current.destroy(); } catch (e) { // Ignore cleanup errors } normalSortableRef.current = null; } if (heSortableRef.current) { try { heSortableRef.current.destroy(); } catch (e) { // Ignore cleanup errors } heSortableRef.current = null; } }; }, [sortMode]); const handleSortEnd = async (evt) => { // Neue Reihenfolge aus DOM extrahieren const newOrder = []; // Normale Projekte if (normalProjectsRef.current) { const normalElements = normalProjectsRef.current.children; for (let el of normalElements) { const projectName = el.getAttribute('data-project-name'); if (projectName) newOrder.push(projectName); } } // HE Projekte if (heProjectsRef.current) { const heElements = heProjectsRef.current.children; for (let el of heElements) { const projectName = el.getAttribute('data-project-name'); if (projectName) newOrder.push(projectName); } } // Speichere neue Reihenfolge await saveProjectOrder(newOrder); }; const saveProjectOrder = async (order) => { setSaving(true); try { const token = localStorage.getItem('jwt'); const response = await fetch(`${backendUrl}/api/projects/${encodeURIComponent(user?.dir)}/project-order`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify({ order }) }); const result = await response.json(); if (result.success) { message.success('Reihenfolge gespeichert'); // Projekte neu laden um die neue Reihenfolge zu übernehmen const token = localStorage.getItem('jwt'); const res = await getClientProjects(token); if (res.success) { setProjects(res.projects); } } else { message.error('Fehler beim Speichern: ' + (result.error?.message || 'Unbekannter Fehler')); } } catch (err) { message.error('Netzwerkfehler beim Speichern'); } finally { setSaving(false); } }; if (loading) { return } size="large" tip="Lade Projekte..." fullscreen />; } if (error) { return ; } // Projekte trennen const normalProjects = projects.filter(p => !p.isHE); const heProjects = projects.filter(p => p.isHE); return (
Übersicht

Cinema

{normalProjects.length === 0 && } {normalProjects.map((p, i) => { return ( {p.poster && ( Poster )} ); })}

Home Entertainment

{heProjects.length === 0 && } {heProjects.map((p, i) => { return ( {p.poster && ( Poster )} ); })}
); }