import React, { useEffect, useState } from 'react'
import { Link, useNavigate, useParams } from 'react-router-dom'
import api from '../utils/api'
import { sanitizeTrainerHtml } from '../utils/htmlUtils'
const API_BASE = (import.meta.env.VITE_API_URL || '').replace(/\/$/, '')
function resolveMediaUrl(filePath) {
if (!filePath) return null
if (filePath.startsWith('http://') || filePath.startsWith('https://')) return filePath
const p = filePath.startsWith('/') ? filePath : `/${filePath}`
return `${API_BASE}${p}`
}
function HtmlBlock({ html, className = '' }) {
if (!html || !String(html).trim()) return null
const safe = sanitizeTrainerHtml(html)
return (
)
}
function MediaBlock({ media }) {
if (media.embed_url) {
return (
)
}
const src = resolveMediaUrl(media.file_path)
if (!src) return null
if (media.media_type === 'image' || (media.mime_type && media.mime_type.startsWith('image/'))) {
return (
)
}
if (media.media_type === 'video' || (media.mime_type && media.mime_type.startsWith('video/'))) {
return
}
return (
{media.title || media.original_filename || 'Datei öffnen'}
)
}
function TagRow({ exercise }) {
const tags = []
;(exercise.focus_areas || []).forEach((f) => {
tags.push({ key: `fa-${f.id}`, label: f.name, accent: !!f.is_primary })
})
;(exercise.training_styles || []).forEach((t) => {
tags.push({ key: `ts-${t.id}`, label: t.name, accent: false })
})
;(exercise.training_types || []).forEach((t) => {
tags.push({ key: `tt-${t.id}`, label: t.name, accent: false })
})
;(exercise.target_groups || []).forEach((g) => {
tags.push({ key: `tg-${g.id}`, label: g.name, accent: !!g.is_primary })
})
if (tags.length === 0) return null
return (
{tags.map((t) => (
{t.label}
))}
)
}
function metaParts(exercise) {
const parts = []
if (exercise.duration_min != null || exercise.duration_max != null) {
const a = exercise.duration_min
const b = exercise.duration_max
if (a != null && b != null && a !== b) parts.push(`${a}–${b} Min.`)
else if (a != null) parts.push(`ca. ${a} Min.`)
else if (b != null) parts.push(`ca. ${b} Min.`)
}
if (exercise.group_size_min != null || exercise.group_size_max != null) {
const a = exercise.group_size_min
const b = exercise.group_size_max
if (a != null && b != null && a !== b) parts.push(`Gruppe ${a}–${b}`)
else if (a != null) parts.push(`Gruppe ab ${a}`)
else if (b != null) parts.push(`Gruppe bis ${b}`)
}
return parts
}
function ExerciseDetailPage() {
const { id } = useParams()
const navigate = useNavigate()
const [exercise, setExercise] = useState(null)
const [error, setError] = useState(null)
const [loading, setLoading] = useState(true)
useEffect(() => {
let cancelled = false
const load = async () => {
setLoading(true)
setError(null)
try {
const data = await api.getExercise(id)
if (!cancelled) setExercise(data)
} catch (err) {
if (!cancelled) setError(err)
} finally {
if (!cancelled) setLoading(false)
}
}
if (id) load()
return () => {
cancelled = true
}
}, [id])
if (loading) {
return (
)
}
if (error) {
const msg = error.message || String(error)
return (
Übung
{msg}
)
}
if (!exercise) return null
const meta = metaParts(exercise)
return (
Bearbeiten
{exercise.title}
{exercise.summary && (
)}
{exercise.visibility}
{exercise.status}
{exercise.club_name && {exercise.club_name}}
{meta.length > 0 &&
{meta.join(' · ')}
}
{exercise.goal && (
)}
{(exercise.equipment || []).length > 0 && (
Material & Aufbau
{exercise.equipment.map((x, i) => (
- {x}
))}
)}
{exercise.preparation && (
)}
{exercise.execution && (
)}
{(exercise.media || []).length > 0 && (
Medien
{exercise.media.map((m) => (
{m.title || m.original_filename || m.media_type}
{m.description &&
{m.description}
}
))}
)}
{exercise.trainer_notes && (
)}
{(exercise.skills || []).length > 0 && (
Fähigkeiten
{exercise.skills.map((s) => (
{s.skill_name}
{s.intensity ? ` · ${s.intensity}` : ''}
{s.required_level || s.target_level
? ` (${[s.required_level, s.target_level].filter(Boolean).join(' → ')})`
: ''}
))}
)}
{(exercise.variants || []).length > 0 && (
Varianten
{exercise.variants.map((v) => (
{v.variant_name}
{v.description &&
{v.description}
}
{v.execution_changes && (
)}
))}
)}
)
}
export default ExerciseDetailPage