From f243b236be7dfe688f682e01a2718f0f9d453f23 Mon Sep 17 00:00:00 2001 From: Lars Date: Thu, 23 Apr 2026 10:55:35 +0200 Subject: [PATCH] feat: Phase C - Admin-UI M:N Hierarchie & Matrix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Frontend Admin-UI Refactoring (komplett): 1. Target Groups Tab überarbeitet (Global): - training_style_id Dropdown entfernt (Create + Edit) - Hierarchie-Anzeige entfernt (jetzt global unabhängig) - Nur noch Name, Beschreibung, Altersbereich 2. Neuer Tab 'Hierarchie' (Tree-View): - Fokusbereich → Trainingsstil → Zielgruppen - Expandable/Collapsible Nodes (▶/▼) - is_primary Kennzeichnung (★) - Hierarchische Darstellung mit Einrückung 3. Neuer Tab 'Zuordnungen' (M:N Matrix): - Checkbox-Matrix: Stile × Zielgruppen - Live-Toggle (Checkbox on/off) - Focus Area Kontext bei jedem Stil - is_primary Flag Anzeige (★) UX: - Tab-Reihenfolge: Fokusbereiche → Stile → Hierarchie → Zielgruppen → Zuordnungen - Responsive Tabelle mit Overflow-Scroll - Konsistente Card-basierte Layouts Version: 0.3.4 Page: AdminCatalogsPage 2.0.0 --- frontend/src/pages/AdminCatalogsPage.jsx | 297 +++++++++++++++++++---- frontend/src/version.js | 2 +- 2 files changed, 252 insertions(+), 47 deletions(-) diff --git a/frontend/src/pages/AdminCatalogsPage.jsx b/frontend/src/pages/AdminCatalogsPage.jsx index 3194d95..8bd78af 100644 --- a/frontend/src/pages/AdminCatalogsPage.jsx +++ b/frontend/src/pages/AdminCatalogsPage.jsx @@ -26,16 +26,24 @@ export default function AdminCatalogsPage() { const [editingSC, setEditingSC] = useState(null) const [newSC, setNewSC] = useState({ name: '', description: '', parent_category_id: null }) - // Target Groups + // Target Groups (Global - unabhängig von Stilen) const [targetGroups, setTargetGroups] = useState([]) const [editingTG, setEditingTG] = useState(null) - const [newTG, setNewTG] = useState({ name: '', description: '', training_style_id: null, min_age: null, max_age: null }) + const [newTG, setNewTG] = useState({ name: '', description: '', min_age: null, max_age: null }) // Trainer Focus Areas const [trainerAssignments, setTrainerAssignments] = useState([]) const [profiles, setProfiles] = useState([]) const [newAssignment, setNewAssignment] = useState({ profile_id: '', focus_area_id: '' }) + // Hierarchy (Tree-View) + const [hierarchyData, setHierarchyData] = useState([]) + const [expandedNodes, setExpandedNodes] = useState(new Set()) + + // M:N Assignment Matrix + const [assignments, setAssignments] = useState([]) + const [matrixLoading, setMatrixLoading] = useState(false) + useEffect(() => { loadData() }, [activeTab]) @@ -57,12 +65,8 @@ export default function AdminCatalogsPage() { const data = await api.listSkillCategories() setSkillCategories(data) } else if (activeTab === 'target-groups') { - const [groups, styles] = await Promise.all([ - api.listTargetGroups(), - api.listTrainingStyles() - ]) + const groups = await api.listTargetGroups() setTargetGroups(groups) - setTrainingStyles(styles) } else if (activeTab === 'trainer-assignments') { const [assignments, profs, areas] = await Promise.all([ api.listTrainerFocusAreas(), @@ -72,6 +76,18 @@ export default function AdminCatalogsPage() { setTrainerAssignments(assignments) setProfiles(profs) setFocusAreas(areas) + } else if (activeTab === 'hierarchy') { + const data = await api.getTrainingStylesHierarchy() + setHierarchyData(data) + } else if (activeTab === 'target-groups-matrix') { + const [styles, groups, assigns] = await Promise.all([ + api.listTrainingStyles(), + api.listTargetGroups(), + api.listTrainingStyleTargetGroups() + ]) + setTrainingStyles(styles) + setTargetGroups(groups) + setAssignments(assigns) } } catch (e) { setError(e.message) @@ -208,7 +224,7 @@ export default function AdminCatalogsPage() { async function createTargetGroup() { try { await api.createTargetGroup(newTG) - setNewTG({ name: '', description: '', training_style_id: null, min_age: null, max_age: null }) + setNewTG({ name: '', description: '', min_age: null, max_age: null }) loadData() } catch (e) { setError(e.message) @@ -265,9 +281,11 @@ export default function AdminCatalogsPage() { {[ { id: 'focus-areas', label: 'Fokusbereiche' }, { id: 'training-styles', label: 'Trainingsstile' }, + { id: 'hierarchy', label: 'Hierarchie' }, + { id: 'target-groups', label: 'Zielgruppen' }, + { id: 'target-groups-matrix', label: 'Zuordnungen' }, { id: 'training-characters', label: 'Trainingscharakter' }, { id: 'skill-categories', label: 'Fähigkeitskategorien' }, - { id: 'target-groups', label: 'Zielgruppen' }, { id: 'trainer-assignments', label: 'Trainer-Zuordnungen' } ].map(tab => (