From f46c864bbcf98e0037bd24dfda03145255371f31 Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Fri, 10 Apr 2026 14:26:01 +0200 Subject: [PATCH] feat(vocab-lesson): implement pagination for vocabulary overview in lesson view - Added pagination functionality to the vocabulary overview in the VocabLessonView component, allowing users to navigate through vocabulary items more efficiently. - Introduced new localization entries for pagination controls in Cebuano, German, English, Spanish, and French, ensuring a consistent user experience across languages. - Enhanced the UI with buttons for previous and next navigation, improving accessibility and usability of the vocabulary list. --- .../src/i18n/locales/ceb/socialnetwork.json | 3 + .../src/i18n/locales/de/socialnetwork.json | 3 + .../src/i18n/locales/en/socialnetwork.json | 3 + .../src/i18n/locales/es/socialnetwork.json | 3 + .../src/i18n/locales/fr/socialnetwork.json | 3 + frontend/src/views/social/VocabLessonView.vue | 109 +++++++++++++++++- 6 files changed, 122 insertions(+), 2 deletions(-) diff --git a/frontend/src/i18n/locales/ceb/socialnetwork.json b/frontend/src/i18n/locales/ceb/socialnetwork.json index 24d4c32..f726b50 100644 --- a/frontend/src/i18n/locales/ceb/socialnetwork.json +++ b/frontend/src/i18n/locales/ceb/socialnetwork.json @@ -617,6 +617,9 @@ "deepenSectionTitle": "Deepen ug review", "assistantSectionTitle": "Deepen uban sa pinulongan katabang", "vocabOverviewToggle": "Show full overview sa terms", + "vocabOverviewPrev": "Balik", + "vocabOverviewNext": "Sunod", + "vocabOverviewPager": "{from}–{to} sa {total} · Page {page} sa {pages}", "vocabTrainerLockedHint": "Palihog confirm two preparation steps under “Preparation sa wala pa ang bokabularyo trainer” una.", "exerciseUnlockHintAfterPrep": "Work through ang prepared terms una. Ang tsek sa kapitulo will unlock afterwards.", "speakingTasks": "Speaking Tasks", diff --git a/frontend/src/i18n/locales/de/socialnetwork.json b/frontend/src/i18n/locales/de/socialnetwork.json index 430c651..5086e72 100644 --- a/frontend/src/i18n/locales/de/socialnetwork.json +++ b/frontend/src/i18n/locales/de/socialnetwork.json @@ -586,6 +586,9 @@ "deepenSectionTitle": "Vertiefen und nachlesen", "assistantSectionTitle": "Mit Sprachassistent vertiefen", "vocabOverviewToggle": "Gesamtübersicht der Begriffe anzeigen", + "vocabOverviewPrev": "Zurück", + "vocabOverviewNext": "Weiter", + "vocabOverviewPager": "{from}–{to} von {total} · Seite {page} von {pages}", "vocabTrainerLockedHint": "Bitte bestätige zuerst zwei Lern-Durchgänge bei „Vorbereitung vor dem Vokabeltrainer“.", "exerciseUnlockHintAfterPrep": "Arbeite zuerst die vorbereiteten Begriffe durch. Danach wird die Kapitel-Prüfung freigeschaltet.", "speakingTasks": "Sprechaufträge", diff --git a/frontend/src/i18n/locales/en/socialnetwork.json b/frontend/src/i18n/locales/en/socialnetwork.json index 6960989..6e55879 100644 --- a/frontend/src/i18n/locales/en/socialnetwork.json +++ b/frontend/src/i18n/locales/en/socialnetwork.json @@ -586,6 +586,9 @@ "deepenSectionTitle": "Deepen and review", "assistantSectionTitle": "Deepen with language assistant", "vocabOverviewToggle": "Show full overview of terms", + "vocabOverviewPrev": "Previous", + "vocabOverviewNext": "Next", + "vocabOverviewPager": "{from}–{to} of {total} · Page {page} of {pages}", "vocabTrainerLockedHint": "Please confirm two preparation steps under “Preparation before the vocabulary trainer” first.", "exerciseUnlockHintAfterPrep": "Work through the prepared terms first. The chapter test will unlock afterwards.", "speakingTasks": "Speaking Tasks", diff --git a/frontend/src/i18n/locales/es/socialnetwork.json b/frontend/src/i18n/locales/es/socialnetwork.json index 1ca2250..e541262 100644 --- a/frontend/src/i18n/locales/es/socialnetwork.json +++ b/frontend/src/i18n/locales/es/socialnetwork.json @@ -584,6 +584,9 @@ "deepenSectionTitle": "Profundizar y repasar", "assistantSectionTitle": "Profundizar con el asistente de idiomas", "vocabOverviewToggle": "Mostrar vista general completa de los términos", + "vocabOverviewPrev": "Anterior", + "vocabOverviewNext": "Siguiente", + "vocabOverviewPager": "{from}–{to} de {total} · Página {page} de {pages}", "vocabTrainerLockedHint": "Confirma primero los dos pasos de preparación arriba.", "exerciseUnlockHintAfterPrep": "Primero recorre los términos preparados. Después se desbloqueará la prueba del capítulo.", "speakingTasks": "Tareas orales", diff --git a/frontend/src/i18n/locales/fr/socialnetwork.json b/frontend/src/i18n/locales/fr/socialnetwork.json index 0511ad9..3bd0c3e 100644 --- a/frontend/src/i18n/locales/fr/socialnetwork.json +++ b/frontend/src/i18n/locales/fr/socialnetwork.json @@ -584,6 +584,9 @@ "deepenSectionTitle": "Approfondissez et lisez", "assistantSectionTitle": "Approfondissez avec l'assistant vocal", "vocabOverviewToggle": "Afficher un aperçu complet des termes", + "vocabOverviewPrev": "Précédent", + "vocabOverviewNext": "Suivant", + "vocabOverviewPager": "{from}–{to} sur {total} · Page {page} sur {pages}", "vocabTrainerLockedHint": "Veuillez d'abord confirmer deux séances d'apprentissage dans « Préparation avant l'entraîneur de vocabulaire ».", "exerciseUnlockHintAfterPrep": "Commencez par parcourir les termes préparés. L’examen du chapitre sera alors débloqué.", "speakingTasks": "Travaux oraux", diff --git a/frontend/src/views/social/VocabLessonView.vue b/frontend/src/views/social/VocabLessonView.vue index 1f29c19..19d38e8 100644 --- a/frontend/src/views/social/VocabLessonView.vue +++ b/frontend/src/views/social/VocabLessonView.vue @@ -156,12 +156,31 @@

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

-
+
{{ vocab.learning || '—' }} {{ vocab.reference }}
+
+ + {{ $t('socialnetwork.vocab.courses.vocabOverviewPager', vocabOverviewPagerMeta) }} + +
@@ -957,6 +976,8 @@ const LESSON_STATE_VERSION = 1; const VOCAB_REPEAT_INTERVALS = [1, 2, 4]; /** Mindest-Erfolgsquote im Vokabeltrainer (gesamt), damit die Kapitel-Prüfung freigeschaltet wird. */ const EXERCISE_UNLOCK_MIN_SUCCESS_PERCENT = 70; +/** Max. Zeilen pro Seite in der einklappbaren Vokabel-Gesamtübersicht */ +const VOCAB_OVERVIEW_PAGE_SIZE = 40; export default { name: 'VocabLessonView', @@ -1046,7 +1067,9 @@ export default { lessonStateSaveTimer: null, lessonStateSaveInFlight: false, pendingLessonStatePayload: null, - resettingLessonProgress: false + resettingLessonProgress: false, + /** Seitennummer (1-basiert) für die Vokabel-Gesamtübersicht */ + vocabOverviewPage: 1 }; }, computed: { @@ -1347,6 +1370,31 @@ export default { return Array.from(vocabByReference.values()); }, + vocabOverviewTotalPages() { + const n = this.lessonVocab.length; + if (n === 0) { + return 1; + } + return Math.ceil(n / VOCAB_OVERVIEW_PAGE_SIZE); + }, + paginatedLessonVocab() { + const list = this.lessonVocab; + const totalPages = this.vocabOverviewTotalPages; + const page = Math.min(Math.max(1, this.vocabOverviewPage), totalPages); + const start = (page - 1) * VOCAB_OVERVIEW_PAGE_SIZE; + return list.slice(start, start + VOCAB_OVERVIEW_PAGE_SIZE); + }, + vocabOverviewPagerMeta() { + const n = this.lessonVocab.length; + const totalPages = this.vocabOverviewTotalPages; + const page = Math.min(Math.max(1, this.vocabOverviewPage), totalPages); + if (n === 0) { + return { from: 0, to: 0, page: 1, pages: 1, total: 0 }; + } + const from = (page - 1) * VOCAB_OVERVIEW_PAGE_SIZE + 1; + const to = Math.min(page * VOCAB_OVERVIEW_PAGE_SIZE, n); + return { from, to, page, pages: totalPages, total: n }; + }, trainableLessonVocab() { return this.lessonVocab.filter((entry) => entry.learning && entry.reference && entry.learning !== entry.reference); }, @@ -1598,9 +1646,28 @@ export default { this.isNavigatingToNext = false; this.loadLesson(); } + }, + lessonVocab: { + handler() { + const totalPages = this.vocabOverviewTotalPages; + if (this.vocabOverviewPage > totalPages) { + this.vocabOverviewPage = Math.max(1, totalPages); + } + }, + deep: true } }, methods: { + vocabOverviewGoPrev() { + if (this.vocabOverviewPagerMeta.page > 1) { + this.vocabOverviewPage -= 1; + } + }, + vocabOverviewGoNext() { + if (this.vocabOverviewPagerMeta.page < this.vocabOverviewPagerMeta.pages) { + this.vocabOverviewPage += 1; + } + }, exportPersistedExerciseAnswers() { const exportedAnswers = {}; this.effectiveExercises.forEach((exercise) => { @@ -2267,6 +2334,7 @@ export default { this.exerciseRetryPending = false; this.exerciseRetryPendingSinceAttempts = 0; this.exerciseSequentialIndex = 0; + this.vocabOverviewPage = 1; this.exercisePreparationCompleted = false; this.lessonPrepStage = 0; this.lessonPrepIndex = 0; @@ -4548,6 +4616,43 @@ export default { margin: 0 10px; } +.vocab-items-pager { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: center; + gap: 12px 16px; + margin-top: 14px; + padding-top: 12px; + border-top: 1px solid #eadfcf; +} + +.vocab-items-pager__btn { + padding: 6px 14px; + font-size: 0.9rem; + border: 1px solid #c9b896; + border-radius: 8px; + background: #faf8f3; + color: #333; + cursor: pointer; +} + +.vocab-items-pager__btn:hover:not(:disabled) { + background: #f0ebe0; +} + +.vocab-items-pager__btn:disabled { + opacity: 0.45; + cursor: not-allowed; +} + +.vocab-items-pager__meta { + font-size: 0.88rem; + color: #555; + text-align: center; + min-width: min(100%, 14rem); +} + .grammar-explanations { margin: 20px 0; padding: 15px;