From e50c18f92e415ab8fca6a1efd0561a4e6945e936 Mon Sep 17 00:00:00 2001 From: Lars Date: Wed, 20 May 2026 06:38:53 +0200 Subject: [PATCH] Enhance ExerciseFormPageRoot with save and close functionality - Added a new `handleSaveAndClose` function to allow users to save and navigate back to the exercise list. - Updated `performSaveAttempt` to accept a `closeAfter` parameter for conditional navigation. - Refactored form submission handling to include separate actions for saving and saving with closure. - Integrated `PageFormEditorChrome` for improved layout and user experience, including a back navigation option. --- .../exercises/ExerciseFormPageRoot.jsx | 87 ++++++++++++------- 1 file changed, 58 insertions(+), 29 deletions(-) diff --git a/frontend/src/components/exercises/ExerciseFormPageRoot.jsx b/frontend/src/components/exercises/ExerciseFormPageRoot.jsx index 5d4a94a..2e1aa9d 100644 --- a/frontend/src/components/exercises/ExerciseFormPageRoot.jsx +++ b/frontend/src/components/exercises/ExerciseFormPageRoot.jsx @@ -26,6 +26,7 @@ import { COMBINATION_ARCHETYPE_OPTIONS, ARCHETYPE_DEFAULT_REP_SERIES_COUNT, defa import { readSlotProfilesV1, normalizeAdvanceMode, parseComboRepSeriesCountUi } from '../../utils/combinationMethodProfileUi' import { GripVertical } from 'lucide-react' import UnsavedChangesPrompt from '../UnsavedChangesPrompt' +import PageFormEditorChrome from '../PageFormEditorChrome' import { useBeforeUnloadWhen, useUnsavedChangesBlocker } from '../../hooks/useUnsavedChangesBlocker' const INTENSITY_OPTIONS = [ @@ -836,7 +837,7 @@ function ExerciseFormPageRoot() { } const performSaveAttempt = useCallback( - async ({ fromUnsavedDialog = false } = {}) => { + async ({ fromUnsavedDialog = false, closeAfter = false } = {}) => { if (!formData.title || formData.title.trim().length < 3) { toast.error('Titel mindestens 3 Zeichen') return false @@ -940,12 +941,15 @@ function ExerciseFormPageRoot() { setVariants((ex.variants || []).map(apiVariantToRow)) setFormDirty(false) toast.success('Gespeichert.') + if (closeAfter) navigate('/exercises') return true } const created = await api.createExercise(payload) setFormDirty(false) toast.success('Übung angelegt.') - if (!fromUnsavedDialog) { + if (closeAfter) { + navigate('/exercises') + } else if (!fromUnsavedDialog) { navigate(`/exercises/${created.id}/edit`, { replace: true }) } return true @@ -959,10 +963,39 @@ function ExerciseFormPageRoot() { [exerciseId, formData, isEdit, navigate, toast], ) - const handleSubmit = async (e) => { - e.preventDefault() - await performSaveAttempt({ fromUnsavedDialog: false }) - } + const handleSubmit = useCallback( + async (e) => { + e?.preventDefault?.() + await performSaveAttempt({ fromUnsavedDialog: false, closeAfter: false }) + }, + [performSaveAttempt], + ) + + const handleSaveAndClose = useCallback( + async (e) => { + e?.preventDefault?.() + await performSaveAttempt({ fromUnsavedDialog: false, closeAfter: true }) + }, + [performSaveAttempt], + ) + + const goBackToList = useCallback(() => { + navigate('/exercises') + }, [navigate]) + + const actionConfig = useMemo( + () => ({ + formId: 'exercise-form', + saving, + isNew: !isEdit, + onSave: handleSubmit, + onSaveAndClose: handleSaveAndClose, + onCancel: goBackToList, + showSave: true, + showSaveAndClose: true, + }), + [saving, isEdit, handleSubmit, handleSaveAndClose, goBackToList], + ) const handleUnsavedDialogSave = async () => { const ok = await performSaveAttempt({ fromUnsavedDialog: true }) @@ -1162,27 +1195,28 @@ function ExerciseFormPageRoot() { } return ( -
-
- - {isEdit && ( - - )} -
+ +

+ ) : null}
-

{isEdit ? 'Übung bearbeiten' : 'Neue Übung'}

- -
+
) : null} - -
- -
@@ -2439,8 +2467,9 @@ function ExerciseFormPageRoot() { isBusy={saving} onSave={handleUnsavedDialogSave} onDiscardWithoutSave={() => setFormDirty(false)} + detail="Du hast ungespeicherte Änderungen vorgenommen. Möchtest du die Seite wirklich verlassen?" /> -
+ ) }