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

159 lines
5.5 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 MODE_LABELS = {
concrete: 'Konkret (Gruppe / Einheiten)',
library: 'Bibliothek',
}
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' }}>
Mehrere Entwicklungsziele und Übungen über Session‑Slots verteilen — als Vorlage in der Bibliothek oder
im Kontext einer Gruppe.{' '}
<span style={{ color: 'var(--text3)', fontSize: '0.88rem' }}>
In der <strong>Bearbeitungsansicht</strong> gibt es auf schmalen Fenstern Registerkarten, auf breiten
Bildschirmen zwei Spalten (Ziele | Slots).
</span>
</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>{MODE_LABELS[r.plan_mode] || r.plan_mode}</span>
{typeof r.goals_count === 'number' || typeof r.slots_count === 'number' ? (
<span>
{' '}
· {r.goals_count ?? '—'} Ziele · {r.slots_count ?? '—'} Slots
</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>
)
}