feat(vocab): enhance feedback acknowledgment and localization in vocabulary lessons
All checks were successful
Deploy to production / deploy (push) Successful in 2m41s
All checks were successful
Deploy to production / deploy (push) Successful in 2m41s
- Added acknowledgment messages for exercise reinforcement in German, English, and Spanish localization files to improve user guidance. - Updated VocabLessonView to include a feedback acknowledgment button, enhancing user interaction after answering questions. - Implemented logic to track feedback acknowledgment state, improving the flow of lesson reviews and user experience.
This commit is contained in:
@@ -49,6 +49,14 @@
|
||||
>
|
||||
{{ $t('socialnetwork.vocab.courses.checkAnswer') }}
|
||||
</button>
|
||||
<button
|
||||
v-if="needsFeedbackAck"
|
||||
type="button"
|
||||
class="button-secondary"
|
||||
@click="advanceAfterFeedback"
|
||||
>
|
||||
Gelesen, weiter
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p v-if="feedback" class="review-feedback" :class="feedbackCorrect ? 'ok' : 'bad'">
|
||||
@@ -80,8 +88,10 @@ export default {
|
||||
typedAnswer: '',
|
||||
feedback: '',
|
||||
feedbackCorrect: false,
|
||||
needsFeedbackAck: false,
|
||||
correctCount: 0,
|
||||
reviewDone: false
|
||||
reviewDone: false,
|
||||
weakVocabMap: {}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -103,6 +113,9 @@ export default {
|
||||
normalize(s) {
|
||||
return String(s || '').trim().toLowerCase();
|
||||
},
|
||||
getItemKey(item) {
|
||||
return `${String(item?.gloss || '').trim()}|${String(item?.target || '').trim()}`;
|
||||
},
|
||||
parseCorePatterns() {
|
||||
const raw = this.lesson?.didactics?.corePatterns || [];
|
||||
const out = [];
|
||||
@@ -151,6 +164,7 @@ export default {
|
||||
this.feedback = '';
|
||||
this.selectedOption = '';
|
||||
this.typedAnswer = '';
|
||||
this.needsFeedbackAck = false;
|
||||
if (!this.currentItem) return;
|
||||
this.mode = this.currentItem.gloss ? 'multiple_choice' : 'typing';
|
||||
if (this.mode === 'multiple_choice') {
|
||||
@@ -170,26 +184,85 @@ export default {
|
||||
if (isCorrect) {
|
||||
this.correctCount += 1;
|
||||
this.feedback = this.$t('socialnetwork.vocab.courses.correct');
|
||||
this.needsFeedbackAck = false;
|
||||
window.setTimeout(() => {
|
||||
this.advanceAfterFeedback();
|
||||
}, 550);
|
||||
} else {
|
||||
this.feedback = `${this.$t('socialnetwork.vocab.courses.wrong')} - ${this.$t('socialnetwork.vocab.courses.correctAnswer')}: ${this.mode === 'multiple_choice' ? this.currentItem.gloss : this.currentItem.target}`;
|
||||
this.needsFeedbackAck = true;
|
||||
const key = this.getItemKey(this.currentItem);
|
||||
const existing = this.weakVocabMap[key] || {
|
||||
learning: this.currentItem.gloss || '',
|
||||
reference: this.currentItem.target || '',
|
||||
wrongCount: 0,
|
||||
lastWrongAt: ''
|
||||
};
|
||||
existing.wrongCount += 1;
|
||||
existing.lastWrongAt = new Date().toISOString();
|
||||
this.weakVocabMap[key] = existing;
|
||||
}
|
||||
|
||||
window.setTimeout(async () => {
|
||||
this.currentIndex += 1;
|
||||
if (this.currentIndex >= this.reviewQueue.length) {
|
||||
await this.finishReview();
|
||||
return;
|
||||
}
|
||||
this.setupCurrent();
|
||||
}, 500);
|
||||
},
|
||||
async advanceAfterFeedback() {
|
||||
this.currentIndex += 1;
|
||||
if (this.currentIndex >= this.reviewQueue.length) {
|
||||
await this.finishReview();
|
||||
return;
|
||||
}
|
||||
this.setupCurrent();
|
||||
},
|
||||
async finishReview() {
|
||||
this.reviewDone = true;
|
||||
try {
|
||||
let mergedWeak = Object.values(this.weakVocabMap);
|
||||
try {
|
||||
const { data: progressList } = await apiClient.get(`/api/vocab/courses/${this.courseId}/progress`);
|
||||
const existingProgress = Array.isArray(progressList)
|
||||
? progressList.find((p) => Number(p.lessonId) === Number(this.lessonId))
|
||||
: null;
|
||||
const existingWeak = Array.isArray(existingProgress?.lessonState?.reviewWeakVocab)
|
||||
? existingProgress.lessonState.reviewWeakVocab
|
||||
: [];
|
||||
const map = new Map();
|
||||
existingWeak.forEach((entry) => {
|
||||
const key = `${String(entry?.learning || '').trim()}|${String(entry?.reference || '').trim()}`;
|
||||
if (!key) return;
|
||||
map.set(key, {
|
||||
learning: String(entry?.learning || '').trim(),
|
||||
reference: String(entry?.reference || '').trim(),
|
||||
wrongCount: Math.max(0, Number(entry?.wrongCount) || 0),
|
||||
lastWrongAt: String(entry?.lastWrongAt || '')
|
||||
});
|
||||
});
|
||||
mergedWeak.forEach((entry) => {
|
||||
const key = `${String(entry?.learning || '').trim()}|${String(entry?.reference || '').trim()}`;
|
||||
if (!key) return;
|
||||
const prev = map.get(key);
|
||||
if (!prev) {
|
||||
map.set(key, entry);
|
||||
} else {
|
||||
map.set(key, {
|
||||
learning: prev.learning || entry.learning,
|
||||
reference: prev.reference || entry.reference,
|
||||
wrongCount: Math.max(0, Number(prev.wrongCount) || 0) + Math.max(0, Number(entry.wrongCount) || 0),
|
||||
lastWrongAt: entry.lastWrongAt || prev.lastWrongAt
|
||||
});
|
||||
}
|
||||
});
|
||||
mergedWeak = Array.from(map.values())
|
||||
.filter((entry) => entry.learning && entry.reference)
|
||||
.sort((a, b) => (b.wrongCount || 0) - (a.wrongCount || 0))
|
||||
.slice(0, 40);
|
||||
} catch (mergeErr) {
|
||||
console.warn('Konnte bestehende Review-Schwachstellen nicht laden:', mergeErr);
|
||||
}
|
||||
await apiClient.put(`/api/vocab/lessons/${this.lessonId}/progress`, {
|
||||
completed: true,
|
||||
score: 100,
|
||||
timeSpentMinutes: 1
|
||||
timeSpentMinutes: 1,
|
||||
lessonState: {
|
||||
reviewWeakVocab: mergedWeak
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('Review-Fortschritt konnte nicht gespeichert werden:', e);
|
||||
|
||||
Reference in New Issue
Block a user