146 lines
5.0 KiB
React
146 lines
5.0 KiB
React
|
|
import React from 'react'
|
||
|
|
|
||
|
|
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 (
|
||
|
|
<div style={{ marginBottom: '12px' }}>
|
||
|
|
{/* Focus Area Header */}
|
||
|
|
<div
|
||
|
|
onClick={() => 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
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
<span
|
||
|
|
onClick={(e) => { e.stopPropagation(); onToggle(nodeId) }}
|
||
|
|
style={{ marginRight: '8px', cursor: 'pointer', fontSize: '18px' }}
|
||
|
|
>
|
||
|
|
{isExpanded ? '▼' : '▶'}
|
||
|
|
</span>
|
||
|
|
<span style={{ marginRight: '8px' }}>{focusArea.icon}</span>
|
||
|
|
<span>{focusArea.name}</span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Children: Style Directions + Training Types */}
|
||
|
|
{isExpanded && (
|
||
|
|
<div style={{ marginLeft: '28px', marginTop: '8px' }}>
|
||
|
|
{/* Style Directions Section */}
|
||
|
|
<div style={{ marginBottom: '12px' }}>
|
||
|
|
<div style={{ fontSize: '12px', color: 'var(--text3)', marginBottom: '6px', textTransform: 'uppercase', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||
|
|
<span>Stilrichtungen</span>
|
||
|
|
<button
|
||
|
|
className="btn"
|
||
|
|
style={{ fontSize: '11px', padding: '4px 8px' }}
|
||
|
|
onClick={(e) => {
|
||
|
|
e.stopPropagation()
|
||
|
|
onSelect({ _createType: 'style_direction', focus_area_id: focusArea.id, focus_area_name: focusArea.name }, 'create_style_direction')
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
+ Neu
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
{focusArea.style_directions && focusArea.style_directions.map(sd => (
|
||
|
|
<StyleDirectionNode
|
||
|
|
key={sd.id}
|
||
|
|
styleDirection={sd}
|
||
|
|
onSelect={onSelect}
|
||
|
|
isSelected={selectedType === 'style_direction' && selectedId === sd.id}
|
||
|
|
/>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Training Types Section */}
|
||
|
|
<div>
|
||
|
|
<div style={{ fontSize: '12px', color: 'var(--text3)', marginBottom: '6px', textTransform: 'uppercase', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||
|
|
<span>Trainingstypen</span>
|
||
|
|
<button
|
||
|
|
className="btn"
|
||
|
|
style={{ fontSize: '11px', padding: '4px 8px' }}
|
||
|
|
onClick={(e) => {
|
||
|
|
e.stopPropagation()
|
||
|
|
onSelect({ _createType: 'training_type', focus_area_id: focusArea.id, focus_area_name: focusArea.name }, 'create_training_type')
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
+ Neu
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
{focusArea.training_types && focusArea.training_types.map(tt => (
|
||
|
|
<TrainingTypeNode
|
||
|
|
key={tt.id}
|
||
|
|
trainingType={tt}
|
||
|
|
onSelect={onSelect}
|
||
|
|
isSelected={selectedType === 'training_type' && selectedId === tt.id}
|
||
|
|
/>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
)
|
||
|
|
}
|
||
|
|
|
||
|
|
function StyleDirectionNode({ styleDirection, onSelect, isSelected }) {
|
||
|
|
return (
|
||
|
|
<div
|
||
|
|
onClick={() => 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 && (
|
||
|
|
<span style={{ marginLeft: '8px', opacity: 0.7, fontSize: '12px' }}>
|
||
|
|
({styleDirection.abbreviation})
|
||
|
|
</span>
|
||
|
|
)}
|
||
|
|
{styleDirection.target_groups && styleDirection.target_groups.length > 0 && (
|
||
|
|
<div style={{ fontSize: '11px', opacity: 0.8, marginTop: '4px' }}>
|
||
|
|
Zielgruppen: {styleDirection.target_groups.map(tg => tg.name).join(', ')}
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
)
|
||
|
|
}
|
||
|
|
|
||
|
|
function TrainingTypeNode({ trainingType, onSelect, isSelected }) {
|
||
|
|
return (
|
||
|
|
<div
|
||
|
|
onClick={() => 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 && (
|
||
|
|
<span style={{ marginLeft: '8px', opacity: 0.7, fontSize: '12px' }}>
|
||
|
|
({trainingType.abbreviation})
|
||
|
|
</span>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
)
|
||
|
|
}
|
||
|
|
|
||
|
|
export default FocusAreaNode
|