Refactor TrainingUnitSectionsEditor to support new parallel stream functionality
All checks were successful
Deploy Development / deploy (push) Successful in 40s
Test Suite / pytest-backend (push) Successful in 41s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 12s
Test Suite / k6 /health Baseline (push) Successful in 33s
Test Suite / playwright-tests (push) Successful in 1m8s
All checks were successful
Deploy Development / deploy (push) Successful in 40s
Test Suite / pytest-backend (push) Successful in 41s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 12s
Test Suite / k6 /health Baseline (push) Successful in 33s
Test Suite / playwright-tests (push) Successful in 1m8s
- Renamed functions and parameters to clarify the handling of sections within parallel streams, enhancing code readability. - Updated drag-and-drop event handlers to accommodate additional parameters for managing sections in parallel streams. - Introduced a new utility function to reorder sections as the first entry in a parallel stream, improving section management. - Enhanced the visual representation of drop zones for sections, ensuring a better user experience during reordering operations.
This commit is contained in:
parent
72e8f31cff
commit
3005f1cb3e
|
|
@ -25,7 +25,7 @@ import {
|
|||
swapAdjacentPhaseRuns,
|
||||
reorderBlocksImmutableWithPlanLoc,
|
||||
reorderSectionBeforeParallelRunAsWholeGroup,
|
||||
reorderSectionAsFirstInParallelPhase,
|
||||
reorderSectionAsFirstInParallelStream,
|
||||
reorderBlockIntoParallelStreamEnd,
|
||||
globalInsertBeforeIndexForParallelStreamEnd,
|
||||
movePhaseRunUpByPhaseOrder,
|
||||
|
|
@ -744,7 +744,7 @@ export default function TrainingUnitSectionsEditor({
|
|||
setDropSectionBand({ slot: sectionToSlot, phaseAboveSplitPo: Number(po) || 0 })
|
||||
}
|
||||
|
||||
const onPhaseBelowSplitDragOver = (e, po) => {
|
||||
const onPhaseBelowSplitDragOver = (e, po, so) => {
|
||||
if (!enableSectionDragReorder || !enableParallelPhaseControls) return
|
||||
if (!dtHasType(e, DND_TU_SECTION)) return
|
||||
e.preventDefault()
|
||||
|
|
@ -754,7 +754,10 @@ export default function TrainingUnitSectionsEditor({
|
|||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
setDropSectionBand({ slot: sectionToSlot, phaseBelowSplitPo: Number(po) || 0 })
|
||||
setDropSectionBand({
|
||||
slot: sectionToSlot,
|
||||
phaseBelowSplit: { po: Number(po) || 0, so: Number(so) || 0 },
|
||||
})
|
||||
}
|
||||
|
||||
const applyParsedSectionDrop = (data) => {
|
||||
|
|
@ -824,7 +827,7 @@ export default function TrainingUnitSectionsEditor({
|
|||
})
|
||||
}
|
||||
|
||||
const onPhaseBelowSplitDrop = (e, po) => {
|
||||
const onPhaseBelowSplitDrop = (e, po, so) => {
|
||||
if (!enableSectionDragReorder || !enableParallelPhaseControls) return
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
|
|
@ -843,6 +846,7 @@ export default function TrainingUnitSectionsEditor({
|
|||
return
|
||||
}
|
||||
const targetPo = Number(po) || 0
|
||||
const targetSo = Number(so) || 0
|
||||
const parsed = applyParsedSectionDrop(data)
|
||||
if (!parsed) return
|
||||
|
||||
|
|
@ -864,7 +868,7 @@ export default function TrainingUnitSectionsEditor({
|
|||
|
||||
const { fromSi } = parsed
|
||||
patch((prev) => {
|
||||
let next = reorderSectionAsFirstInParallelPhase(prev, fromSi, targetPo)
|
||||
let next = reorderSectionAsFirstInParallelStream(prev, fromSi, targetPo, targetSo)
|
||||
if (enableParallelPhaseControls) next = afterSectionReorderParallelGuard(prev, next)
|
||||
return next
|
||||
})
|
||||
|
|
@ -1383,7 +1387,7 @@ export default function TrainingUnitSectionsEditor({
|
|||
dropSectionBand.beforeIdx === bx &&
|
||||
!dropSectionBand.streamDrop &&
|
||||
dropSectionBand.phaseAboveSplitPo == null &&
|
||||
dropSectionBand.phaseBelowSplitPo == null
|
||||
!dropSectionBand.phaseBelowSplit
|
||||
|
||||
const streamChipDropActive = (po, so) =>
|
||||
useStreamTagDropUx &&
|
||||
|
|
@ -1398,7 +1402,8 @@ export default function TrainingUnitSectionsEditor({
|
|||
const phaseBelowSplitDnd =
|
||||
parallelPhaseOrder != null &&
|
||||
dropSectionBand?.slot === sectionToSlot &&
|
||||
dropSectionBand?.phaseBelowSplitPo === parallelPhaseOrder
|
||||
dropSectionBand?.phaseBelowSplit?.po === parallelPhaseOrder &&
|
||||
dropSectionBand?.phaseBelowSplit?.so === activeParallelStream
|
||||
|
||||
const streamVisual =
|
||||
enableParallelPhaseControls && pl?.phaseKind === 'parallel'
|
||||
|
|
@ -1622,22 +1627,6 @@ export default function TrainingUnitSectionsEditor({
|
|||
+ Abschnitt in diesem Stream
|
||||
</button>
|
||||
</div>
|
||||
{enableSectionDragReorder ? (
|
||||
<div
|
||||
className={
|
||||
'tu-section-dropband tu-phase-drop--below-split tu-section-dropband--phase-parallel-slot' +
|
||||
(phaseBelowSplitDnd ? ' tu-section-dropband--active' : '')
|
||||
}
|
||||
title="Erster Abschnitt dieser Split-Phase (vorne einfügen)"
|
||||
aria-label="Dropzone erster Abschnitt unter dem Split-Kopf"
|
||||
onDragOver={(e) => onPhaseBelowSplitDragOver(e, parallelPhaseOrder)}
|
||||
onDragLeave={(e) => {
|
||||
if (e.currentTarget.contains(e.relatedTarget)) return
|
||||
clearSectionDnD()
|
||||
}}
|
||||
onDrop={(e) => onPhaseBelowSplitDrop(e, parallelPhaseOrder)}
|
||||
/>
|
||||
) : null}
|
||||
<div
|
||||
role="tablist"
|
||||
aria-label={`Streams · Phase ${parallelPhaseOrder}`}
|
||||
|
|
@ -1797,6 +1786,39 @@ export default function TrainingUnitSectionsEditor({
|
|||
})}
|
||||
</div>
|
||||
</div>
|
||||
{enableSectionDragReorder ? (
|
||||
<div
|
||||
className={
|
||||
'tu-section-dropband tu-phase-drop--below-split tu-section-dropband--phase-parallel-slot' +
|
||||
sectionDropBandRegionClass(
|
||||
list,
|
||||
firstVisibleIdxActiveStream ?? firstGlobalIdxThisPhase ?? sIdx,
|
||||
enableParallelPhaseControls
|
||||
) +
|
||||
(phaseBelowSplitDnd ? ' tu-section-dropband--active' : '')
|
||||
}
|
||||
title="Erster Abschnitt in dieser Gruppe (hier vorne einfügen)"
|
||||
aria-label="Dropzone: erster Slot der gewählten Splitgruppe unter dem Split-Kopf"
|
||||
onDragOver={(e) =>
|
||||
onPhaseBelowSplitDragOver(
|
||||
e,
|
||||
parallelPhaseOrder,
|
||||
activeParallelStream ?? 0
|
||||
)
|
||||
}
|
||||
onDragLeave={(e) => {
|
||||
if (e.currentTarget.contains(e.relatedTarget)) return
|
||||
clearSectionDnD()
|
||||
}}
|
||||
onDrop={(e) =>
|
||||
onPhaseBelowSplitDrop(
|
||||
e,
|
||||
parallelPhaseOrder,
|
||||
activeParallelStream ?? 0
|
||||
)
|
||||
}
|
||||
/>
|
||||
) : null}
|
||||
</Fragment>
|
||||
) : null}
|
||||
{!hideParallelSection ? (
|
||||
|
|
|
|||
|
|
@ -905,6 +905,58 @@ export function reorderSectionAsFirstInParallelPhase(prev, fromI, phaseOrderInde
|
|||
return arr
|
||||
}
|
||||
|
||||
/** Abschnitt als ersten Eintrag eines parallelen Streams setzen (planLoc wie erster Abschnitt dieses Streams, bzw. leerer Stream wie reorderBlockIntoParallelStreamEnd). */
|
||||
export function reorderSectionAsFirstInParallelStream(prev, fromI, phaseOrderIndex, streamOrderIndex) {
|
||||
const po = Number(phaseOrderIndex) || 0
|
||||
const so = Number(streamOrderIndex) || 0
|
||||
const len = prev?.length ?? 0
|
||||
if (fromI < 0 || fromI >= len) return prev
|
||||
|
||||
const arr = [...prev]
|
||||
const [moved] = arr.splice(fromI, 1)
|
||||
|
||||
const streamIdx = sectionIndicesForParallelStream(arr, po, so)
|
||||
let insertAt
|
||||
let headTpl
|
||||
let skipFromIAdjust = false
|
||||
|
||||
if (streamIdx.length) {
|
||||
const first = Math.min(...streamIdx)
|
||||
headTpl = { ...arr[first].planLoc }
|
||||
insertAt = first
|
||||
} else {
|
||||
const phaseIdx = indicesOfParallelPhase(arr, po)
|
||||
if (!phaseIdx.length) {
|
||||
const ml = moved?.planLoc
|
||||
if (ml?.phaseKind !== 'parallel' || (ml.phaseOrderIndex ?? 0) !== po) return prev
|
||||
headTpl = {
|
||||
...ml,
|
||||
parallelStreamOrderIndex: so,
|
||||
streamTitle: null,
|
||||
streamNotes: null,
|
||||
streamAssignedTrainerProfileIds: null,
|
||||
}
|
||||
insertAt = Math.min(fromI, arr.length)
|
||||
skipFromIAdjust = true
|
||||
} else {
|
||||
const ref = arr[phaseIdx[phaseIdx.length - 1]]
|
||||
headTpl = {
|
||||
...ref.planLoc,
|
||||
parallelStreamOrderIndex: so,
|
||||
streamTitle: null,
|
||||
streamNotes: null,
|
||||
streamAssignedTrainerProfileIds: null,
|
||||
}
|
||||
insertAt = phaseIdx[phaseIdx.length - 1] + 1
|
||||
}
|
||||
}
|
||||
|
||||
if (!skipFromIAdjust && fromI < insertAt) insertAt -= 1
|
||||
insertAt = Math.max(0, Math.min(insertAt, arr.length))
|
||||
arr.splice(insertAt, 0, { ...moved, planLoc: { ...headTpl } })
|
||||
return arr
|
||||
}
|
||||
|
||||
/**
|
||||
* Abschnitt ans Ende eines parallelen Streams setzen (planLoc wie dieser Stream).
|
||||
* Leerer Stream: Einfügen hinter den letzten Abschnitt der zugehörigen parallelen Phase, planLoc vom Referenz-Abschnitt mit angepasstem streamIndex.
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user