From eade9af2fe7d091cf7245dc2aee688860a0db082 Mon Sep 17 00:00:00 2001 From: Lars Date: Tue, 5 May 2026 11:53:45 +0200 Subject: [PATCH] feat: implement mobile and desktop layouts for framework editing - Added responsive design for the framework editing page, including mobile tabs and a grid layout for goals and slots on desktop. - Introduced state management for tab selection and layout detection based on screen size. - Enhanced user interface with a tabbed navigation system for better organization of content. - Updated styles in app.css to support new layout features and improve overall aesthetics. --- frontend/src/app.css | 65 ++++++++++++++++ .../TrainingFrameworkProgramEditPage.jsx | 76 +++++++++++++++++-- frontend/src/version.js | 2 +- 3 files changed, 137 insertions(+), 6 deletions(-) diff --git a/frontend/src/app.css b/frontend/src/app.css index 52ecbf4..21fc969 100644 --- a/frontend/src/app.css +++ b/frontend/src/app.css @@ -2713,6 +2713,71 @@ a.analysis-split__nav-item { accent-color: var(--accent); } +/* Rahmenprogramm bearbeiten — Mobile Tabs, Desktop Ziele | Slots nebeneinander */ +.framework-edit { + max-width: 800px; + margin: 0 auto; +} +@media (min-width: 1024px) { + .framework-edit { + max-width: min(1200px, 100%); + } + .framework-edit__tabbar { + display: none !important; + } + .framework-edit__goals-slots { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 16px; + align-items: start; + } + /* breit: alle Bereiche sichtbar */ + .framework-edit__panel { + display: block !important; + } +} +.framework-edit__tabbar { + display: flex; + gap: 6px; + margin-bottom: 14px; + padding: 2px 0 12px; + border-bottom: 1px solid var(--border); + flex-wrap: nowrap; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + scrollbar-width: none; +} +.framework-edit__tabbar::-webkit-scrollbar { + display: none; +} +.framework-edit__tab { + flex: 1 1 0; + min-width: 0; + padding: 10px 8px; + border: 1px solid var(--border2); + border-radius: 10px; + background: var(--surface2); + color: var(--text2); + font-size: 12px; + font-weight: 600; + cursor: pointer; + white-space: nowrap; + font-family: var(--font); +} +.framework-edit__tab--active { + background: var(--accent-light); + color: var(--accent-dark); + border-color: var(--accent); +} +.framework-edit__goals-slots { + display: block; +} +@media (max-width: 1023px) { + .framework-edit .framework-edit__panel:not(.framework-edit__panel--active) { + display: none !important; + } +} + @media print { .desktop-sidebar, .bottom-nav, diff --git a/frontend/src/pages/TrainingFrameworkProgramEditPage.jsx b/frontend/src/pages/TrainingFrameworkProgramEditPage.jsx index 08788fd..a0f3987 100644 --- a/frontend/src/pages/TrainingFrameworkProgramEditPage.jsx +++ b/frontend/src/pages/TrainingFrameworkProgramEditPage.jsx @@ -175,6 +175,19 @@ export default function TrainingFrameworkProgramEditPage() { const [units, setUnits] = useState([]) const [pickerSlotIdx, setPickerSlotIdx] = useState(null) const [peekId, setPeekId] = useState(null) + /** 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 + ) + + useEffect(() => { + const mq = window.matchMedia('(min-width: 1024px)') + const apply = () => setDesktopLayout(!!mq.matches) + apply() + mq.addEventListener('change', apply) + return () => mq.removeEventListener('change', apply) + }, []) const loadMeta = useCallback(async () => { try { @@ -429,6 +442,8 @@ export default function TrainingFrameworkProgramEditPage() { } } + const panelActive = (key) => desktopLayout || frameworkTab === key + if (loading) { return (
@@ -440,16 +455,53 @@ export default function TrainingFrameworkProgramEditPage() { return (
-
+

← Alle Rahmenprogramme

-

{isNew ? 'Neues Rahmenprogramm' : 'Rahmenprogramm bearbeiten'}

+

{isNew ? 'Neues Rahmenprogramm' : 'Rahmenprogramm bearbeiten'}

-
+
+

+ 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. +

+
+ +
+ {[ + { id: 'meta', label: 'Stammdaten' }, + { id: 'goals', label: 'Ziele' }, + { id: 'slots', label: 'Slots & Übungen' }, + ].map((t) => ( + + ))} +
+ +

Stammdaten

@@ -555,7 +607,14 @@ export default function TrainingFrameworkProgramEditPage() {
-
+
+

Entwicklungsziele @@ -617,7 +676,13 @@ export default function TrainingFrameworkProgramEditPage() { ))}

-
+

Session‑Slots & Übungen @@ -779,6 +844,7 @@ export default function TrainingFrameworkProgramEditPage() {

))}
+