Enhance TrainingCoachPage and trainingPlanUtils with split rejoin transition logic
All checks were successful
Deploy Development / deploy (push) Successful in 39s
Test Suite / pytest-backend (push) Successful in 37s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 13s
Test Suite / k6 /health Baseline (push) Successful in 33s
Test Suite / playwright-tests (push) Successful in 1m11s
Test Suite / pytest-backend (pull_request) Successful in 34s
Test Suite / lint-backend (pull_request) Successful in 0s
Test Suite / build-frontend (pull_request) Successful in 12s
Test Suite / k6 /health Baseline (pull_request) Successful in 34s
Test Suite / playwright-tests (pull_request) Successful in 1m20s
All checks were successful
Deploy Development / deploy (push) Successful in 39s
Test Suite / pytest-backend (push) Successful in 37s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 13s
Test Suite / k6 /health Baseline (push) Successful in 33s
Test Suite / playwright-tests (push) Successful in 1m11s
Test Suite / pytest-backend (pull_request) Successful in 34s
Test Suite / lint-backend (pull_request) Successful in 0s
Test Suite / build-frontend (pull_request) Successful in 12s
Test Suite / k6 /health Baseline (pull_request) Successful in 34s
Test Suite / playwright-tests (pull_request) Successful in 1m20s
- 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.
This commit is contained in:
parent
5e5350d5ac
commit
a4f11a8225
|
|
@ -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?
|
||||
</p>
|
||||
<p style={{ fontSize: '0.84rem', color: 'var(--text2)', margin: '0 0 12px', lineHeight: 1.45 }}>
|
||||
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.
|
||||
</p>
|
||||
<ul style={{ margin: '0 0 14px', paddingLeft: '1.2rem', fontSize: '0.82rem', color: 'var(--text2)' }}>
|
||||
{splitRejoinPrompt.streams.map((st) => (
|
||||
|
|
@ -786,22 +795,36 @@ export default function TrainingCoachPage() {
|
|||
))}
|
||||
</ul>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-primary"
|
||||
style={{ minHeight: '48px', fontWeight: 700 }}
|
||||
onClick={() => {
|
||||
setSplitRejoinPrompt(null)
|
||||
setCoachDebriefPhase(true)
|
||||
try {
|
||||
sessionStorage.setItem(storageDebriefKey(idNum), '1')
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}}
|
||||
>
|
||||
Alle Gruppen fertig — zur Nachbereitung
|
||||
</button>
|
||||
{safeStep < timeline.length - 1 ? (
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-primary"
|
||||
style={{ minHeight: '48px', fontWeight: 700 }}
|
||||
onClick={() => {
|
||||
setSplitRejoinPrompt(null)
|
||||
setStep((s) => clampStep(s + 1, timeline.length))
|
||||
}}
|
||||
>
|
||||
Gruppen zusammengeführt — weiter mit dem Plan
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-primary"
|
||||
style={{ minHeight: '48px', fontWeight: 700 }}
|
||||
onClick={() => {
|
||||
setSplitRejoinPrompt(null)
|
||||
setCoachDebriefPhase(true)
|
||||
try {
|
||||
sessionStorage.setItem(storageDebriefKey(idNum), '1')
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}}
|
||||
>
|
||||
Alle Gruppen fertig — zur Nachbereitung
|
||||
</button>
|
||||
)}
|
||||
<button type="button" className="btn btn-secondary" style={{ minHeight: '44px' }} onClick={() => setSplitRejoinPrompt(null)}>
|
||||
Zurück — andere Gruppe läuft noch
|
||||
</button>
|
||||
|
|
@ -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'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user