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 && (