feat(vocab-lesson): implement pagination for vocabulary overview in lesson view
All checks were successful
Deploy to production / deploy (push) Successful in 2m59s
All checks were successful
Deploy to production / deploy (push) Successful in 2m59s
- 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.
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -156,12 +156,31 @@
|
||||
</summary>
|
||||
<p class="vocab-info-text">{{ $t('socialnetwork.vocab.courses.vocabInfoText') }}</p>
|
||||
<div class="vocab-items">
|
||||
<div v-for="(vocab, index) in lessonVocab" :key="index" class="vocab-item">
|
||||
<div v-for="(vocab, index) in paginatedLessonVocab" :key="`v-${vocabOverviewPagerMeta.page}-${index}`" class="vocab-item">
|
||||
<strong>{{ vocab.learning || '—' }}</strong>
|
||||
<span class="separator">→</span>
|
||||
<span>{{ vocab.reference }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="vocabOverviewTotalPages > 1" class="vocab-items-pager">
|
||||
<button
|
||||
type="button"
|
||||
class="vocab-items-pager__btn"
|
||||
:disabled="vocabOverviewPagerMeta.page <= 1"
|
||||
@click="vocabOverviewGoPrev"
|
||||
>
|
||||
{{ $t('socialnetwork.vocab.courses.vocabOverviewPrev') }}
|
||||
</button>
|
||||
<span class="vocab-items-pager__meta">{{ $t('socialnetwork.vocab.courses.vocabOverviewPager', vocabOverviewPagerMeta) }}</span>
|
||||
<button
|
||||
type="button"
|
||||
class="vocab-items-pager__btn"
|
||||
:disabled="vocabOverviewPagerMeta.page >= vocabOverviewPagerMeta.pages"
|
||||
@click="vocabOverviewGoNext"
|
||||
>
|
||||
{{ $t('socialnetwork.vocab.courses.vocabOverviewNext') }}
|
||||
</button>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<div v-if="visibleGrammarExplanations.length > 0" class="lesson-grammar-impulse didactic-card">
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user