import React, { useState, useEffect, useCallback } from 'react'; import { API, APIHandler, Validators } from './api.js'; const CategoryManager = () => { const [categories, setCategories] = useState([]); const [newCategoryName, setNewCategoryName] = useState(''); const [loading, setLoading] = useState(true); useEffect(() => { fetchCategories(); }, []); const fetchCategories = async () => { try { const response = await fetch('/backend/categories'); const data = await response.json(); setCategories(data); } catch (error) { console.error('Fehler beim Laden der Kategorien:', error); } finally { setLoading(false); } }; const handleSubmit = async (e) => { e.preventDefault(); try { const response = await fetch('/create_category', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: `name=${encodeURIComponent(newCategoryName)}` }); if (!response.ok) throw new Error('Fehler beim Erstellen'); await fetchCategories(); setNewCategoryName(''); } catch (error) { console.error('Fehler:', error); } }; const handleDelete = async (id) => { if (!confirm('Kategorie wirklich löschen?')) return; try { const response = await fetch(`/delete_category/${id}`, { method: 'DELETE' }); if (!response.ok) throw new Error('Fehler beim Löschen'); await fetchCategories(); } catch (error) { console.error('Fehler:', error); } }; if (loading) return
Lade...
; return (
setNewCategoryName(e.target.value)} placeholder="Neue Kategorie" required />
{categories.map(category => ( ))}
Name Bilder Aktionen
{category.name} {category.imageCount}
); }; export default CategoryManager; ### END: category-manager.txt ### START: category-manager-updated.txt import React, { useState, useEffect, useCallback } from 'react'; import { API, APIHandler, Validators } from '@/api'; const CategoryManager = () => { const [categories, setCategories] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [selectedCategories, setSelectedCategories] = useState(new Set()); const [editingCategory, setEditingCategory] = useState(null); const [searchTerm, setSearchTerm] = useState(''); const [sortConfig, setSortConfig] = useState({ key: 'name', direction: 'asc' }); // Daten laden const fetchCategories = useCallback(async () => { try { setLoading(true); const data = await APIHandler.get(API.categories.list); setCategories(data); setError(null); } catch (err) { setError('Fehler beim Laden der Kategorien: ' + err.message); console.error('Fetch error:', err); } finally { setLoading(false); } }, []); useEffect(() => { fetchCategories(); }, [fetchCategories]); // Sortierung const sortedCategories = React.useMemo(() => { const sorted = [...categories]; sorted.sort((a, b) => { if (sortConfig.key === 'imageCount') { return sortConfig.direction === 'asc' ? a.imageCount - b.imageCount : b.imageCount - a.imageCount; } return sortConfig.direction === 'asc' ? a[sortConfig.key].localeCompare(b[sortConfig.key]) : b[sortConfig.key].localeCompare(a[sortConfig.key]); }); return sorted; }, [categories, sortConfig]); // Kategorie erstellen const handleCreate = async (name) => { try { //const validationErrors = Validators.category({ name }); //if (Object.keys(validationErrors).length > 0) { // throw new Error(Object.values(validationErrors).join(', ')); //} await APIHandler.post(API.categories.create, { name }); await fetchCategories(); window.showToast('Erfolg', 'Kategorie wurde erstellt', 'success'); } catch (err) { window.showToast('Fehler', err.message, 'danger'); } }; // Kategorie löschen const handleDelete = async (id) => { if (!window.confirm('Möchten Sie diese Kategorie wirklich löschen?')) return; try { await APIHandler.delete(API.categories.delete(id)); await fetchCategories(); window.showToast('Erfolg', 'Kategorie wurde gelöscht', 'success'); } catch (err) { window.showToast('Fehler', err.message, 'danger'); } }; // Kategorie aktualisieren const handleUpdate = async (id, updates) => { try { //const validationErrors = Validators.category(updates); //if (Object.keys(validationErrors).length > 0) { // throw new Error(Object.values(validationErrors).join(', ')); //} await APIHandler.put(API.categories.update(id), updates); await fetchCategories(); setEditingCategory(null); window.showToast('Erfolg', 'Kategorie wurde aktualisiert', 'success'); } catch (err) { window.showToast('Fehler', err.message, 'danger'); } }; // Massenbearbeitung const handleBulkDelete = async () => { if (selectedCategories.size === 0) return; if (!window.confirm(`Möchten Sie ${selectedCategories.size} Kategorien wirklich löschen?`)) return; try { await Promise.all( Array.from(selectedCategories).map(id => APIHandler.delete(API.categories.delete(id)) ) ); setSelectedCategories(new Set()); await fetchCategories(); window.showToast('Erfolg', 'Ausgewählte Kategorien wurden gelöscht', 'success'); } catch (err) { window.showToast('Fehler', err.message, 'danger'); } }; // Massenbearbeitung - Kategorien zusammenführen const handleMergeCategories = async (targetId) => { const selectedIds = Array.from(selectedCategories); if (selectedIds.length < 2) return; try { await APIHandler.post(API.categories.merge, { targetId, sourceIds: selectedIds.filter(id => id !== targetId) }); setSelectedCategories(new Set()); await fetchCategories(); window.showToast('Erfolg', 'Kategorien wurden zusammengeführt', 'success'); } catch (err) { window.showToast('Fehler', err.message, 'danger'); } }; const handleSelectAll = (event) => { if (event.target.checked) { setSelectedCategories(new Set(categories.map(cat => cat.id))); } else { setSelectedCategories(new Set()); } }; const handleSort = (key) => { setSortConfig(current => ({ key, direction: current.key === key && current.direction === 'asc' ? 'desc' : 'asc' })); }; if (loading) { return (
Laden...
); } if (error) { return (

Fehler

{error}

); } // Filtern basierend auf Suchbegriff const filteredCategories = searchTerm ? sortedCategories.filter(cat => cat.name.toLowerCase().includes(searchTerm.toLowerCase()) ) : sortedCategories; return (
{/* Toolbar */}
{selectedCategories.size > 0 && (
{selectedCategories.size > 1 && ( )}
)}
setSearchTerm(e.target.value)} />
{/* Kategorie Liste */}
{filteredCategories.map(category => ( ))}
handleSort('name')} > Name {sortConfig.key === 'name' && ( )} handleSort('imageCount')} > Bilder {sortConfig.key === 'imageCount' && ( )} Aktionen
{ const newSelected = new Set(selectedCategories); if (e.target.checked) { newSelected.add(category.id); } else { newSelected.delete(category.id); } setSelectedCategories(newSelected); }} /> {editingCategory?.id === category.id ? ( setEditingCategory({ ...editingCategory, name: e.target.value })} onKeyPress={(e) => { if (e.key === 'Enter') { handleUpdate(category.id, editingCategory); } }} /> ) : category.name} {category.imageCount}
{editingCategory?.id === category.id ? ( <> ) : ( <> )}
{filteredCategories.length === 0 && (

{searchTerm ? 'Keine Kategorien gefunden' : 'Keine Kategorien vorhanden'}

)}
); }; export default CategoryManager; window.CategoryManager = CategoryManager; // <-- Diese Zeile ans Ende setzen