diff --git a/frontend/src/i18n/locales/de/socialnetwork.json b/frontend/src/i18n/locales/de/socialnetwork.json index 609ecfe..012b9e4 100644 --- a/frontend/src/i18n/locales/de/socialnetwork.json +++ b/frontend/src/i18n/locales/de/socialnetwork.json @@ -359,7 +359,14 @@ "forAllLanguages": "Für alle Sprachen", "optional": "Optional", "invalidCode": "Ungültiger Code", - "courseNotFound": "Kurs nicht gefunden" + "courseNotFound": "Kurs nicht gefunden", + "grammarExercises": "Grammatik-Übungen", + "noExercises": "Keine Übungen verfügbar", + "enterAnswer": "Antwort eingeben", + "checkAnswer": "Antwort prüfen", + "correct": "Richtig!", + "wrong": "Falsch", + "explanation": "Erklärung" } } } diff --git a/frontend/src/i18n/locales/en/socialnetwork.json b/frontend/src/i18n/locales/en/socialnetwork.json index 0e2a70f..405d342 100644 --- a/frontend/src/i18n/locales/en/socialnetwork.json +++ b/frontend/src/i18n/locales/en/socialnetwork.json @@ -359,7 +359,14 @@ "forAllLanguages": "For All Languages", "optional": "Optional", "invalidCode": "Invalid code", - "courseNotFound": "Course not found" + "courseNotFound": "Course not found", + "grammarExercises": "Grammar Exercises", + "noExercises": "No exercises available", + "enterAnswer": "Enter answer", + "checkAnswer": "Check Answer", + "correct": "Correct!", + "wrong": "Wrong", + "explanation": "Explanation" } } } diff --git a/frontend/src/views/social/VocabLessonView.vue b/frontend/src/views/social/VocabLessonView.vue index 37e0597..8f4aef2 100644 --- a/frontend/src/views/social/VocabLessonView.vue +++ b/frontend/src/views/social/VocabLessonView.vue @@ -13,15 +13,73 @@

{{ $t('socialnetwork.vocab.courses.grammarExercises') }}

{{ exercise.title }}

-

{{ exercise.description }}

-
-

- - -
- {{ exerciseResults[exercise.id].correct ? $t('socialnetwork.vocab.courses.correct') : $t('socialnetwork.vocab.courses.wrong') }} -

{{ exercise.explanation }}

+

{{ exercise.instruction }}

+ + +
+

{{ getQuestionText(exercise) }}

+
+
+ +
+ {{ exerciseResults[exercise.id].correct ? $t('socialnetwork.vocab.courses.correct') : $t('socialnetwork.vocab.courses.wrong') }} +

{{ exercise.explanation }}

+
+
+ + +
+

+
+ +
+ +
+ {{ exerciseResults[exercise.id].correct ? $t('socialnetwork.vocab.courses.correct') : $t('socialnetwork.vocab.courses.wrong') }} +

{{ exercise.explanation }}

+
+
+ + +
+

{{ getQuestionText(exercise) }}

+ + +
+ {{ exerciseResults[exercise.id].correct ? $t('socialnetwork.vocab.courses.correct') : $t('socialnetwork.vocab.courses.wrong') }} +

{{ exercise.explanation }}

+
+
+ + +
+

Übungstyp: {{ getExerciseType(exercise) }}

+
{{ JSON.stringify(exercise, null, 2) }}
@@ -87,23 +145,115 @@ export default { async loadGrammarExercises() { try { const res = await apiClient.get(`/api/vocab/lessons/${this.lessonId}/grammar-exercises`); - this.lesson.grammarExercises = res.data || []; + const exercises = res.data || []; + this.lesson.grammarExercises = exercises; + + // Initialisiere Antwort-Arrays für Gap Fill Übungen + exercises.forEach(exercise => { + const exerciseType = this.getExerciseType(exercise); + if (exerciseType === 'gap_fill') { + const gapCount = this.getGapCount(exercise); + this.$set(this.exerciseAnswers, exercise.id, new Array(gapCount).fill('')); + } else { + this.$set(this.exerciseAnswers, exercise.id, ''); + } + }); } catch (e) { console.error('Konnte Grammatik-Übungen nicht laden:', e); this.lesson.grammarExercises = []; } }, - formatGapFill(content) { - // Ersetze Platzhalter mit Input-Feldern - return content.replace(/\{gap\}/g, '_____'); + getExerciseType(exercise) { + // Hole den Typ aus questionData oder exerciseType + if (exercise.questionData) { + const qData = typeof exercise.questionData === 'string' + ? JSON.parse(exercise.questionData) + : exercise.questionData; + return qData.type; + } + // Fallback: Prüfe exerciseTypeId + const typeMap = { + 1: 'gap_fill', + 2: 'multiple_choice', + 3: 'sentence_building', + 4: 'transformation', + 5: 'conjugation', + 6: 'declension' + }; + return typeMap[exercise.exerciseTypeId] || 'unknown'; + }, + getQuestionData(exercise) { + if (!exercise.questionData) return null; + return typeof exercise.questionData === 'string' + ? JSON.parse(exercise.questionData) + : exercise.questionData; + }, + getAnswerData(exercise) { + if (!exercise.answerData) return null; + return typeof exercise.answerData === 'string' + ? JSON.parse(exercise.answerData) + : exercise.answerData; + }, + getQuestionText(exercise) { + const qData = this.getQuestionData(exercise); + if (!qData) return exercise.title; + + if (qData.question) return qData.question; + if (qData.text) return qData.text; + return exercise.title; + }, + getOptions(exercise) { + const qData = this.getQuestionData(exercise); + return qData?.options || []; + }, + formatGapFill(exercise) { + const qData = this.getQuestionData(exercise); + if (!qData || !qData.text) return ''; + + // Ersetze {gap} mit Platzhaltern + return qData.text.replace(/\{gap\}/g, '_____'); + }, + getGapCount(exercise) { + const qData = this.getQuestionData(exercise); + if (!qData) return 0; + + // Zähle {gap} im Text + const matches = qData.text?.match(/\{gap\}/g); + return matches ? matches.length : (qData.gaps || 1); + }, + hasAllGapsFilled(exercise) { + const answers = this.exerciseAnswers[exercise.id]; + if (!answers || !Array.isArray(answers)) return false; + const gapCount = this.getGapCount(exercise); + return answers.length === gapCount && answers.every(a => a && a.trim()); }, async checkAnswer(exerciseId) { try { - const answer = this.exerciseAnswers[exerciseId]; - const res = await apiClient.post(`/api/vocab/exercises/${exerciseId}/check`, { answer }); - this.exerciseResults[exerciseId] = res.data; + const exercise = this.lesson.grammarExercises.find(e => e.id === exerciseId); + if (!exercise) return; + + const exerciseType = this.getExerciseType(exercise); + let answer = this.exerciseAnswers[exerciseId]; + + // Formatiere Antwort je nach Typ + if (exerciseType === 'gap_fill') { + // Gap Fill: Array von Antworten + if (!Array.isArray(answer)) { + answer = [answer]; + } + } else if (exerciseType === 'multiple_choice') { + // Multiple Choice: Index als Zahl + answer = Number(answer); + } else if (exerciseType === 'transformation') { + // Transformation: String + answer = String(answer || '').trim(); + } + + const res = await apiClient.post(`/api/vocab/grammar-exercises/${exerciseId}/check`, { answer }); + this.$set(this.exerciseResults, exerciseId, res.data); } catch (e) { console.error('Fehler beim Prüfen der Antwort:', e); + alert(e.response?.data?.error || 'Fehler beim Prüfen der Antwort'); } }, back() { @@ -153,27 +303,130 @@ export default { margin-bottom: 15px; } -.gap-fill-exercise input { +.exercise-instruction { + color: #666; + font-style: italic; + margin-bottom: 15px; +} + +.exercise-question { + font-weight: 600; + margin-bottom: 15px; + font-size: 1.1em; +} + +.exercise-text { + margin-bottom: 15px; + line-height: 1.6; +} + +.gap { + display: inline-block; + min-width: 100px; + border-bottom: 2px solid #333; + margin: 0 5px; + padding: 0 5px; +} + +.multiple-choice-exercise .options { + margin: 15px 0; +} + +.option-label { + display: block; + padding: 10px; + margin: 8px 0; + border: 1px solid #ddd; + border-radius: 4px; + cursor: pointer; + transition: background-color 0.2s; +} + +.option-label:hover { + background-color: #f5f5f5; +} + +.option-label input[type="radio"] { + margin-right: 10px; +} + +.gap-fill-exercise .gap-inputs { + display: flex; + flex-direction: column; + gap: 10px; + margin: 15px 0; +} + +.gap-input { width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; - margin: 10px 0; +} + +.transformation-input { + width: 100%; + padding: 8px; + border: 1px solid #ddd; + border-radius: 4px; + margin: 15px 0; +} + +.exercise-item button { + padding: 10px 20px; + background: #4CAF50; + color: white; + border: none; + border-radius: 4px; + cursor: pointer; + font-size: 1em; + margin-top: 10px; + transition: background-color 0.2s; +} + +.exercise-item button:hover:not(:disabled) { + background: #45a049; +} + +.exercise-item button:disabled { + background: #ccc; + cursor: not-allowed; } .exercise-result { - margin-top: 10px; - padding: 10px; + margin-top: 15px; + padding: 15px; border-radius: 4px; } .exercise-result.correct { background: #d4edda; color: #155724; + border: 1px solid #c3e6cb; } .exercise-result.wrong { background: #f8d7da; color: #721c24; + border: 1px solid #f5c6cb; +} + +.exercise-explanation { + margin-top: 10px; + font-style: italic; +} + +.unknown-exercise { + padding: 15px; + background: #fff3cd; + border: 1px solid #ffc107; + border-radius: 4px; + margin-top: 10px; +} + +.unknown-exercise pre { + margin-top: 10px; + font-size: 0.85em; + overflow-x: auto; }