diff --git a/frontend/src/app.css b/frontend/src/app.css index 21fc969..008cda0 100644 --- a/frontend/src/app.css +++ b/frontend/src/app.css @@ -2713,12 +2713,12 @@ a.analysis-split__nav-item { accent-color: var(--accent); } -/* Rahmenprogramm bearbeiten — Mobile Tabs, Desktop Ziele | Slots nebeneinander */ +/* Rahmenprogramm bearbeiten — Mobile Tabs, Desktop Ziele | Slots nebeneinander (synchron zu FRAMEWORK_DESKTOP_MIN_PX im Editor) */ .framework-edit { max-width: 800px; margin: 0 auto; } -@media (min-width: 1024px) { +@media (min-width: 900px) { .framework-edit { max-width: min(1200px, 100%); } @@ -2772,12 +2772,42 @@ a.analysis-split__nav-item { .framework-edit__goals-slots { display: block; } -@media (max-width: 1023px) { +@media (max-width: 899px) { .framework-edit .framework-edit__panel:not(.framework-edit__panel--active) { display: none !important; } } +/* Rahmen-Editor: Slots (= Session‑Spalten) horizontal, scrollbar */ +.framework-slots-board { + display: flex; + flex-direction: row; + flex-wrap: nowrap; + gap: 12px; + align-items: stretch; + overflow-x: auto; + overflow-y: hidden; + padding: 4px 2px 12px; + margin: 0 -4px; + -webkit-overflow-scrolling: touch; + scroll-snap-type: x proximity; +} +.framework-slots-board .framework-slot-card { + flex: 0 0 min(320px, calc(100vw - 48px)); + min-width: min(320px, calc(100vw - 48px)); + max-height: min(70vh, 720px); + overflow-x: hidden; + overflow-y: auto; + scroll-snap-align: start; + box-sizing: border-box; +} +@media (min-width: 900px) { + .framework-slots-board .framework-slot-card { + flex-basis: 300px; + min-width: 280px; + } +} + @media print { .desktop-sidebar, .bottom-nav, diff --git a/frontend/src/pages/TrainingFrameworkProgramEditPage.jsx b/frontend/src/pages/TrainingFrameworkProgramEditPage.jsx index a0f3987..db922c7 100644 --- a/frontend/src/pages/TrainingFrameworkProgramEditPage.jsx +++ b/frontend/src/pages/TrainingFrameworkProgramEditPage.jsx @@ -4,6 +4,9 @@ import api from '../utils/api' import ExercisePickerModal from '../components/ExercisePickerModal' import ExercisePeekModal from '../components/ExercisePeekModal' +/** Unter dieser Breite: Registerkarten; darüber: Ziele | Slots nebeneinander (muss zu app.css passen) */ +const FRAMEWORK_DESKTOP_MIN_PX = 900 + function emptyGoal() { return { title: '', notes: '' } } @@ -178,11 +181,13 @@ export default function TrainingFrameworkProgramEditPage() { /** Nur schmal: welcher Block sichtbar — Desktop zeigt Stammdaten + zwei Spalten Ziele|Slots */ const [frameworkTab, setFrameworkTab] = useState('meta') const [desktopLayout, setDesktopLayout] = useState( - typeof window !== 'undefined' ? window.matchMedia('(min-width: 1024px)').matches : false + typeof window !== 'undefined' + ? window.matchMedia(`(min-width: ${FRAMEWORK_DESKTOP_MIN_PX}px)`).matches + : false ) useEffect(() => { - const mq = window.matchMedia('(min-width: 1024px)') + const mq = window.matchMedia(`(min-width: ${FRAMEWORK_DESKTOP_MIN_PX}px)`) const apply = () => setDesktopLayout(!!mq.matches) apply() mq.addEventListener('change', apply) @@ -444,6 +449,10 @@ export default function TrainingFrameworkProgramEditPage() { const panelActive = (key) => desktopLayout || frameworkTab === key + /** Schmale Ansicht: Sichtbarkeit per Inline (falls globales CSS nicht greift / altes Bundle) */ + const panelVisibilityStyle = (key) => + desktopLayout ? undefined : { display: panelActive(key) ? 'block' : 'none' } + if (loading) { return (
@@ -467,16 +476,38 @@ export default function TrainingFrameworkProgramEditPage() {

Stand dieser Funktion: Der Rahmen speichert Ziele, Slots - und Übungslisten — noch ohne die feinere{' '} - Trainingsplan‑Struktur pro Einheit (Abschnitte wie in der Einheitenplanung) und{' '} - ohne Übernahme in die nächste konkrete Trainingseinheit (geplanter Schritt „Warenkorb“). - Ziele lassen sich noch nicht einzelnen Einheiten oder Übungen zuordnen;{' '} - Progressionsketten aus dem Übungs‑Graph sind hier noch nicht eingebunden — dafür sind - API‑/Daten‑Erweiterungen und ein Folgerelease vorgesehen. + und pro Slot eine Übungsliste (Stückliste). Eine volle Einheiten‑Struktur wie + in der Trainingsplanung (Abschnitte, Notizen, Mikrovorlage pro Slot) ist im Konzept optional ( + CURR‑010: training_plan_template_id pro Slot) — in der DB derzeit{' '} + noch nicht umgesetzt.{' '} + Übernahme in konkrete Trainingseinheiten mit Referenz auf den Rahmen (Kopie, editierbar){' '} + ist Stufe 3 / Lineage vorgesehen. Die Slot‑Spalten unten sind die geplanten + Session‑Positionen ohne feste Termine am Rahmen.

-
+
{[ { id: 'meta', label: 'Stammdaten' }, { id: 'goals', label: 'Ziele' }, @@ -500,7 +531,7 @@ export default function TrainingFrameworkProgramEditPage() { 'framework-edit__panel framework-edit__panel--meta card' + (panelActive('meta') ? ' framework-edit__panel--active' : '') } - style={{ marginBottom: '1rem' }} + style={{ marginBottom: '1rem', ...(panelVisibilityStyle('meta') || {}) }} >

Stammdaten

@@ -607,13 +638,25 @@ export default function TrainingFrameworkProgramEditPage() {
-
+

@@ -681,7 +724,7 @@ export default function TrainingFrameworkProgramEditPage() { 'framework-edit__panel framework-edit__panel--slots card' + (panelActive('slots') ? ' framework-edit__panel--active' : '') } - style={{ marginBottom: '1.5rem' }} + style={{ marginBottom: '1.5rem', ...(panelVisibilityStyle('slots') || {}) }} >

@@ -694,15 +737,21 @@ export default function TrainingFrameworkProgramEditPage() { {form.slots.length === 0 ? (

- Noch keine Slots — mit + Slot legst du z. B. „Woche 1 / Einheit A“ an und ordnest Übungen zu. + Noch keine Slots — mit + Slot legst du Einheiten‑Spalten an (z. B. „Woche 1“, „Einheit + A“) und ordnest Übungen zu. Spalten kannst du horizontal scrollen.

- ) : null} + ) : ( +

+ Slots = geplante Einheiten/Sessions im Überblick — nach rechts scrollen, wenn es viele sind. +

+ )} +
{form.slots.map((slot, si) => (
+