From 80986735b5b0bae24f72e44f2bbb67a259806d36 Mon Sep 17 00:00:00 2001 From: Lars Date: Fri, 24 Apr 2026 08:39:48 +0200 Subject: [PATCH] feat: complete admin hierarchy - create + reassign functions Create Functions: - '+ Neu' buttons for Style Directions and Training Types - Create forms with focus area context - Auto-assigns to parent focus area - Loading states + validation Reassignment: - Focus area dropdown in edit forms - Move style directions between focus areas - Move training types between focus areas - Updates hierarchy immediately after save Full CRUD now complete: - Create: new elements under focus area - Read: tree view with nested elements - Update: edit + reassign to different focus area - Delete: with confirmation dialogs Mobile + Desktop responsive design maintained. Co-Authored-By: Claude Sonnet 4.5 --- frontend/src/pages/AdminHierarchyPage.jsx | 254 +++++++++++++++++++--- 1 file changed, 220 insertions(+), 34 deletions(-) diff --git a/frontend/src/pages/AdminHierarchyPage.jsx b/frontend/src/pages/AdminHierarchyPage.jsx index 54e424f..ebdf7de 100644 --- a/frontend/src/pages/AdminHierarchyPage.jsx +++ b/frontend/src/pages/AdminHierarchyPage.jsx @@ -136,7 +136,7 @@ function AdminHierarchyPage() { > ← Zurück zur Übersicht - + )} @@ -183,38 +183,54 @@ function FocusAreaNode({ focusArea, expanded, onToggle, onSelect, selectedId, se {isExpanded && (
{/* Style Directions Section */} - {focusArea.style_directions && focusArea.style_directions.length > 0 && ( -
-
- Stilrichtungen -
- {focusArea.style_directions.map(sd => ( - - ))} +
+
+ Stilrichtungen +
- )} + {focusArea.style_directions && focusArea.style_directions.map(sd => ( + + ))} +
{/* Training Types Section */} - {focusArea.training_types && focusArea.training_types.length > 0 && ( -
-
- Trainingstypen -
- {focusArea.training_types.map(tt => ( - - ))} +
+
+ Trainingstypen +
- )} + {focusArea.training_types && focusArea.training_types.map(tt => ( + + ))} +
)}
@@ -278,7 +294,7 @@ function TrainingTypeNode({ trainingType, onSelect, isSelected }) { // Detail Panel (Edit Forms) // ============================================================================ -function DetailPanel({ item, onUpdate }) { +function DetailPanel({ item, onUpdate, focusAreas }) { if (!item) return null const type = item._type @@ -286,9 +302,13 @@ function DetailPanel({ item, onUpdate }) { if (type === 'focus_area') { return } else if (type === 'style_direction') { - return + return } else if (type === 'training_type') { - return + return + } else if (type === 'create_style_direction') { + return + } else if (type === 'create_training_type') { + return } return null @@ -360,7 +380,7 @@ function FocusAreaDetail({ focusArea, onUpdate }) { ) } -function StyleDirectionDetail({ styleDirection, onUpdate }) { +function StyleDirectionDetail({ styleDirection, onUpdate, focusAreas }) { const [editing, setEditing] = useState(false) const [form, setForm] = useState({ name: styleDirection.name, @@ -445,6 +465,19 @@ function StyleDirectionDetail({ styleDirection, onUpdate }) { rows={4} />
+
+ + +
@@ -453,7 +486,7 @@ function StyleDirectionDetail({ styleDirection, onUpdate }) { ) } -function TrainingTypeDetail({ trainingType, onUpdate }) { +function TrainingTypeDetail({ trainingType, onUpdate, focusAreas }) { const [editing, setEditing] = useState(false) const [form, setForm] = useState({ name: trainingType.name, @@ -525,6 +558,19 @@ function TrainingTypeDetail({ trainingType, onUpdate }) { rows={4} />
+
+ + +
@@ -533,4 +579,144 @@ function TrainingTypeDetail({ trainingType, onUpdate }) { ) } +// ============================================================================ +// Create Forms +// ============================================================================ + +function CreateStyleDirectionForm({ context, onUpdate }) { + const [form, setForm] = useState({ + name: '', + abbreviation: '', + description: '', + focus_area_id: context.focus_area_id + }) + const [loading, setLoading] = useState(false) + + async function handleCreate() { + if (!form.name) { + alert('Name ist erforderlich') + return + } + setLoading(true) + try { + await api.createStyleDirection(form) + onUpdate() + } catch (e) { + alert('Fehler: ' + e.message) + } finally { + setLoading(false) + } + } + + return ( +
+

Neue Stilrichtung für {context.focus_area_name}

+
+ + setForm({ ...form, name: e.target.value })} + placeholder="z.B. Shotokan" + /> +
+
+ + setForm({ ...form, abbreviation: e.target.value })} + placeholder="z.B. SKA" + /> +
+
+ +