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 }}
+
+
+
+
+
+
+
+
+
Ü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;
}