shinkan-jinkendo/frontend/src/components/exercises/ExerciseSkillsEditor.jsx

135 lines
5.4 KiB
React
Raw Normal View History

import React from 'react'
import SkillTreeSelect from '../SkillTreeSelect'
import { skillCatalogPathLabel } from '../../utils/skillCatalogTree'
import { SKILL_LEVEL_OPTIONS } from '../../constants/skillLevels'
import {
EXERCISE_SKILL_INTENSITY_DEFAULT,
EXERCISE_SKILL_INTENSITY_OPTIONS,
normalizeExerciseSkillIntensity,
} from '../../constants/exerciseSkillIntensity'
export default function ExerciseSkillsEditor({
rows,
skillsCatalog,
skillPick,
onSkillPickChange,
onAdd,
onRemove,
onUpdateField,
}) {
return (
<div className="exercise-meta-block exercise-meta-block--skills">
<div className="exercise-meta-block__head">
<h4 className="exercise-meta-block__title">Fähigkeiten</h4>
</div>
<p className="exercise-meta-block__hint">Je Übung mehrere Fähigkeiten mit Intensität und Niveau (von–bis).</p>
<div className="exercise-skills-add">
<SkillTreeSelect
value={skillPick}
onChange={onSkillPickChange}
skills={skillsCatalog}
excludeIds={rows.map((s) => s.skill_id)}
placeholder="Fähigkeit wählen…"
/>
<button type="button" className="btn btn-secondary" onClick={onAdd}>
Hinzufügen
</button>
</div>
{rows.length === 0 ? (
<p className="exercise-meta-block__empty">Noch keine Fähigkeit zugeordnet.</p>
) : (
<ul className="exercise-skills-list">
{rows.map((row, idx) => {
const sk = skillsCatalog.find((s) => s.id === row.skill_id)
const intensity = normalizeExerciseSkillIntensity(row.intensity)
return (
<li key={`${row.skill_id}-${idx}`} className="exercise-skill-chip">
<div className="exercise-skill-chip__identity">
<span className="exercise-skill-chip__name">{sk?.name || `Skill #${row.skill_id}`}</span>
{sk ? (
<span className="exercise-skill-chip__path" title={skillCatalogPathLabel(sk)}>
{skillCatalogPathLabel(sk)}
</span>
) : null}
</div>
<div className="exercise-skill-chip__controls">
<div className="exercise-skill-chip__field">
<span className="exercise-skill-chip__caption">Intensität</span>
<div className="exercise-intensity-segment" role="radiogroup" aria-label="Intensität">
{EXERCISE_SKILL_INTENSITY_OPTIONS.map((o) => (
<button
key={o.value}
type="button"
role="radio"
aria-checked={intensity === o.value}
className={`exercise-intensity-segment__btn${
intensity === o.value ? ' exercise-intensity-segment__btn--active' : ''
}`}
onClick={() => onUpdateField(idx, 'intensity', o.value)}
>
{o.label}
</button>
))}
</div>
</div>
<div className="exercise-skill-chip__levels">
<label className="exercise-skill-chip__field">
<span className="exercise-skill-chip__caption">von</span>
<select
className="form-input exercise-skill-level-select"
value={row.required_level || ''}
title="Mindest-Niveau"
onChange={(e) => onUpdateField(idx, 'required_level', e.target.value)}
>
{SKILL_LEVEL_OPTIONS.map((o) => (
<option key={`r-${o.value}`} value={o.value} title={o.label}>
{o.level != null ? o.level : '–'}
</option>
))}
</select>
</label>
<span className="exercise-skill-chip__dash" aria-hidden>
→
</span>
<label className="exercise-skill-chip__field">
<span className="exercise-skill-chip__caption">bis</span>
<select
className="form-input exercise-skill-level-select"
value={row.target_level || ''}
title="Ziel-Niveau"
onChange={(e) => onUpdateField(idx, 'target_level', e.target.value)}
>
{SKILL_LEVEL_OPTIONS.map((o) => (
<option key={`t-${o.value}`} value={o.value} title={o.label}>
{o.level != null ? o.level : '–'}
</option>
))}
</select>
</label>
</div>
<button
type="button"
className="exercise-skill-chip__remove"
aria-label="Fähigkeit entfernen"
title="Entfernen"
onClick={() => onRemove(idx)}
>
✕
</button>
</div>
</li>
)
})}
</ul>
)}
</div>
)
}
export { EXERCISE_SKILL_INTENSITY_DEFAULT }