shinkan-jinkendo/frontend/src/pages/TrainingFrameworkProgramsListPage.jsx

167 lines
5.8 KiB
React
Raw Normal View History

import React, { useCallback, useEffect, useState } from 'react'
import { Link } from 'react-router-dom'
import api from '../utils/api'
const TYPE_COUNT = (r) =>
typeof r.training_types_count === 'number' ? r.training_types_count : null
const TG_COUNT = (r) =>
typeof r.target_groups_count === 'number' ? r.target_groups_count : null
function contextTeaser(r) {
const bits = []
if (r.focus_area_name) bits.push(r.focus_area_name)
if (r.style_direction_name) bits.push(r.style_direction_name)
const tn = TYPE_COUNT(r)
const gn = TG_COUNT(r)
if (tn != null && tn > 0) bits.push(`${tn} Trainingsart${tn === 1 ? '' : 'en'}`)
if (gn != null && gn > 0) bits.push(`${gn} Zielgruppe${gn === 1 ? '' : 'n'}`)
return bits.length ? bits.join(' · ') : null
}
export default function TrainingFrameworkProgramsListPage() {
const [rows, setRows] = useState([])
const [loading, setLoading] = useState(true)
const [error, setError] = useState('')
const load = useCallback(async () => {
setLoading(true)
setError('')
try {
const list = await api.listTrainingFrameworkPrograms()
setRows(Array.isArray(list) ? list : [])
} catch (e) {
setError(e.message || 'Laden fehlgeschlagen')
setRows([])
} finally {
setLoading(false)
}
}, [])
useEffect(() => {
load()
}, [load])
async function handleDelete(id, title) {
if (!confirm(`Rahmenprogramm „${title || id}“ wirklich löschen?`)) return
try {
await api.deleteTrainingFrameworkProgram(id)
await load()
} catch (e) {
alert(e.message || 'Löschen fehlgeschlagen')
}
}
return (
<div className="app-page">
<div
style={{
display: 'flex',
flexWrap: 'wrap',
alignItems: 'flex-start',
justifyContent: 'space-between',
gap: '1rem',
marginBottom: '1.25rem',
}}
>
<div>
<h1 style={{ marginBottom: '0.35rem' }}>Trainingsrahmenprogramme</h1>
<p style={{ color: 'var(--text2)', fontSize: '0.95rem', maxWidth: '36rem' }}>
Wiederverwendbare Vorlagen für Ziele und Sessions. Die Verknüpfung mit{' '}
<strong>konkreten Gruppeneinheiten</strong> erfolgt aus der <strong>Planung der Gruppe</strong> (Übernahme
mit Bezug zum Rahmen).
</p>
</div>
<Link
to="/planning/framework-programs/new"
className="btn btn-primary"
style={{ textDecoration: 'none', whiteSpace: 'nowrap' }}
>
Rahmenprogramm anlegen
</Link>
</div>
<p style={{ marginBottom: '1rem' }}>
<Link to="/planning" style={{ color: 'var(--accent-dark)' }}>
← Zurück zur Trainingsplanung
</Link>
</p>
{error && (
<div className="card" style={{ borderLeft: '4px solid var(--danger)', marginBottom: '1rem' }}>
{error}
</div>
)}
{loading ? (
<div style={{ textAlign: 'center', padding: '2rem' }}>
<div className="spinner" />
<p>Laden…</p>
</div>
) : rows.length === 0 ? (
<div className="card">
<p style={{ color: 'var(--text2)', marginBottom: '1rem' }}>
Noch kein Rahmenprogramm gespeichert. Lege ein neues an — mit Titel, mindestens einem Ziel und optional
Slots samt Übungen.
</p>
<Link
to="/planning/framework-programs/new"
className="btn btn-primary btn-full"
style={{ textDecoration: 'none' }}
>
Rahmenprogramm anlegen
</Link>
</div>
) : (
<ul style={{ listStyle: 'none' }}>
{rows.map((r) => (
<li key={r.id} className="card" style={{ marginBottom: '12px' }}>
<div
style={{
display: 'flex',
flexWrap: 'wrap',
alignItems: 'flex-start',
justifyContent: 'space-between',
gap: '0.75rem',
}}
>
<div>
<Link
to={`/planning/framework-programs/${r.id}`}
style={{ fontWeight: 600, fontSize: '1.05rem', color: 'var(--text1)' }}
>
{r.title || `Rahmen #${r.id}`}
</Link>
<div style={{ fontSize: '0.85rem', color: 'var(--text2)', marginTop: '0.35rem' }}>
<span>
{r.goals_count ?? '—'} Ziele · {r.slots_count ?? '—'} Slots
</span>
{contextTeaser(r) ? (
<span style={{ display: 'block', marginTop: '0.25rem', color: 'var(--text3)' }}>
{contextTeaser(r)}
</span>
) : null}
</div>
{r.description ? (
<p style={{ marginTop: '0.5rem', fontSize: '0.9rem', color: 'var(--text2)' }}>{r.description}</p>
) : null}
</div>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.5rem' }}>
<Link
to={`/planning/framework-programs/${r.id}`}
className="btn btn-secondary"
style={{ textDecoration: 'none' }}
>
Bearbeiten
</Link>
<button type="button" className="btn btn-secondary" onClick={() => handleDelete(r.id, r.title)}>
Löschen
</button>
</div>
</div>
</li>
))}
</ul>
)}
</div>
)
}