From a4f11a8225796086d14d2bb5437accf4e8680847 Mon Sep 17 00:00:00 2001 From: Lars Date: Fri, 15 May 2026 18:52:27 +0200 Subject: [PATCH] Enhance TrainingCoachPage and trainingPlanUtils with split rejoin transition logic - Introduced a new utility function to determine when to prompt for a split rejoin transition between phases, improving user guidance during training sessions. - Updated TrainingCoachPage to incorporate this logic, enhancing the flow of navigation through training timelines. - Refactored button actions to provide clearer options for users when managing group transitions, optimizing the user experience during training. --- frontend/src/pages/TrainingCoachPage.jsx | 73 ++++++++++++++++-------- frontend/src/utils/trainingPlanUtils.js | 14 +++++ 2 files changed, 63 insertions(+), 24 deletions(-) diff --git a/frontend/src/pages/TrainingCoachPage.jsx b/frontend/src/pages/TrainingCoachPage.jsx index abdf806..10c9ecc 100644 --- a/frontend/src/pages/TrainingCoachPage.jsx +++ b/frontend/src/pages/TrainingCoachPage.jsx @@ -13,6 +13,7 @@ import { coachBranchPicksStorageKey, coachOutlineGroupsFromTimeline, coachShouldPromptSplitRejoin, + coachShouldPromptSplitRejoinTransition, durationOverridesMapFromDeltas, findCoachTimelineJumpIndexForPhase, flattenPlanTimeline, @@ -549,6 +550,14 @@ export default function TrainingCoachPage() { } return } + const nextIdx = safeStep + 1 + if (nextIdx < timeline.length) { + const rejoinMid = coachShouldPromptSplitRejoinTransition(unit, timeline[safeStep], timeline[nextIdx]) + if (rejoinMid) { + setSplitRejoinPrompt(rejoinMid) + return + } + } setStep((s) => clampStep(s + 1, timeline.length)) } @@ -620,7 +629,6 @@ export default function TrainingCoachPage() { .trim() } await api.updateTrainingUnit(idNum, payload) - await reloadUnit() setTrainerAppend('') try { sessionStorage.removeItem(storageDeltasKey(idNum)) @@ -630,14 +638,15 @@ export default function TrainingCoachPage() { } catch { /* ignore */ } - setSearchParams({}, { replace: true }) - setStep(0) setDeltas({}) setBranchPicks({}) setStreamChoiceHint(null) + setSplitRejoinPrompt(null) setCoachDebriefPhase(false) - setSaveOk('Gespeichert.') setDebriefOpen(false) + setSaveOk('Gespeichert.') + setSearchParams({}, { replace: true }) + navigate(`/planning/run/${unitId}`, { replace: true }) } catch (e) { setSaveOk(`Fehler: ${e.message || e}`) } finally { @@ -771,11 +780,11 @@ export default function TrainingCoachPage() { ? String(splitRejoinPrompt.phaseTitle).trim() : `Phase ${splitRejoinPrompt.phaseOrderIndex}`} {' — '} - alle Gruppen fertig? + alle Gruppen zusammen?

- Diese Phase hat mehrere Streams. Kurz mit dem anderen Trainer klären, dann gemeinsam Ist-Zeiten und Speichern - (gilt auch, wenn danach kein weiterer Block mehr kommt). + Diese Phase hat mehrere Streams. Kurz mit dem anderen Trainer klären, dann gemeinsam weitermachen. Ist-Zeiten + und Speichern erfolgen in der Nachbereitung am Ende der Einheit.

- + {safeStep < timeline.length - 1 ? ( + + ) : ( + + )} @@ -819,7 +842,9 @@ export default function TrainingCoachPage() { } }} > - Ausnahme: trotzdem zur Nachbereitung + {safeStep < timeline.length - 1 + ? 'Ausnahme: jetzt schon zur Nachbereitung' + : 'Ausnahme: trotzdem zur Nachbereitung'}
diff --git a/frontend/src/utils/trainingPlanUtils.js b/frontend/src/utils/trainingPlanUtils.js index 616de23..df92ca6 100644 --- a/frontend/src/utils/trainingPlanUtils.js +++ b/frontend/src/utils/trainingPlanUtils.js @@ -519,6 +519,20 @@ export function coachShouldPromptSplitRejoin(unit, lastTimelineEntry) { } } +/** + * Nach dem letzten Block eines gewählten Streams: Rückfrage vor Ganzgruppenphase oder vor dem nächsten Split, + * wenn die aktuelle Parallelphase mehrere Streams hat. + */ +export function coachShouldPromptSplitRejoinTransition(unit, currentEntry, nextEntry) { + if (!currentEntry || !nextEntry) return null + const cRm = currentEntry.runMeta + if (!cRm || cRm.kind !== 'parallel' || cRm.streamOrder == null) return null + const intoWholeGroup = nextEntry.runMeta?.kind === 'whole_group' + const intoNextSplit = nextEntry.entryKind === COACH_ENTRY_BRANCH_GATE + if (!intoWholeGroup && !intoNextSplit) return null + return coachShouldPromptSplitRejoin(unit, currentEntry) +} + export function summarizeTimelineEntry(ent) { if (!ent) return '' if (ent.entryKind === COACH_ENTRY_BRANCH_GATE) {