Trainingsplanung und Rahmenplanung #9

Merged
Lars merged 29 commits from develop into main 2026-05-05 16:05:01 +02:00
3 changed files with 26 additions and 13 deletions
Showing only changes of commit 17f0513821 - Show all commits

View File

@ -1,5 +1,5 @@
import React, { useCallback, useEffect, useState } from 'react' import React, { useCallback, useEffect, useState } from 'react'
import { Link, useNavigate, useParams } from 'react-router-dom' import { Link, useNavigate, useParams, useLocation } from 'react-router-dom'
import api from '../utils/api' import api from '../utils/api'
import ExercisePickerModal from '../components/ExercisePickerModal' import ExercisePickerModal from '../components/ExercisePickerModal'
import ExercisePeekModal from '../components/ExercisePeekModal' import ExercisePeekModal from '../components/ExercisePeekModal'
@ -161,9 +161,11 @@ function buildApiPayload(form) {
} }
export default function TrainingFrameworkProgramEditPage() { export default function TrainingFrameworkProgramEditPage() {
const { id: routeId } = useParams() const { id: idParam } = useParams()
const location = useLocation()
const navigate = useNavigate() const navigate = useNavigate()
const isNew = routeId === 'new' /** Route `…/framework-programs/new` hat kein dynamisches `:id` — useParams ist dann leer. */
const isNew = /\/framework-programs\/new\/?$/.test(location.pathname)
const [loading, setLoading] = useState(!isNew) const [loading, setLoading] = useState(!isNew)
const [saving, setSaving] = useState(false) const [saving, setSaving] = useState(false)
@ -226,7 +228,7 @@ export default function TrainingFrameworkProgramEditPage() {
setLoading(false) setLoading(false)
return return
} }
const fid = parseInt(routeId, 10) const fid = parseInt(idParam, 10)
if (Number.isNaN(fid)) { if (Number.isNaN(fid)) {
navigate('/planning/framework-programs', { replace: true }) navigate('/planning/framework-programs', { replace: true })
return return
@ -250,7 +252,7 @@ export default function TrainingFrameworkProgramEditPage() {
return () => { return () => {
cancelled = true cancelled = true
} }
}, [isNew, routeId, navigate]) }, [isNew, idParam, navigate, location.pathname])
const updateField = (key, val) => { const updateField = (key, val) => {
setForm((prev) => { setForm((prev) => {
@ -401,7 +403,7 @@ export default function TrainingFrameworkProgramEditPage() {
const created = await api.createTrainingFrameworkProgram(payload) const created = await api.createTrainingFrameworkProgram(payload)
navigate(`/planning/framework-programs/${created.id}`, { replace: true }) navigate(`/planning/framework-programs/${created.id}`, { replace: true })
} else { } else {
const fid = parseInt(routeId, 10) const fid = parseInt(idParam, 10)
await api.updateTrainingFrameworkProgram(fid, payload) await api.updateTrainingFrameworkProgram(fid, payload)
const refreshed = await api.getTrainingFrameworkProgram(fid) const refreshed = await api.getTrainingFrameworkProgram(fid)
let next = serverFrameworkToForm(refreshed) let next = serverFrameworkToForm(refreshed)
@ -417,7 +419,7 @@ export default function TrainingFrameworkProgramEditPage() {
async function handleDelete() { async function handleDelete() {
if (isNew) return if (isNew) return
const fid = parseInt(routeId, 10) const fid = parseInt(idParam, 10)
if (!confirm('Dieses Rahmenprogramm wirklich löschen?')) return if (!confirm('Dieses Rahmenprogramm wirklich löschen?')) return
try { try {
await api.deleteTrainingFrameworkProgram(fid) await api.deleteTrainingFrameworkProgram(fid)

View File

@ -60,8 +60,12 @@ export default function TrainingFrameworkProgramsListPage() {
im Kontext einer Gruppe. im Kontext einer Gruppe.
</p> </p>
</div> </div>
<Link to="/planning/framework-programs/new" className="btn btn-primary" style={{ textDecoration: 'none' }}> <Link
+ Neues Rahmenprogramm to="/planning/framework-programs/new"
className="btn btn-primary"
style={{ textDecoration: 'none', whiteSpace: 'nowrap' }}
>
Rahmenprogramm anlegen
</Link> </Link>
</div> </div>
@ -84,10 +88,17 @@ export default function TrainingFrameworkProgramsListPage() {
</div> </div>
) : rows.length === 0 ? ( ) : rows.length === 0 ? (
<div className="card"> <div className="card">
<p style={{ color: 'var(--text2)' }}> <p style={{ color: 'var(--text2)', marginBottom: '1rem' }}>
Noch kein Rahmenprogramm angelegt. Über <strong>Neues Rahmenprogramm</strong> startest du mit Titel, Noch kein Rahmenprogramm gespeichert. Lege ein neues an mit Titel, mindestens einem Ziel und optional
Zielen und Slots. Slots samt Übungen.
</p> </p>
<Link
to="/planning/framework-programs/new"
className="btn btn-primary btn-full"
style={{ textDecoration: 'none' }}
>
Rahmenprogramm anlegen
</Link>
</div> </div>
) : ( ) : (
<ul style={{ listStyle: 'none' }}> <ul style={{ listStyle: 'none' }}>

View File

@ -12,7 +12,7 @@ export const PAGE_VERSIONS = {
SkillsPage: "1.0.0", SkillsPage: "1.0.0",
TrainingPlanningPage: "1.3.1", TrainingPlanningPage: "1.3.1",
TrainingFrameworkProgramsListPage: "1.0.0", TrainingFrameworkProgramsListPage: "1.0.0",
TrainingFrameworkProgramEditPage: "1.0.0", TrainingFrameworkProgramEditPage: "1.0.1",
TrainingUnitRunPage: "1.1.0", TrainingUnitRunPage: "1.1.0",
TrainingCoachPage: "1.0.0", TrainingCoachPage: "1.0.0",
AdminCatalogsPage: "2.2.0", // Updated: Frontend API Calls & Field Names für renamed tables AdminCatalogsPage: "2.2.0", // Updated: Frontend API Calls & Field Names für renamed tables