Trainingsplanung und Rahmenplanung #9
|
|
@ -3012,7 +3012,7 @@ a.analysis-split__nav-item {
|
||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Horizontaler Überblick: äußerer Scroll‑Container (Zuverlässigkeit in Flex/Grid‑Eltern) */
|
/* Horizontaler Überblick: äußerer Scroll‑Container (Desktop: breite Session‑Karten) */
|
||||||
.framework-slots-board-outer {
|
.framework-slots-board-outer {
|
||||||
container-type: inline-size;
|
container-type: inline-size;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
@ -3029,8 +3029,22 @@ a.analysis-split__nav-item {
|
||||||
scrollbar-gutter: stable;
|
scrollbar-gutter: stable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.framework-slots-board-outer--mobile-single {
|
||||||
|
overflow-x: visible;
|
||||||
|
scrollbar-gutter: auto;
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: 0;
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
padding-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.framework-slots-board-outer--desktop {
|
||||||
|
scrollbar-gutter: stable;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 1023px) {
|
@media (max-width: 1023px) {
|
||||||
.framework-slots-board-outer {
|
.framework-slots-board-outer:not(.framework-slots-board-outer--mobile-single) {
|
||||||
scrollbar-gutter: auto;
|
scrollbar-gutter: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3047,13 +3061,12 @@ a.analysis-split__nav-item {
|
||||||
scroll-snap-type: x proximity;
|
scroll-snap-type: x proximity;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Kartenbreite an den Scroll-Container koppeln (100vw wäre oft breiter als .app-main → horizontales Wischen der ganzen Seite) */
|
.framework-slots-board--desktop-wide .framework-slot-card {
|
||||||
.framework-slots-board .framework-slot-card {
|
flex: 0 0 min(760px, max(560px, calc(100cqw - 48px)));
|
||||||
flex: 0 0 min(300px, calc(100vw - 72px));
|
width: min(760px, max(560px, calc(100cqw - 48px)));
|
||||||
width: min(300px, calc(100vw - 72px));
|
min-width: min(760px, max(560px, calc(100cqw - 48px)));
|
||||||
min-width: min(300px, calc(100vw - 72px));
|
height: auto;
|
||||||
height: min(520px, 72vh);
|
max-height: none;
|
||||||
max-height: min(520px, 72vh);
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
|
|
@ -3063,10 +3076,87 @@ a.analysis-split__nav-item {
|
||||||
scroll-snap-align: start;
|
scroll-snap-align: start;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
.framework-slots-board .framework-slot-card {
|
|
||||||
flex: 0 0 min(300px, calc(100cqw - 24px));
|
.framework-slots-board--desktop-wide .framework-slot-card__plan-editor {
|
||||||
width: min(300px, calc(100cqw - 24px));
|
flex: 1;
|
||||||
min-width: min(300px, calc(100cqw - 24px));
|
min-height: 240px;
|
||||||
|
max-height: min(78vh, 1200px);
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ein Slot = nutzbare Bildschirmbreite; Chips oben/unten wechseln die Session */
|
||||||
|
.framework-slot-mobile-panel {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.framework-slot-mobile-panel .framework-slot-card--mobile-single {
|
||||||
|
flex: none;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
height: auto;
|
||||||
|
max-height: none;
|
||||||
|
overflow: visible;
|
||||||
|
scroll-snap-align: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.framework-slot-mobile-panel .framework-slot-card__plan-editor {
|
||||||
|
max-height: none;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.framework-slot-chips-bar {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
gap: 8px;
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: hidden;
|
||||||
|
padding: 8px 0 10px;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
scrollbar-width: thin;
|
||||||
|
}
|
||||||
|
|
||||||
|
.framework-slot-chips-bar--bottom {
|
||||||
|
margin-bottom: 0;
|
||||||
|
margin-top: 12px;
|
||||||
|
padding-top: 6px;
|
||||||
|
border-top: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.framework-slot-chip {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
appearance: none;
|
||||||
|
margin: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 8px 16px;
|
||||||
|
border-radius: 999px;
|
||||||
|
border: 1px solid var(--border2);
|
||||||
|
background: var(--surface2);
|
||||||
|
color: var(--text2);
|
||||||
|
font-size: 0.86rem;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.25;
|
||||||
|
max-width: 220px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.framework-slot-chip:hover {
|
||||||
|
border-color: var(--accent);
|
||||||
|
color: var(--accent-dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
.framework-slot-chip--active {
|
||||||
|
background: var(--accent);
|
||||||
|
border-color: var(--accent);
|
||||||
|
color: var(--accent-text, #fff);
|
||||||
}
|
}
|
||||||
|
|
||||||
.framework-slot-card__head {
|
.framework-slot-card__head {
|
||||||
|
|
@ -3295,11 +3385,6 @@ a.analysis-split__nav-item {
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 900px) {
|
@media (min-width: 900px) {
|
||||||
.framework-slots-board .framework-slot-card {
|
|
||||||
flex-basis: 300px;
|
|
||||||
width: 300px;
|
|
||||||
min-width: 300px;
|
|
||||||
}
|
|
||||||
.framework-slot-card__slot-actions {
|
.framework-slot-card__slot-actions {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ export default function TrainingUnitSectionsEditor({
|
||||||
showExecutionExtras = false,
|
showExecutionExtras = false,
|
||||||
heading = 'Abschnitte & Übungen',
|
heading = 'Abschnitte & Übungen',
|
||||||
hideHeading = false,
|
hideHeading = false,
|
||||||
|
wideExerciseGrid = false,
|
||||||
}) {
|
}) {
|
||||||
const ensure = (prev) =>
|
const ensure = (prev) =>
|
||||||
prev && prev.length ? prev : [defaultSection()]
|
prev && prev.length ? prev : [defaultSection()]
|
||||||
|
|
@ -233,7 +234,9 @@ export default function TrainingUnitSectionsEditor({
|
||||||
key={`ex-${sIdx}-${iIdx}`}
|
key={`ex-${sIdx}-${iIdx}`}
|
||||||
style={{
|
style={{
|
||||||
display: 'grid',
|
display: 'grid',
|
||||||
gridTemplateColumns: '28px minmax(0, 1fr) minmax(0, 64px) 36px',
|
gridTemplateColumns: wideExerciseGrid
|
||||||
|
? '32px minmax(0, 1fr) minmax(96px, 220px) 44px'
|
||||||
|
: '28px minmax(0, 1fr) minmax(0, 64px) 36px',
|
||||||
gap: '6px',
|
gap: '6px',
|
||||||
alignItems: 'start',
|
alignItems: 'start',
|
||||||
marginTop: '0.65rem',
|
marginTop: '0.65rem',
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,11 @@ function reorderArray(arr, from, to) {
|
||||||
return next
|
return next
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function slotChipLabel(slot, idx) {
|
||||||
|
const t = (slot?.title || '').trim()
|
||||||
|
return t || `Session ${idx + 1}`
|
||||||
|
}
|
||||||
|
|
||||||
function emptyGoal() {
|
function emptyGoal() {
|
||||||
return { title: '', notes: '' }
|
return { title: '', notes: '' }
|
||||||
}
|
}
|
||||||
|
|
@ -187,6 +192,8 @@ export default function TrainingFrameworkProgramEditPage() {
|
||||||
? window.matchMedia(`(min-width: ${FRAMEWORK_DESKTOP_MIN_PX}px)`).matches
|
? window.matchMedia(`(min-width: ${FRAMEWORK_DESKTOP_MIN_PX}px)`).matches
|
||||||
: false
|
: false
|
||||||
)
|
)
|
||||||
|
/** Schmale Ansicht: welcher Session-Slot gerade die volle Breite nutzt (Chip-Navigation) */
|
||||||
|
const [mobileSlotIdx, setMobileSlotIdx] = useState(0)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const mq = window.matchMedia(`(min-width: ${FRAMEWORK_DESKTOP_MIN_PX}px)`)
|
const mq = window.matchMedia(`(min-width: ${FRAMEWORK_DESKTOP_MIN_PX}px)`)
|
||||||
|
|
@ -233,6 +240,10 @@ export default function TrainingFrameworkProgramEditPage() {
|
||||||
loadMeta()
|
loadMeta()
|
||||||
}, [loadMeta])
|
}, [loadMeta])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setMobileSlotIdx(0)
|
||||||
|
}, [idParam, isNew])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isNew) {
|
if (isNew) {
|
||||||
setForm(defaultForm())
|
setForm(defaultForm())
|
||||||
|
|
@ -307,13 +318,32 @@ export default function TrainingFrameworkProgramEditPage() {
|
||||||
if (j < 0 || j >= prev.slots.length) return prev
|
if (j < 0 || j >= prev.slots.length) return prev
|
||||||
const sl = [...prev.slots]
|
const sl = [...prev.slots]
|
||||||
;[sl[idx], sl[j]] = [sl[j], sl[idx]]
|
;[sl[idx], sl[j]] = [sl[j], sl[idx]]
|
||||||
|
setMobileSlotIdx((mi) => {
|
||||||
|
if (mi === idx) return j
|
||||||
|
if (mi === j) return idx
|
||||||
|
return mi
|
||||||
|
})
|
||||||
return { ...prev, slots: sl }
|
return { ...prev, slots: sl }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const addSlot = () => setForm((prev) => ({ ...prev, slots: [...prev.slots, emptySlot()] }))
|
const addSlot = () => {
|
||||||
const removeSlot = (idx) =>
|
setForm((prev) => {
|
||||||
|
const slots = [...prev.slots, emptySlot()]
|
||||||
|
setMobileSlotIdx(slots.length - 1)
|
||||||
|
return { ...prev, slots }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeSlot = (idx) => {
|
||||||
|
const n = form.slots.length
|
||||||
|
setMobileSlotIdx((mi) => {
|
||||||
|
if (idx < mi) return Math.max(0, mi - 1)
|
||||||
|
if (idx === mi) return Math.min(mi, Math.max(0, n - 2))
|
||||||
|
return mi
|
||||||
|
})
|
||||||
setForm((prev) => ({ ...prev, slots: prev.slots.filter((_, i) => i !== idx) }))
|
setForm((prev) => ({ ...prev, slots: prev.slots.filter((_, i) => i !== idx) }))
|
||||||
|
}
|
||||||
|
|
||||||
const slotField = (sIdx, key, val) => {
|
const slotField = (sIdx, key, val) => {
|
||||||
setForm((prev) => ({
|
setForm((prev) => ({
|
||||||
|
|
@ -440,6 +470,129 @@ export default function TrainingFrameworkProgramEditPage() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const slotChipButtons = (opts) =>
|
||||||
|
form.slots.map((slot, si) => {
|
||||||
|
const isActive = si === mobileSlotIdx
|
||||||
|
const sel = opts?.tabSemantics
|
||||||
|
const baseClass = `framework-slot-chip${isActive ? ' framework-slot-chip--active' : ''}`
|
||||||
|
const attrs = sel
|
||||||
|
? { role: 'tab', id: `fw-slot-chip-${si}`, 'aria-selected': isActive }
|
||||||
|
: { 'aria-pressed': isActive }
|
||||||
|
return (
|
||||||
|
<button key={si} type="button" {...attrs} className={baseClass} onClick={() => setMobileSlotIdx(si)} title={slotChipLabel(slot, si)}>
|
||||||
|
{slotChipLabel(slot, si)}
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const renderFrameworkSlotCard = (si) => {
|
||||||
|
const slot = form.slots[si]
|
||||||
|
if (!slot) return null
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={si}
|
||||||
|
className={`card framework-slot-card${!desktopLayout ? ' framework-slot-card--mobile-single' : ''}`}
|
||||||
|
onDragOver={desktopLayout ? onSlotColumnDragOver : undefined}
|
||||||
|
onDrop={desktopLayout ? (e) => onSlotColumnDrop(e, si) : undefined}
|
||||||
|
>
|
||||||
|
<div className="framework-slot-card__head">
|
||||||
|
{desktopLayout ? (
|
||||||
|
<span
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
className="framework-slot-card__drag-handle"
|
||||||
|
draggable
|
||||||
|
onDragStart={(e) => onSlotDragStart(e, si)}
|
||||||
|
aria-label="Slot ziehen: Reihenfolge ändern"
|
||||||
|
title="Slot ziehen (Reihenfolge)"
|
||||||
|
>
|
||||||
|
⋮⋮
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
|
<div className="framework-slot-card__head-main">
|
||||||
|
<span className="framework-slot-card__slot-label">Session {si + 1}</span>
|
||||||
|
<input
|
||||||
|
className="form-input framework-slot-card__title-input"
|
||||||
|
value={slot.title}
|
||||||
|
onChange={(e) => slotField(si, 'title', e.target.value)}
|
||||||
|
placeholder={`z. B. Woche ${si + 1}`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="framework-slot-card__slot-actions">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-secondary framework-ctrl framework-ctrl--xs"
|
||||||
|
onClick={() => moveSlot(si, -1)}
|
||||||
|
aria-label="Slot nach links / oben"
|
||||||
|
>
|
||||||
|
↑
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-secondary framework-ctrl framework-ctrl--xs"
|
||||||
|
onClick={() => moveSlot(si, 1)}
|
||||||
|
aria-label="Slot nach rechts / unten"
|
||||||
|
>
|
||||||
|
↓
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-secondary framework-ctrl framework-ctrl--xs"
|
||||||
|
onClick={() => removeSlot(si)}
|
||||||
|
>
|
||||||
|
×
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<details className="framework-slot-details">
|
||||||
|
<summary className="framework-slot-details__summary">Notizen (Session)</summary>
|
||||||
|
<div className="form-row">
|
||||||
|
<label className="form-label">Notizen</label>
|
||||||
|
<textarea
|
||||||
|
className="form-input"
|
||||||
|
rows={2}
|
||||||
|
value={slot.notes}
|
||||||
|
onChange={(e) => slotField(si, 'notes', e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<div className="framework-slot-card__plan-editor" style={{ marginTop: '0.65rem', minHeight: '120px' }}>
|
||||||
|
<TrainingUnitSectionsEditor
|
||||||
|
heading={`Ablauf · Session ${si + 1}`}
|
||||||
|
sections={slot.sections}
|
||||||
|
showExecutionExtras={false}
|
||||||
|
wideExerciseGrid
|
||||||
|
onSectionsChange={(updater) => {
|
||||||
|
setForm((prev) => ({
|
||||||
|
...prev,
|
||||||
|
slots: prev.slots.map((sl, ii) =>
|
||||||
|
ii !== si
|
||||||
|
? sl
|
||||||
|
: {
|
||||||
|
...sl,
|
||||||
|
sections: updater(
|
||||||
|
sl.sections && sl.sections.length ? sl.sections : [defaultSection('Ablauf')]
|
||||||
|
),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
}))
|
||||||
|
}}
|
||||||
|
onRequestExercisePick={({ sectionIndex, itemIndex }) =>
|
||||||
|
setSectionPickerCtx({
|
||||||
|
slotIdx: si,
|
||||||
|
sectionIndex,
|
||||||
|
itemIndex,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
onPeekExercise={(id) => setPeekId(id)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="app-page" style={{ padding: '2rem 0', textAlign: 'center' }}>
|
<div className="app-page" style={{ padding: '2rem 0', textAlign: 'center' }}>
|
||||||
|
|
@ -840,114 +993,49 @@ export default function TrainingFrameworkProgramEditPage() {
|
||||||
className="framework-slots-hint"
|
className="framework-slots-hint"
|
||||||
style={{ fontSize: '0.8rem', color: 'var(--text3)', marginBottom: '10px', lineHeight: 1.45 }}
|
style={{ fontSize: '0.8rem', color: 'var(--text3)', marginBottom: '10px', lineHeight: 1.45 }}
|
||||||
>
|
>
|
||||||
Reihenfolge der Spalten per Griff (⋮⋮) ziehen; Inhalt eines Slots wie in der Planung bearbeiten.
|
{desktopLayout ? (
|
||||||
|
<>
|
||||||
|
Reihenfolge der Spalten per Griff (⋮⋮) ziehen oder mit ↑ / ↓ verschieben; Inhalt eines Slots wie
|
||||||
|
in der Planung bearbeiten (Abschnitte, Übungen, Anmerkungen).
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
Sessions oben oder unten per Chips wählen — ein Slot nutzt die volle Breite. Reihenfolge mit ↑ / ↓;
|
||||||
|
bearbeiten wie in der Planung.
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="framework-slots-board-outer">
|
{form.slots.length > 0 ? (
|
||||||
<div className="framework-slots-board">
|
!desktopLayout ? (
|
||||||
{form.slots.map((slot, si) => (
|
<div className="framework-slots-board-outer framework-slots-board-outer--mobile-single">
|
||||||
<div
|
<div
|
||||||
key={si}
|
className="framework-slot-chips-bar framework-slot-chips-bar--top"
|
||||||
className="card framework-slot-card"
|
role="tablist"
|
||||||
onDragOver={onSlotColumnDragOver}
|
aria-label="Sessions"
|
||||||
onDrop={(e) => onSlotColumnDrop(e, si)}
|
|
||||||
>
|
>
|
||||||
<div className="framework-slot-card__head">
|
{slotChipButtons({ tabSemantics: true })}
|
||||||
<span
|
|
||||||
role="button"
|
|
||||||
tabIndex={0}
|
|
||||||
className="framework-slot-card__drag-handle"
|
|
||||||
draggable
|
|
||||||
onDragStart={(e) => onSlotDragStart(e, si)}
|
|
||||||
aria-label="Slot ziehen: Reihenfolge ändern"
|
|
||||||
title="Slot ziehen (Reihenfolge)"
|
|
||||||
>
|
|
||||||
⋮⋮
|
|
||||||
</span>
|
|
||||||
<div className="framework-slot-card__head-main">
|
|
||||||
<span className="framework-slot-card__slot-label">Session {si + 1}</span>
|
|
||||||
<input
|
|
||||||
className="form-input framework-slot-card__title-input"
|
|
||||||
value={slot.title}
|
|
||||||
onChange={(e) => slotField(si, 'title', e.target.value)}
|
|
||||||
placeholder={`z. B. Woche ${si + 1}`}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="framework-slot-card__slot-actions">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="btn btn-secondary framework-ctrl framework-ctrl--xs"
|
|
||||||
onClick={() => moveSlot(si, -1)}
|
|
||||||
aria-label="Slot nach links / oben"
|
|
||||||
>
|
|
||||||
↑
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="btn btn-secondary framework-ctrl framework-ctrl--xs"
|
|
||||||
onClick={() => moveSlot(si, 1)}
|
|
||||||
aria-label="Slot nach rechts / unten"
|
|
||||||
>
|
|
||||||
↓
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="btn btn-secondary framework-ctrl framework-ctrl--xs"
|
|
||||||
onClick={() => removeSlot(si)}
|
|
||||||
>
|
|
||||||
×
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<details className="framework-slot-details">
|
|
||||||
<summary className="framework-slot-details__summary">Notizen (Session)</summary>
|
|
||||||
<div className="form-row">
|
|
||||||
<label className="form-label">Notizen</label>
|
|
||||||
<textarea
|
|
||||||
className="form-input"
|
|
||||||
rows={2}
|
|
||||||
value={slot.notes}
|
|
||||||
onChange={(e) => slotField(si, 'notes', e.target.value)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<div className="framework-slot-card__plan-editor" style={{ marginTop: '0.65rem', minHeight: '120px' }}>
|
|
||||||
<TrainingUnitSectionsEditor
|
|
||||||
heading={`Ablauf · Session ${si + 1}`}
|
|
||||||
sections={slot.sections}
|
|
||||||
showExecutionExtras={false}
|
|
||||||
onSectionsChange={(updater) => {
|
|
||||||
setForm((prev) => ({
|
|
||||||
...prev,
|
|
||||||
slots: prev.slots.map((sl, ii) =>
|
|
||||||
ii !== si
|
|
||||||
? sl
|
|
||||||
: {
|
|
||||||
...sl,
|
|
||||||
sections: updater(
|
|
||||||
sl.sections && sl.sections.length ? sl.sections : [defaultSection('Ablauf')]
|
|
||||||
),
|
|
||||||
}
|
|
||||||
),
|
|
||||||
}))
|
|
||||||
}}
|
|
||||||
onRequestExercisePick={({ sectionIndex, itemIndex }) =>
|
|
||||||
setSectionPickerCtx({
|
|
||||||
slotIdx: si,
|
|
||||||
sectionIndex,
|
|
||||||
itemIndex,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
onPeekExercise={(id) => setPeekId(id)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
<div className="framework-slot-mobile-panel" tabIndex={-1}>
|
||||||
</div>
|
{renderFrameworkSlotCard(mobileSlotIdx)}
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
className="framework-slot-chips-bar framework-slot-chips-bar--bottom"
|
||||||
|
role="group"
|
||||||
|
aria-label="Sessions (Anwahl unten)"
|
||||||
|
>
|
||||||
|
{slotChipButtons({ tabSemantics: false })}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="framework-slots-board-outer framework-slots-board-outer--desktop">
|
||||||
|
<div className="framework-slots-board framework-slots-board--desktop-wide">
|
||||||
|
{form.slots.map((_, si) => renderFrameworkSlotCard(si))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user