From 352237bbb94b01f96e324aeb03feb59e7c7dcbeb Mon Sep 17 00:00:00 2001 From: Lars Date: Fri, 15 May 2026 16:31:54 +0200 Subject: [PATCH] Refactor TrainingCoachPage and TrainingUnitRunPage to enhance coach branching functionality - Updated TrainingCoachPage to implement branching logic for coach steps, allowing for improved navigation through training phases. - Enhanced session storage handling to manage branch picks and streamline state management during training sessions. - Modified TrainingUnitRunPage to update links for coaching views, reflecting the new branching structure and improving user experience. - Introduced new utility functions in trainingPlanUtils for managing coach branch picks and timeline navigation, optimizing data handling across components. --- frontend/src/pages/TrainingCoachPage.jsx | 351 +++++++++++++++++---- frontend/src/pages/TrainingUnitRunPage.jsx | 4 +- frontend/src/utils/trainingPlanUtils.js | 186 ++++++++++- 3 files changed, 459 insertions(+), 82 deletions(-) diff --git a/frontend/src/pages/TrainingCoachPage.jsx b/frontend/src/pages/TrainingCoachPage.jsx index 3e2f207..c855ba1 100644 --- a/frontend/src/pages/TrainingCoachPage.jsx +++ b/frontend/src/pages/TrainingCoachPage.jsx @@ -1,6 +1,5 @@ /** - * Coach-Modus: eine Position nach der anderen mit Assistentenhinweisen, Zeitnahme und optionaler Nachbereitung. - * Timeline: flach in Phasen-/Stream-Reihenfolge (flattenPlanTimeline). + * Coach-Modus: Schrittfolge mit Split-Punkten (branch_gate), Stream-Wahl pro paralleler Phase, Assistenz und Zeitnahme. */ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { Link, useNavigate, useParams, useSearchParams } from 'react-router-dom' @@ -8,17 +7,23 @@ import api from '../utils/api' import ExerciseFullContent from '../components/ExerciseFullContent' import ExercisePeekModal from '../components/ExercisePeekModal' import { + COACH_ENTRY_BRANCH_GATE, + coachBranchPicksStepStorageSuffix, + coachBranchPicksStorageKey, + coachOutlineGroupsFromTimeline, durationOverridesMapFromDeltas, + findCoachTimelineJumpIndexForPhase, flattenPlanTimeline, itemStableKey, listCoachStreamFocusOptions, + mergeCoachBranchPicksWithUrlFocus, + normalizeCoachBranchPicks, sectionsToPutPayload, summarizeTimelineEntry, } from '../utils/trainingPlanUtils' -function storageStepKey(unitId, coachFocus) { - if (coachFocus == null) return `sj_coach_step_${unitId}_full` - return `sj_coach_step_${unitId}_po${coachFocus.phaseOrder}-so${coachFocus.streamOrder}` +function storageStepKey(unitId, mergedPicks) { + return `sj_coach_step_${unitId}_${coachBranchPicksStepStorageSuffix(mergedPicks)}` } function storageDeltasKey(unitId) { @@ -58,14 +63,16 @@ function CoachControlsBand({ showJumpToTimerOwnerRow = true, onJumpToTimerOwner, timerOwnerLabelIndex, + branchGateMode = false, }) { const disPrev = step <= 0 - const disNext = step >= timelineLength - 1 + const disNext = branchGateMode || step >= timelineLength - 1 const alive = runStartAt != null || pausedAccumMs > 0 - const canApplyForOwner = roundedMinForApply != null && roundedMinForApply >= 1 + const canApplyForOwner = !branchGateMode && roundedMinForApply != null && roundedMinForApply >= 1 const istLabelMin = roundedMinForApply == null ? '—' : String(roundedMinForApply) - const doneLabel = - timelineLength <= 1 + const doneLabel = branchGateMode + ? 'Zuerst Gruppe wählen' + : timelineLength <= 1 ? 'Nachbereitung öffnen' : isLastCoachStep ? 'Nachbereitung & Ist-Zeit' @@ -109,6 +116,7 @@ function CoachControlsBand({ type="button" className="btn btn-primary" style={{ minHeight: '44px', flex: '1 1 auto', fontWeight: 700 }} + disabled={branchGateMode} onClick={onTimerStart} > ▶ Start{alive ? ` · ${clockStr}` : ''} @@ -121,7 +129,7 @@ function CoachControlsBand({ - {alive && ( + {alive && !branchGateMode && (