Enhance full-page editor layout and integrate form editor actions
Some checks failed
Deploy Development / deploy (push) Successful in 41s
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 34s
Test Suite / playwright-tests (push) Failing after 6m4s
Some checks failed
Deploy Development / deploy (push) Successful in 41s
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 34s
Test Suite / playwright-tests (push) Failing after 6m4s
- Updated CSS styles for the full-page editor, introducing a sticky header and mobile dock for improved navigation. - Refactored App component to include FormEditorActionsProvider and FormEditorBottomSlot for better form handling. - Simplified TrainingUnitFormShell by removing the FormActionBar, streamlining the form structure and enhancing usability.
This commit is contained in:
parent
734d943d73
commit
6a9351874f
|
|
@ -7,7 +7,7 @@ import {
|
||||||
useLocation,
|
useLocation,
|
||||||
Outlet,
|
Outlet,
|
||||||
} from 'react-router-dom'
|
} from 'react-router-dom'
|
||||||
import { AuthProvider, useAuth } from './context/AuthContext'
|
import { FormEditorActionsProvider, FormEditorBottomSlot } from './context/FormEditorActionsContext'
|
||||||
import { ToastProvider } from './context/ToastContext'
|
import { ToastProvider } from './context/ToastContext'
|
||||||
import { OrgInboxProvider, useOrgInbox } from './context/OrgInboxContext'
|
import { OrgInboxProvider, useOrgInbox } from './context/OrgInboxContext'
|
||||||
import DesktopSidebar from './components/DesktopSidebar'
|
import DesktopSidebar from './components/DesktopSidebar'
|
||||||
|
|
@ -145,6 +145,7 @@ function ProtectedLayout() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<OrgInboxProvider user={user}>
|
<OrgInboxProvider user={user}>
|
||||||
|
<FormEditorActionsProvider>
|
||||||
<DesktopSidebar showAdminNav={showAdminNav} user={user} onLogout={handleLogout} />
|
<DesktopSidebar showAdminNav={showAdminNav} user={user} onLogout={handleLogout} />
|
||||||
<div className="app-shell">
|
<div className="app-shell">
|
||||||
<div className="app-shell__column">
|
<div className="app-shell__column">
|
||||||
|
|
@ -158,9 +159,12 @@ function ProtectedLayout() {
|
||||||
<InactiveMembershipBanner />
|
<InactiveMembershipBanner />
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</div>
|
</div>
|
||||||
|
<FormEditorBottomSlot>
|
||||||
<Nav showAdminNav={showAdminNav} />
|
<Nav showAdminNav={showAdminNav} />
|
||||||
|
</FormEditorBottomSlot>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</FormEditorActionsProvider>
|
||||||
</OrgInboxProvider>
|
</OrgInboxProvider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1450,61 +1450,87 @@ html.modal-scroll-locked .app-main {
|
||||||
padding-right: var(--page-pad, 16px);
|
padding-right: var(--page-pad, 16px);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Einheiten-Editor (Vollseite): Scroll im Formular, Action Bar am unteren Formularrand */
|
/* Vollseiten-Editor: Kopfzeile (Desktop) + Mobile-Dock statt Bottom-Nav */
|
||||||
.app-main:has(.planning-unit-edit-host) {
|
.page-form-editor__header {
|
||||||
overflow: hidden;
|
position: sticky;
|
||||||
display: flex;
|
top: 0;
|
||||||
flex-direction: column;
|
z-index: 12;
|
||||||
|
background: var(--bg);
|
||||||
|
padding-bottom: 0.75rem;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
}
|
}
|
||||||
.planning-layout:has(.planning-unit-edit-host) {
|
.page-form-editor__intro {
|
||||||
flex: 1 1 auto;
|
min-width: 0;
|
||||||
min-height: 0;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
.planning-layout__main:has(.planning-unit-edit-host) {
|
.page-form-editor__back {
|
||||||
flex: 1 1 auto;
|
display: inline-block;
|
||||||
min-height: 0;
|
margin-bottom: 0.35rem;
|
||||||
display: flex;
|
color: var(--accent-dark);
|
||||||
flex-direction: column;
|
font-weight: 600;
|
||||||
overflow: hidden;
|
font-size: 0.9rem;
|
||||||
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
.planning-unit-edit-host {
|
.page-form-editor__title {
|
||||||
flex: 1 1 auto;
|
margin: 0;
|
||||||
min-height: 0;
|
font-size: clamp(1.15rem, 4vw, 1.45rem);
|
||||||
display: flex;
|
line-height: 1.25;
|
||||||
flex-direction: column;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
.planning-unit-edit-host__body {
|
.page-form-editor__header-actions {
|
||||||
flex: 1 1 auto;
|
display: none;
|
||||||
min-height: 0;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
.page-form-shell--fill {
|
.page-form-editor__header-actions .form-action-bar {
|
||||||
flex: 1 1 auto;
|
|
||||||
min-height: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
.page-form-shell--fill .page-form-shell__scroll {
|
|
||||||
overflow-x: hidden;
|
|
||||||
overflow-y: auto;
|
|
||||||
overscroll-behavior: contain;
|
|
||||||
overscroll-behavior-x: none;
|
|
||||||
touch-action: pan-y;
|
|
||||||
-webkit-overflow-scrolling: touch;
|
|
||||||
padding-bottom: 4px;
|
|
||||||
}
|
|
||||||
.page-form-shell--fill .form-action-bar {
|
|
||||||
position: static;
|
position: static;
|
||||||
margin-top: auto;
|
border: none;
|
||||||
flex-shrink: 0;
|
box-shadow: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
.form-editor-mobile-dock {
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 20;
|
||||||
|
width: auto;
|
||||||
|
max-width: none;
|
||||||
|
background: var(--surface);
|
||||||
|
border-top: 1px solid var(--border);
|
||||||
|
box-shadow: 0 -4px 16px rgba(0, 0, 0, 0.06);
|
||||||
|
padding: var(--nav-pad-top, 8px) max(10px, env(safe-area-inset-right, 0px))
|
||||||
|
env(safe-area-inset-bottom, 0px) max(10px, env(safe-area-inset-left, 0px));
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.form-editor-mobile-dock .form-action-bar {
|
||||||
|
position: static;
|
||||||
|
border: none;
|
||||||
|
box-shadow: none;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1024px) {
|
||||||
|
.page-form-editor__header {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: flex-end;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 12px 16px;
|
||||||
|
}
|
||||||
|
.page-form-editor__header-actions {
|
||||||
|
display: block;
|
||||||
|
flex: 1 1 320px;
|
||||||
|
min-width: min(100%, 420px);
|
||||||
|
}
|
||||||
|
.form-editor-mobile-dock {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1023px) {
|
||||||
|
.app-shell__column:has(.form-editor-mobile-dock) .app-main {
|
||||||
|
padding-bottom: calc(52px + var(--nav-pad-top, 8px) + env(safe-area-inset-bottom, 0px) + 12px);
|
||||||
}
|
}
|
||||||
.form-action-bar--page {
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Einstellungen: gleiche Split-Struktur wie Analyse/Admin */
|
/* Einstellungen: gleiche Split-Struktur wie Analyse/Admin */
|
||||||
|
|
|
||||||
48
frontend/src/components/PageFormEditorChrome.jsx
Normal file
48
frontend/src/components/PageFormEditorChrome.jsx
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
import React, { useMemo } from 'react'
|
||||||
|
import { Link } from 'react-router-dom'
|
||||||
|
import FormActionBar from './FormActionBar'
|
||||||
|
import { useFormEditorActions } from '../context/FormEditorActionsContext'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vollseiten-Editor: sticky Kopfzeile (Desktop) + Mobile-Dock via FormEditorActionsProvider.
|
||||||
|
*/
|
||||||
|
export default function PageFormEditorChrome({
|
||||||
|
title,
|
||||||
|
backTo,
|
||||||
|
backLabel = 'Zurück',
|
||||||
|
actionConfig,
|
||||||
|
children,
|
||||||
|
testId,
|
||||||
|
}) {
|
||||||
|
useFormEditorActions(actionConfig)
|
||||||
|
|
||||||
|
const headerBar = useMemo(() => {
|
||||||
|
if (!actionConfig) return null
|
||||||
|
return (
|
||||||
|
<FormActionBar
|
||||||
|
{...actionConfig}
|
||||||
|
placement="top"
|
||||||
|
variant="page"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}, [actionConfig])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="page-form-editor" data-testid={testId}>
|
||||||
|
<header className="page-form-editor__header">
|
||||||
|
<div className="page-form-editor__intro">
|
||||||
|
{backTo ? (
|
||||||
|
<Link to={backTo} className="page-form-editor__back">
|
||||||
|
← {backLabel}
|
||||||
|
</Link>
|
||||||
|
) : null}
|
||||||
|
<h1 className="page-form-editor__title">{title}</h1>
|
||||||
|
</div>
|
||||||
|
{headerBar ? (
|
||||||
|
<div className="page-form-editor__header-actions">{headerBar}</div>
|
||||||
|
) : null}
|
||||||
|
</header>
|
||||||
|
<div className="page-form-editor__body">{children}</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import React, { useEffect, useMemo, useState } from 'react'
|
import React, { useEffect, useMemo, useState } from 'react'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import FormActionBar from '../FormActionBar'
|
|
||||||
import TrainingPlanExerciseVisibilityPanel from '../TrainingPlanExerciseVisibilityPanel'
|
import TrainingPlanExerciseVisibilityPanel from '../TrainingPlanExerciseVisibilityPanel'
|
||||||
import TrainingUnitSectionsEditor from '../TrainingUnitSectionsEditor'
|
import TrainingUnitSectionsEditor from '../TrainingUnitSectionsEditor'
|
||||||
import { activeClubMemberships } from '../../utils/activeClub'
|
import { activeClubMemberships } from '../../utils/activeClub'
|
||||||
|
|
@ -16,7 +15,6 @@ export default function TrainingUnitFormShell({
|
||||||
setFormData,
|
setFormData,
|
||||||
onSaveOnly,
|
onSaveOnly,
|
||||||
onSaveAndClose,
|
onSaveAndClose,
|
||||||
onCancel,
|
|
||||||
draftPlanTemplateId,
|
draftPlanTemplateId,
|
||||||
onDraftTemplateSelect,
|
onDraftTemplateSelect,
|
||||||
planTemplates,
|
planTemplates,
|
||||||
|
|
@ -33,7 +31,6 @@ export default function TrainingUnitFormShell({
|
||||||
onRequestTrainingModulePick,
|
onRequestTrainingModulePick,
|
||||||
onRequestExercisePick,
|
onRequestExercisePick,
|
||||||
onPeekExercise,
|
onPeekExercise,
|
||||||
saving = false,
|
|
||||||
formId = 'planning-unit-form',
|
formId = 'planning-unit-form',
|
||||||
}) {
|
}) {
|
||||||
const [newTplVisibility, setNewTplVisibility] = useState('private')
|
const [newTplVisibility, setNewTplVisibility] = useState('private')
|
||||||
|
|
@ -52,14 +49,9 @@ export default function TrainingUnitFormShell({
|
||||||
}, [planningClubId, memberClubs])
|
}, [planningClubId, memberClubs])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="planning-unit-edit-host__body" data-testid="planning-unit-form">
|
|
||||||
<h1 className="page-title" style={{ marginBottom: '1rem', flexShrink: 0 }}>
|
|
||||||
{editingUnit ? 'Trainingseinheit bearbeiten' : 'Neue Trainingseinheit'}
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
<form
|
<form
|
||||||
id={formId}
|
id={formId}
|
||||||
className="card page-form-shell page-form-shell--fill"
|
className="card page-form-shell"
|
||||||
style={{ padding: 'clamp(14px, 3vw, 1.75rem)' }}
|
style={{ padding: 'clamp(14px, 3vw, 1.75rem)' }}
|
||||||
onSubmit={(e) => (onSaveAndClose ? onSaveAndClose(e) : onSaveOnly?.(e))}
|
onSubmit={(e) => (onSaveAndClose ? onSaveAndClose(e) : onSaveOnly?.(e))}
|
||||||
>
|
>
|
||||||
|
|
@ -488,20 +480,6 @@ export default function TrainingUnitFormShell({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<FormActionBar
|
|
||||||
placement="bottom"
|
|
||||||
variant="page"
|
|
||||||
formId={formId}
|
|
||||||
saving={saving}
|
|
||||||
isNew={!editingUnit}
|
|
||||||
onSave={onSaveOnly ? () => onSaveOnly() : undefined}
|
|
||||||
onSaveAndClose={onSaveAndClose ? () => onSaveAndClose() : undefined}
|
|
||||||
onCancel={onCancel}
|
|
||||||
showSave={Boolean(onSaveOnly)}
|
|
||||||
showSaveAndClose
|
|
||||||
/>
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
38
frontend/src/context/FormEditorActionsContext.jsx
Normal file
38
frontend/src/context/FormEditorActionsContext.jsx
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
import React, { createContext, useContext, useEffect, useMemo, useState } from 'react'
|
||||||
|
import FormActionBar from '../components/FormActionBar'
|
||||||
|
|
||||||
|
const FormEditorActionsContext = createContext(null)
|
||||||
|
|
||||||
|
export function FormEditorActionsProvider({ children }) {
|
||||||
|
const [config, setConfig] = useState(null)
|
||||||
|
const value = useMemo(() => ({ config, setConfig }), [config])
|
||||||
|
|
||||||
|
return <FormEditorActionsContext.Provider value={value}>{children}</FormEditorActionsContext.Provider>
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Mobile: FormActionBar statt Bottom-Nav; Desktop: Fallback (Nav ist ohnehin ausgeblendet). */
|
||||||
|
export function FormEditorBottomSlot({ children }) {
|
||||||
|
const ctx = useContext(FormEditorActionsContext)
|
||||||
|
const config = ctx?.config
|
||||||
|
|
||||||
|
if (config) {
|
||||||
|
return (
|
||||||
|
<div className="form-editor-mobile-dock" data-testid="form-editor-mobile-dock">
|
||||||
|
<FormActionBar {...config} placement="bottom" variant="page" />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return children
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param {Record<string, unknown> | null} config FormActionBar-Props */
|
||||||
|
export function useFormEditorActions(config) {
|
||||||
|
const setConfig = useContext(FormEditorActionsContext)?.setConfig
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!setConfig) return undefined
|
||||||
|
setConfig(config ?? null)
|
||||||
|
return () => setConfig(null)
|
||||||
|
}, [setConfig, config])
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||||
import { Link, useLocation, useNavigate, useParams, useSearchParams } from 'react-router-dom'
|
import { useLocation, useNavigate, useParams, useSearchParams } from 'react-router-dom'
|
||||||
import api from '../utils/api'
|
import api from '../utils/api'
|
||||||
import { useAuth } from '../context/AuthContext'
|
import { useAuth } from '../context/AuthContext'
|
||||||
import { useToast } from '../context/ToastContext'
|
import { useToast } from '../context/ToastContext'
|
||||||
|
|
@ -28,6 +28,7 @@ import {
|
||||||
trainingUnitToFormFields,
|
trainingUnitToFormFields,
|
||||||
validateTrainingUnitFormForSave,
|
validateTrainingUnitFormForSave,
|
||||||
} from '../utils/trainingUnitEditorCore'
|
} from '../utils/trainingUnitEditorCore'
|
||||||
|
import PageFormEditorChrome from '../components/PageFormEditorChrome'
|
||||||
import { buildPlanUnitEditPath, planningHubPathFromReturnState } from '../utils/planningUnitRoutes'
|
import { buildPlanUnitEditPath, planningHubPathFromReturnState } from '../utils/planningUnitRoutes'
|
||||||
|
|
||||||
export default function TrainingUnitEditPage() {
|
export default function TrainingUnitEditPage() {
|
||||||
|
|
@ -396,7 +397,8 @@ export default function TrainingUnitEditPage() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const reloadUnitAfterSave = async (savedId) => {
|
const reloadUnitAfterSave = useCallback(
|
||||||
|
async (savedId) => {
|
||||||
const fullUnit = await api.getTrainingUnit(savedId)
|
const fullUnit = await api.getTrainingUnit(savedId)
|
||||||
setEditingUnit(fullUnit)
|
setEditingUnit(fullUnit)
|
||||||
let sections = normalizeUnitToForm(fullUnit)
|
let sections = normalizeUnitToForm(fullUnit)
|
||||||
|
|
@ -405,9 +407,12 @@ export default function TrainingUnitEditPage() {
|
||||||
if (!isNew && savedId !== unitId) {
|
if (!isNew && savedId !== unitId) {
|
||||||
navigate(buildPlanUnitEditPath(savedId), { replace: true, state: location.state })
|
navigate(buildPlanUnitEditPath(savedId), { replace: true, state: location.state })
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
[isNew, unitId, navigate, location.state]
|
||||||
|
)
|
||||||
|
|
||||||
const handleSubmit = async (e, { closeAfter = true } = {}) => {
|
const handleSubmit = useCallback(
|
||||||
|
async (e, { closeAfter = true } = {}) => {
|
||||||
e?.preventDefault?.()
|
e?.preventDefault?.()
|
||||||
const v = validateTrainingUnitFormForSave(formData)
|
const v = validateTrainingUnitFormForSave(formData)
|
||||||
if (!v.ok) {
|
if (!v.ok) {
|
||||||
|
|
@ -439,7 +444,26 @@ export default function TrainingUnitEditPage() {
|
||||||
} finally {
|
} finally {
|
||||||
setSaving(false)
|
setSaving(false)
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
[formData, editingUnit, draftPlanTemplateId, toast, goBack, reloadUnitAfterSave]
|
||||||
|
)
|
||||||
|
|
||||||
|
const actionConfig = useMemo(
|
||||||
|
() => ({
|
||||||
|
formId: 'planning-unit-form',
|
||||||
|
saving,
|
||||||
|
isNew: !editingUnit,
|
||||||
|
onSave: (e) => handleSubmit(e, { closeAfter: false }),
|
||||||
|
onSaveAndClose: (e) => handleSubmit(e, { closeAfter: true }),
|
||||||
|
onCancel: goBack,
|
||||||
|
showSave: true,
|
||||||
|
showSaveAndClose: true,
|
||||||
|
}),
|
||||||
|
[saving, editingUnit, handleSubmit, goBack]
|
||||||
|
)
|
||||||
|
|
||||||
|
const hubBackPath = planningHubPathFromReturnState(location.state?.planningReturn)
|
||||||
|
const pageTitle = editingUnit ? 'Trainingseinheit bearbeiten' : 'Neue Trainingseinheit'
|
||||||
|
|
||||||
const handleSaveAsTemplate = async (opts = {}) => {
|
const handleSaveAsTemplate = async (opts = {}) => {
|
||||||
const name = window.prompt('Name für die neue Trainingsvorlage (nur Abschnitts-Gliederung):')
|
const name = window.prompt('Name für die neue Trainingsvorlage (nur Abschnitts-Gliederung):')
|
||||||
|
|
@ -568,13 +592,13 @@ export default function TrainingUnitEditPage() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="planning-unit-edit-host">
|
<PageFormEditorChrome
|
||||||
<p style={{ marginBottom: '0.75rem', flexShrink: 0 }}>
|
testId="planning-unit-form"
|
||||||
<Link to={planningHubPathFromReturnState(location.state?.planningReturn)} style={{ color: 'var(--accent-dark)' }}>
|
title={pageTitle}
|
||||||
← Zurück zur Trainingsplanung
|
backTo={hubBackPath}
|
||||||
</Link>
|
backLabel="Trainingsplanung"
|
||||||
</p>
|
actionConfig={actionConfig}
|
||||||
|
>
|
||||||
<TrainingUnitFormShell
|
<TrainingUnitFormShell
|
||||||
editingUnit={editingUnit}
|
editingUnit={editingUnit}
|
||||||
formData={formData}
|
formData={formData}
|
||||||
|
|
@ -582,7 +606,6 @@ export default function TrainingUnitEditPage() {
|
||||||
setFormData={setFormData}
|
setFormData={setFormData}
|
||||||
onSaveOnly={(e) => handleSubmit(e, { closeAfter: false })}
|
onSaveOnly={(e) => handleSubmit(e, { closeAfter: false })}
|
||||||
onSaveAndClose={(e) => handleSubmit(e, { closeAfter: true })}
|
onSaveAndClose={(e) => handleSubmit(e, { closeAfter: true })}
|
||||||
onCancel={goBack}
|
|
||||||
draftPlanTemplateId={draftPlanTemplateId}
|
draftPlanTemplateId={draftPlanTemplateId}
|
||||||
onDraftTemplateSelect={applyTemplateFromSelect}
|
onDraftTemplateSelect={applyTemplateFromSelect}
|
||||||
planTemplates={planTemplates}
|
planTemplates={planTemplates}
|
||||||
|
|
@ -611,7 +634,6 @@ export default function TrainingUnitEditPage() {
|
||||||
onPeekExercise={(id, variantId, peekExtras) =>
|
onPeekExercise={(id, variantId, peekExtras) =>
|
||||||
setPlanningPeekCtx({ exerciseId: id, variantId: variantId ?? null, peekExtras: peekExtras ?? null })
|
setPlanningPeekCtx({ exerciseId: id, variantId: variantId ?? null, peekExtras: peekExtras ?? null })
|
||||||
}
|
}
|
||||||
saving={saving}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TrainingPlanningModuleApplyModal
|
<TrainingPlanningModuleApplyModal
|
||||||
|
|
@ -704,6 +726,6 @@ export default function TrainingUnitEditPage() {
|
||||||
peekExtras={planningPeekCtx?.peekExtras ?? undefined}
|
peekExtras={planningPeekCtx?.peekExtras ?? undefined}
|
||||||
onClose={() => setPlanningPeekCtx(null)}
|
onClose={() => setPlanningPeekCtx(null)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</PageFormEditorChrome>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user