From c6a7d668c5c9fafc97f22aff8ba22ef904d0f7d9 Mon Sep 17 00:00:00 2001 From: Lars Date: Thu, 7 May 2026 09:20:19 +0200 Subject: [PATCH] feat: add quick create draft functionality in ExercisePickerModal - Introduced a quick create draft feature allowing users to create private exercise drafts directly from the ExercisePickerModal. - Added state management for quick create inputs including title and summary, with validation for minimum title length. - Updated the Dashboard to display a preview of private exercise drafts, enhancing user visibility of pending exercises. - Enabled quick create functionality in TrainingFrameworkProgramEditPage and TrainingPlanningPage for streamlined exercise management. --- .../src/components/ExercisePickerModal.jsx | 124 ++++++++++++++++++ frontend/src/pages/Dashboard.jsx | 32 ++++- .../TrainingFrameworkProgramEditPage.jsx | 1 + frontend/src/pages/TrainingPlanningPage.jsx | 1 + 4 files changed, 156 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/ExercisePickerModal.jsx b/frontend/src/components/ExercisePickerModal.jsx index 658dc2f..bcf9e15 100644 --- a/frontend/src/components/ExercisePickerModal.jsx +++ b/frontend/src/components/ExercisePickerModal.jsx @@ -21,12 +21,17 @@ const LEVEL_FILTER_OPTS = SKILL_LEVEL_OPTIONS.filter((o) => o.level != null) const INITIAL_FILTERS = { ...INITIAL_EXERCISE_LIST_FILTERS } +/** Stub-Ziel für API-Validator (mind. Ziel oder Durchführung); Nutzer ergänzt Details in der Übungsbearbeitung. */ +const QUICK_CREATE_GOAL_PLACEHOLDER = + 'Aus der Trainingsplanung angelegt — bitte Ziel und Durchführung in der Übungsbearbeitung ergänzen.' + export default function ExercisePickerModal({ open, onClose, onSelectExercise, multiSelect = false, onSelectExercises = null, + enableQuickCreateDraft = false, }) { const { user } = useAuth() const [catalogs, setCatalogs] = useState({ @@ -49,6 +54,10 @@ export default function ExercisePickerModal({ const [offset, setOffset] = useState(0) const [hasMore, setHasMore] = useState(false) const [multiPicked, setMultiPicked] = useState([]) + const [quickOpen, setQuickOpen] = useState(false) + const [quickTitle, setQuickTitle] = useState('') + const [quickSummary, setQuickSummary] = useState('') + const [quickSaving, setQuickSaving] = useState(false) const toggleMultiPick = (ex) => { setMultiPicked((prev) => @@ -110,6 +119,10 @@ export default function ExercisePickerModal({ setOffset(0) setHasMore(false) setMultiPicked([]) + setQuickOpen(false) + setQuickTitle('') + setQuickSummary('') + setQuickSaving(false) return } setFilters(mergeExerciseListPrefsFromApi(user?.exercise_list_prefs)) @@ -256,6 +269,48 @@ export default function ExercisePickerModal({ const resetFilters = () => setFilters({ ...INITIAL_FILTERS }) + const submitQuickCreate = async () => { + const title = (quickTitle || '').trim() + if (title.length < 3) { + alert('Titel: mindestens 3 Zeichen.') + return + } + const summaryRaw = (quickSummary || '').trim() + setQuickSaving(true) + try { + const created = await api.createExercise({ + title, + summary: summaryRaw || null, + goal: QUICK_CREATE_GOAL_PLACEHOLDER, + execution: null, + visibility: 'private', + status: 'draft', + equipment: [], + focus_areas_multi: [], + training_styles_multi: [], + training_types_multi: [], + target_groups_multi: [], + age_groups: [], + skills: [], + club_id: null, + }) + if (!created?.id) { + throw new Error('Anlegen fehlgeschlagen') + } + if (multiSelect && typeof onSelectExercises === 'function') { + await Promise.resolve(onSelectExercises([created])) + } else if (typeof onSelectExercise === 'function') { + await Promise.resolve(onSelectExercise(created)) + } + onClose() + } catch (e) { + console.error(e) + alert(e.message || 'Übung konnte nicht angelegt werden') + } finally { + setQuickSaving(false) + } + } + if (!open) return null return ( @@ -282,6 +337,75 @@ export default function ExercisePickerModal({ + {enableQuickCreateDraft ? ( +
+ + {quickOpen ? ( +
+

+ Wird mit Sichtbarkeit privat und Status Entwurf gespeichert und + erscheint auf dem Dashboard zum Weiterbearbeiten. Nach dem Speichern wird die Übung direkt in den + Ablauf übernommen. +

+
+ + setQuickTitle(e.target.value)} + autoComplete="off" + minLength={3} + maxLength={300} + placeholder="z. B. Partnerübung Abwehr" + /> +
+
+ +