Some checks failed
Deploy Development / deploy (push) Successful in 35s
Test Suite / pytest-backend (push) Successful in 6s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 6s
Test Suite / playwright-tests (push) Failing after 29s
- Added new CSS styles for Skills and Exercises pages, improving layout and responsiveness. - Refactored components to utilize new styles, enhancing visual consistency and user experience. - Implemented horizontal scrollable navigation for exercises and skills tabs, improving usability on smaller screens. - Updated button styles and introduced new class names for better maintainability and accessibility. - Enhanced loading states and empty messages for improved user feedback during data fetching.
154 lines
5.0 KiB
JavaScript
154 lines
5.0 KiB
JavaScript
import React from 'react'
|
|
import { ChevronDown, ChevronRight } from 'lucide-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 className="focus-tree-root">
|
|
<div className={'focus-tree-header' + (isSelected ? ' focus-tree-header--selected' : '')}>
|
|
<button
|
|
type="button"
|
|
className="focus-tree-toggle"
|
|
aria-expanded={isExpanded}
|
|
aria-label={isExpanded ? 'Bereich einklappen' : 'Bereich aufklappen'}
|
|
onClick={(e) => {
|
|
e.stopPropagation()
|
|
onToggle(nodeId)
|
|
}}
|
|
>
|
|
{isExpanded ? (
|
|
<ChevronDown size={18} strokeWidth={2} aria-hidden />
|
|
) : (
|
|
<ChevronRight size={18} strokeWidth={2} aria-hidden />
|
|
)}
|
|
</button>
|
|
<button
|
|
type="button"
|
|
className="focus-tree-header__label"
|
|
onClick={() => onSelect(focusArea, 'focus_area')}
|
|
>
|
|
{focusArea.icon ? (
|
|
<span className="focus-tree-emoji" aria-hidden>
|
|
{focusArea.icon}
|
|
</span>
|
|
) : null}
|
|
<span>{focusArea.name}</span>
|
|
</button>
|
|
</div>
|
|
|
|
{isExpanded && (
|
|
<div className="focus-tree-children">
|
|
<div className="focus-tree-group">
|
|
<div className="focus-tree-group__head">
|
|
<span>Stilrichtungen</span>
|
|
<button
|
|
type="button"
|
|
className="btn btn-secondary btn-tiny focus-tree-add-btn"
|
|
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>
|
|
|
|
<div className="focus-tree-group">
|
|
<div className="focus-tree-group__head">
|
|
<span>Trainingstypen</span>
|
|
<button
|
|
type="button"
|
|
className="btn btn-secondary btn-tiny focus-tree-add-btn"
|
|
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
|
|
role="button"
|
|
tabIndex={0}
|
|
className={'focus-tree-item' + (isSelected ? ' focus-tree-item--selected' : '')}
|
|
onClick={() => onSelect(styleDirection, 'style_direction')}
|
|
onKeyDown={(e) => {
|
|
if (e.key === 'Enter' || e.key === ' ') {
|
|
e.preventDefault()
|
|
onSelect(styleDirection, 'style_direction')
|
|
}
|
|
}}
|
|
>
|
|
{styleDirection.name}
|
|
{styleDirection.abbreviation ? (
|
|
<span className="focus-tree-item__abbr">({styleDirection.abbreviation})</span>
|
|
) : null}
|
|
{styleDirection.target_groups && styleDirection.target_groups.length > 0 ? (
|
|
<div className="focus-tree-item__meta">
|
|
Zielgruppen: {styleDirection.target_groups.map((tg) => tg.name).join(', ')}
|
|
</div>
|
|
) : null}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function TrainingTypeNode({ trainingType, onSelect, isSelected }) {
|
|
return (
|
|
<div
|
|
role="button"
|
|
tabIndex={0}
|
|
className={'focus-tree-item' + (isSelected ? ' focus-tree-item--selected' : '')}
|
|
onClick={() => onSelect(trainingType, 'training_type')}
|
|
onKeyDown={(e) => {
|
|
if (e.key === 'Enter' || e.key === ' ') {
|
|
e.preventDefault()
|
|
onSelect(trainingType, 'training_type')
|
|
}
|
|
}}
|
|
>
|
|
{trainingType.name}
|
|
{trainingType.abbreviation ? (
|
|
<span className="focus-tree-item__abbr">({trainingType.abbreviation})</span>
|
|
) : null}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default FocusAreaNode |