diff --git a/backend/services/vocabService.js b/backend/services/vocabService.js
index 2227d4f..acd920b 100644
--- a/backend/services/vocabService.js
+++ b/backend/services/vocabService.js
@@ -928,6 +928,16 @@ export default class VocabService {
if (!reference || !learning) {
return;
}
+
+ // Heuristik: Vermeide, einzelne sehr kurze Ziel-Token (z.B. "ko")
+ // automatisch mit mehrwortigen Glosses (z.B. "Ich arbeite") zu koppeln.
+ // Das verhindert fehlerhafte Glosszuweisungen für Partikeln/Pronomina.
+ const compactRefLen = reference.replace(/\s+/g, '').length;
+ const learningWordCount = this._wordCount(learning);
+ if (compactRefLen <= 3 && learningWordCount > 1) {
+ return;
+ }
+
if (!glossByReference.has(reference)) {
glossByReference.set(reference, learning);
}
@@ -1885,6 +1895,11 @@ export default class VocabService {
const rows = validDueRows.slice(0, limit);
const totalDueCount = validDueRows.length;
+ // Debug: Logge Anzahl fälliger SRS-Items (nur in Entwicklung sichtbar)
+ try {
+ console.debug('[VocabService] getCourseSrsDue', { userId: user.id, courseId: course.id, totalDueCount });
+ } catch (_) {}
+
return {
courseId: course.id,
dueAt: now.toISOString(),
@@ -1992,6 +2007,18 @@ export default class VocabService {
}
await item.save();
+ // Debug: Logge SRS-Updates, damit wir sehen, ob Reviews ankommen
+ try {
+ console.debug('[VocabService] reviewSrsItem saved', {
+ userId: user.id,
+ courseId: courseId,
+ itemKey: item.itemKey,
+ correct: correct,
+ nextDueAt: this._normalizeIsoDate(item.nextDueAt),
+ stage: item.stage
+ });
+ } catch (_) {}
+
return {
itemKey: item.itemKey,
correct,
diff --git a/frontend/src/dialogues/socialnetwork/VocabPracticeDialog.vue b/frontend/src/dialogues/socialnetwork/VocabPracticeDialog.vue
index f938ca3..45581eb 100644
--- a/frontend/src/dialogues/socialnetwork/VocabPracticeDialog.vue
+++ b/frontend/src/dialogues/socialnetwork/VocabPracticeDialog.vue
@@ -28,7 +28,7 @@
{{ $t('socialnetwork.vocab.practice.noPool') }}
-
+
{{ $t('socialnetwork.vocab.practice.srsFinishedTitle') }}
{{ $t('socialnetwork.vocab.practice.srsFinishedDesc') }}
@@ -180,6 +180,7 @@ export default {
// Stored per day and course so the user can close/reopen without starting over.
srsSession: null, // { version, dateKey, courseId, initialTotalDue, initialDueIds, doneIds, correctCount, wrongCount }
srsQueueIds: [], // remaining due ids for this session, in due order
+ srsServerTotalDue: null,
// session stats
correctCount: 0,
@@ -517,7 +518,8 @@ export default {
version: SRS_SESSION_STORAGE_VERSION,
dateKey: this.getLocalDateKey(),
courseId: this.openParams.courseId,
- initialTotalDue: dueIds.length,
+ // Prefer server-reported total due count when available (shows full-course due count)
+ initialTotalDue: Number.isFinite(Number(this.srsServerTotalDue)) ? Number(this.srsServerTotalDue) : dueIds.length,
initialDueIds: dueIds,
doneIds: [],
correctCount: 0,
@@ -761,9 +763,29 @@ export default {
this.next();
return;
}
- this.loading = true;
+ this.loading = true;
try {
let res;
+ let courseDueRes = null;
+ // Wenn SRS-Modus auf Kurs-Ebene, lade kursweite fällige Items (Server liefert totalDueCount)
+ if (this.srsMode && this.openParams.courseId && !this.openParams.lessonId) {
+ try {
+ courseDueRes = await apiClient.get(`/api/vocab/courses/${this.openParams.courseId}/srs/due`, { params: { limit: 100 } });
+ } catch (e) {
+ // ignore, fallback to lesson/chapters/vocabs endpoints
+ courseDueRes = null;
+ }
+ }
+ if (courseDueRes && Array.isArray(courseDueRes.data?.items) && courseDueRes.data.items.length > 0) {
+ // Map server items to pool shape (use itemKey as id)
+ this.pool = this.normalizePool((courseDueRes.data.items || []).map((it) => ({
+ id: it.itemKey,
+ learning: it.learning,
+ reference: it.reference,
+ lessonId: it.lessonId || null
+ })));
+ this.srsServerTotalDue = Number.isFinite(Number(courseDueRes.data?.totalDueCount)) ? Number(courseDueRes.data.totalDueCount) : null;
+ } else {
if (this.openParams.lessonId) {
if (this.allVocabs && this.openParams.courseId) {
res = await apiClient.get(`/api/vocab/courses/${this.openParams.courseId}/completed-lesson-vocabs`, {
@@ -782,7 +804,7 @@ export default {
} else {
res = await apiClient.get(`/api/vocab/chapters/${this.openParams.chapterId}/vocabs`);
this.pool = this.normalizePool(res.data?.vocabs || []);
- }
+ }
} catch (e) {
console.error('Reload pool failed:', e);
this.pool = [];