import React, { useState, useEffect } from 'react'
import { api } from '../utils/api'
/**
* AdminHierarchyPage - Hierarchische Katalog-Verwaltung
*
* Layout:
* ┌─ Tree (links) ─────────┬─ Detail Panel (rechts) ─┐
* │ ▼ Fokusbereich │ │
* │ ├─ Stilrichtungen │ [Edit-Form für │
* │ └─ Trainingstypen │ ausgewähltes Element] │
* └────────────────────────┴──────────────────────────┘
*/
function AdminHierarchyPage() {
const [hierarchy, setHierarchy] = useState([])
const [expandedNodes, setExpandedNodes] = useState(new Set())
const [selectedItem, setSelectedItem] = useState(null)
const [loading, setLoading] = useState(false)
const [error, setError] = useState('')
useEffect(() => {
loadHierarchy()
}, [])
async function loadHierarchy() {
setLoading(true)
setError('')
try {
const data = await api.getAdminHierarchy()
setHierarchy(data)
// Auto-expand first focus area
if (data.length > 0) {
setExpandedNodes(new Set([`fa-${data[0].id}`]))
}
} catch (e) {
setError(e.message)
} finally {
setLoading(false)
}
}
function toggleNode(nodeId) {
const newExpanded = new Set(expandedNodes)
if (newExpanded.has(nodeId)) {
newExpanded.delete(nodeId)
} else {
newExpanded.add(nodeId)
}
setExpandedNodes(newExpanded)
}
function selectItem(item, type) {
setSelectedItem({ ...item, _type: type })
}
if (loading && hierarchy.length === 0) {
return (
)
}
return (
{/* Left: Tree View */}
Katalog-Hierarchie
{error &&
{error}
}
{hierarchy.map(fa => (
))}
{/* Right: Detail Panel */}
{selectedItem ? (
) : (
← Wähle ein Element aus dem Baum
)}
)
}
// ============================================================================
// Tree Node Components
// ============================================================================
function FocusAreaNode({ focusArea, expanded, onToggle, onSelect, selectedId, selectedType }) {
const nodeId = `fa-${focusArea.id}`
const isExpanded = expanded.has(nodeId)
const isSelected = selectedType === 'focus_area' && selectedId === focusArea.id
return (
{/* Focus Area Header */}
onSelect(focusArea, 'focus_area')}
style={{
display: 'flex',
alignItems: 'center',
padding: '8px 12px',
borderRadius: '8px',
cursor: 'pointer',
background: isSelected ? 'var(--accent)' : 'transparent',
color: isSelected ? 'white' : 'var(--text1)',
fontWeight: 600
}}
>
{ e.stopPropagation(); onToggle(nodeId) }}
style={{ marginRight: '8px', cursor: 'pointer', fontSize: '18px' }}
>
{isExpanded ? '▼' : '▶'}
{focusArea.icon}
{focusArea.name}
{/* Children: Style Directions + Training Types */}
{isExpanded && (
{/* Style Directions Section */}
{focusArea.style_directions && focusArea.style_directions.length > 0 && (
Stilrichtungen
{focusArea.style_directions.map(sd => (
))}
)}
{/* Training Types Section */}
{focusArea.training_types && focusArea.training_types.length > 0 && (
Trainingstypen
{focusArea.training_types.map(tt => (
))}
)}
)}
)
}
function StyleDirectionNode({ styleDirection, onSelect, isSelected }) {
return (
onSelect(styleDirection, 'style_direction')}
style={{
padding: '6px 12px',
marginBottom: '4px',
borderRadius: '6px',
cursor: 'pointer',
background: isSelected ? 'var(--accent)' : 'var(--surface2)',
color: isSelected ? 'white' : 'var(--text1)',
fontSize: '14px'
}}
>
{styleDirection.name}
{styleDirection.abbreviation && (
({styleDirection.abbreviation})
)}
{styleDirection.target_groups && styleDirection.target_groups.length > 0 && (
Zielgruppen: {styleDirection.target_groups.map(tg => tg.name).join(', ')}
)}
)
}
function TrainingTypeNode({ trainingType, onSelect, isSelected }) {
return (
onSelect(trainingType, 'training_type')}
style={{
padding: '6px 12px',
marginBottom: '4px',
borderRadius: '6px',
cursor: 'pointer',
background: isSelected ? 'var(--accent)' : 'var(--surface2)',
color: isSelected ? 'white' : 'var(--text1)',
fontSize: '14px'
}}
>
{trainingType.name}
{trainingType.abbreviation && (
({trainingType.abbreviation})
)}
)
}
// ============================================================================
// Detail Panel (Edit Forms)
// ============================================================================
function DetailPanel({ item, onUpdate }) {
if (!item) return null
const type = item._type
if (type === 'focus_area') {
return
} else if (type === 'style_direction') {
return
} else if (type === 'training_type') {
return
}
return null
}
function FocusAreaDetail({ focusArea, onUpdate }) {
const [editing, setEditing] = useState(false)
const [form, setForm] = useState({
name: focusArea.name,
icon: focusArea.icon,
description: focusArea.description || ''
})
async function handleSave() {
try {
await api.updateFocusArea(focusArea.id, form)
setEditing(false)
onUpdate()
} catch (e) {
alert('Fehler: ' + e.message)
}
}
if (!editing) {
return (
{focusArea.icon} {focusArea.name}
{focusArea.description &&
{focusArea.description}
}
)
}
return (
)
}
function StyleDirectionDetail({ styleDirection, onUpdate }) {
return (
{styleDirection.name}
{styleDirection.abbreviation &&
Kürzel: {styleDirection.abbreviation}
}
{styleDirection.description &&
{styleDirection.description}
}
{styleDirection.target_groups && styleDirection.target_groups.length > 0 && (
Zugeordnete Zielgruppen
{styleDirection.target_groups.map(tg => (
-
{tg.name} {tg.is_primary && ★ Primär}
))}
)}
)
}
function TrainingTypeDetail({ trainingType, onUpdate }) {
return (
{trainingType.name}
{trainingType.abbreviation &&
Kürzel: {trainingType.abbreviation}
}
{trainingType.description &&
{trainingType.description}
}
)
}
export default AdminHierarchyPage