diff --git a/backend/version.py b/backend/version.py index 2a799c3..cbdf9da 100644 --- a/backend/version.py +++ b/backend/version.py @@ -1,6 +1,6 @@ # Shinkan Jinkendo Version Information -APP_VERSION = "0.8.163" +APP_VERSION = "0.8.164" BUILD_DATE = "2026-05-31" DB_SCHEMA_VERSION = "20260531071" @@ -42,6 +42,13 @@ MODULE_VERSIONS = { } CHANGELOG = [ + { + "version": "0.8.164", + "date": "2026-05-31", + "changes": [ + "Planung/Übungspicker: Schnellanlage nutzt suggestExerciseAi (Anleitung, Kurzbeschreibung, Fähigkeiten); Fokusbereich Pflichtfeld.", + ], + }, { "version": "0.8.163", "date": "2026-05-31", diff --git a/frontend/src/components/ExercisePickerModal.jsx b/frontend/src/components/ExercisePickerModal.jsx index c869498..b9c5589 100644 --- a/frontend/src/components/ExercisePickerModal.jsx +++ b/frontend/src/components/ExercisePickerModal.jsx @@ -17,16 +17,13 @@ import { import SkillTreeMultiSelect from './SkillTreeMultiSelect' import ExerciseFocusRulePicker from './ExerciseFocusRulePicker' import CatalogRulePicker from './CatalogRulePicker' +import { buildQuickCreateExercisePayload } from '../utils/exerciseAiQuickCreate' const PAGE_SIZE = 100 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, @@ -59,8 +56,10 @@ export default function ExercisePickerModal({ const [multiPicked, setMultiPicked] = useState([]) const [quickOpen, setQuickOpen] = useState(false) const [quickTitle, setQuickTitle] = useState('') - const [quickSummary, setQuickSummary] = useState('') + const [quickSketch, setQuickSketch] = useState('') + const [quickFocusAreaId, setQuickFocusAreaId] = useState('') const [quickSaving, setQuickSaving] = useState(false) + const [quickAiError, setQuickAiError] = useState('') const pickerScrollRef = useRef(null) const toggleMultiPick = (ex) => { @@ -124,8 +123,10 @@ export default function ExercisePickerModal({ setMultiPicked([]) setQuickOpen(false) setQuickTitle('') - setQuickSummary('') + setQuickSketch('') + setQuickFocusAreaId('') setQuickSaving(false) + setQuickAiError('') return } setFilters(mergeExerciseListPrefsFromApi(user?.exercise_list_prefs)) @@ -291,25 +292,44 @@ export default function ExercisePickerModal({ alert('Titel: mindestens 3 Zeichen.') return } - const summaryRaw = (quickSummary || '').trim() + const sketch = (quickSketch || '').trim() + if (!sketch) { + alert('Bitte eine kurze Skizze / Idee eingeben — die KI erzeugt daraus die Anleitung.') + return + } + const focusId = parseInt(String(quickFocusAreaId).trim(), 10) + if (!Number.isFinite(focusId) || focusId < 1) { + alert('Bitte einen Fokusbereich wählen (für Fähigkeiten-Vorschläge).') + return + } + + const focusRow = (catalogs.focusAreas || []).find((x) => Number(x.id) === focusId) + const focusHint = (focusRow?.name || '').trim() + + setQuickAiError('') setQuickSaving(true) try { - const created = await api.createExercise({ + const aiRes = await api.suggestExerciseAi({ 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, + goal: sketch, + execution: '', + preparation: '', + trainer_notes: '', + focus_area_hint: focusHint || undefined, + focus_areas_context: [{ focus_area_id: focusId, is_primary: true }], + include_summary: true, + include_skills: true, + include_instructions: true, }) + + const payload = buildQuickCreateExercisePayload({ + title, + focusAreaId: focusId, + sketchPlain: sketch, + apiRes: aiRes, + }) + + const created = await api.createExercise(payload) if (!created?.id) { throw new Error('Anlegen fehlgeschlagen') } @@ -321,7 +341,9 @@ export default function ExercisePickerModal({ onClose() } catch (e) { console.error(e) - alert(e.message || 'Übung konnte nicht angelegt werden') + const msg = e?.message || String(e) + setQuickAiError(msg) + alert(msg || 'Übung konnte nicht angelegt werden') } finally { setQuickSaving(false) } @@ -369,18 +391,18 @@ export default function ExercisePickerModal({ onClick={() => setQuickOpen((v) => !v)} aria-expanded={quickOpen} > - {quickOpen ? 'Neue Übung ausblenden' : 'Neue Übung anlegen (Entwurf, privat)'} + {quickOpen ? 'Neue Übung ausblenden' : 'Neue Übung mit KI anlegen'} - {quickOpen ? ( + {quickOpen ? (

- Wird mit Freigabelevel privat und Status Entwurf gespeichert und - erscheint auf dem Dashboard zum Weiterbearbeiten. Nach dem Speichern wird die Übung direkt in den - Ablauf übernommen. + Die KI erzeugt aus Titel und Skizze Anleitung (Ziel, Durchführung, Vorbereitung, + Trainer-Hinweise), eine Kurzbeschreibung und Fähigkeiten. Gespeichert + als Entwurf (privat) und direkt in den Ablauf übernommen. Benötigt OpenRouter auf dem Server.

-
+
+