feat(vocab-prep): implement enhanced vocabulary preparation steps in VocabLessonView
All checks were successful
Deploy to production / deploy (push) Successful in 2m58s
All checks were successful
Deploy to production / deploy (push) Successful in 2m58s
- Introduced a structured vocabulary preparation process with two review stages before engaging with the vocabulary trainer. - Added localization support for new vocabulary preparation messages in German, English, and Spanish, improving accessibility for users. - Updated the VocabLessonView component to display current progress and next steps during vocabulary preparation, enhancing user guidance. - Refactored related logic to manage preparation stages and item navigation effectively.
This commit is contained in:
@@ -450,6 +450,8 @@
|
||||
"corePatternsHint": "Zuerst die Zielsprache lesen, darunter die deutsche Bedeutung — so lernst du jedes Muster bewusst in beiden Richtungen.",
|
||||
"vocabPrepTitle": "Vorbereitung vor dem Vokabeltrainer",
|
||||
"vocabPrepStep1": "Lies Kernmuster und Wortliste (Deutsch ↔ Zielsprache) einmal in Ruhe durch.",
|
||||
"vocabPrepProgress": "Durchgang {pass}: Begriff {current} von {total}",
|
||||
"vocabPrepNextItem": "Nächster Begriff",
|
||||
"vocabPrepConfirm1": "Erste Durchsicht erledigt",
|
||||
"vocabPrepStep2": "Gehe die gleichen Begriffe noch einmal durch (aktive Wiederholung, ohne zu üben).",
|
||||
"vocabPrepConfirm2": "Zweite Durchsicht erledigt",
|
||||
|
||||
@@ -450,6 +450,8 @@
|
||||
"corePatternsHint": "Read the target language first, then the meaning below — you learn each pattern both ways.",
|
||||
"vocabPrepTitle": "Preparation before the vocabulary trainer",
|
||||
"vocabPrepStep1": "Read through core patterns and the word list (native language ↔ target language) once.",
|
||||
"vocabPrepProgress": "Pass {pass}: item {current} of {total}",
|
||||
"vocabPrepNextItem": "Next item",
|
||||
"vocabPrepConfirm1": "First pass done",
|
||||
"vocabPrepStep2": "Go through the same items again (active review, not testing yet).",
|
||||
"vocabPrepConfirm2": "Second pass done",
|
||||
|
||||
@@ -448,6 +448,8 @@
|
||||
"corePatternsHint": "Primero la lengua meta; debajo, el significado en tu idioma.",
|
||||
"vocabPrepTitle": "Preparación antes del entrenador de vocabulario",
|
||||
"vocabPrepStep1": "Lee una vez los patrones clave y la lista de palabras (idioma nativo ↔ lengua meta).",
|
||||
"vocabPrepProgress": "Pasada {pass}: término {current} de {total}",
|
||||
"vocabPrepNextItem": "Siguiente término",
|
||||
"vocabPrepConfirm1": "Primera lectura hecha",
|
||||
"vocabPrepStep2": "Repasa los mismos elementos otra vez (repaso activo, aún sin practicar).",
|
||||
"vocabPrepConfirm2": "Segunda lectura hecha",
|
||||
|
||||
@@ -217,8 +217,41 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Wichtige Begriffe (vor dem Trainer: passiv, mit DE ↔ Zielsprache) -->
|
||||
<div v-if="lesson && importantVocab && importantVocab.length > 0 && !vocabTrainerActive" class="vocab-list">
|
||||
<!-- Zwei Durchgänge: dieselben Kernmuster schrittweise vor dem Trainer -->
|
||||
<div
|
||||
v-if="prepItems.length > 0 && !vocabTrainerActive"
|
||||
class="vocab-prep-pass didactic-card"
|
||||
>
|
||||
<h4>{{ $t('socialnetwork.vocab.courses.vocabPrepTitle') }}</h4>
|
||||
<template v-if="lessonPrepStage < 2 && currentPrepItem">
|
||||
<p class="vocab-prep-pass__step">
|
||||
{{ $t('socialnetwork.vocab.courses.vocabPrepProgress', { pass: lessonPrepStage + 1, current: lessonPrepIndex + 1, total: prepItems.length }) }}
|
||||
</p>
|
||||
<p>
|
||||
{{ lessonPrepStage === 0
|
||||
? $t('socialnetwork.vocab.courses.vocabPrepStep1')
|
||||
: $t('socialnetwork.vocab.courses.vocabPrepStep2') }}
|
||||
</p>
|
||||
<div class="vocab-prep-card">
|
||||
<div class="vocab-prep-card__target">{{ currentPrepItem.target }}</div>
|
||||
<div v-if="currentPrepItem.gloss" class="vocab-prep-card__gloss">{{ currentPrepItem.gloss }}</div>
|
||||
</div>
|
||||
<button type="button" class="btn-prep-pass" @click="advancePrepPass">
|
||||
{{ isLastPrepItemInPass
|
||||
? (lessonPrepStage === 0
|
||||
? $t('socialnetwork.vocab.courses.vocabPrepConfirm1')
|
||||
: $t('socialnetwork.vocab.courses.vocabPrepConfirm2'))
|
||||
: $t('socialnetwork.vocab.courses.vocabPrepNextItem') }}
|
||||
</button>
|
||||
</template>
|
||||
<p v-else class="vocab-prep-pass__ready">{{ $t('socialnetwork.vocab.courses.vocabPrepReady') }}</p>
|
||||
</div>
|
||||
|
||||
<!-- Wichtige Begriffe erst nach den zwei Durchgängen gesammelt anzeigen -->
|
||||
<div
|
||||
v-if="lesson && importantVocab && importantVocab.length > 0 && !vocabTrainerActive && lessonPrepStage >= 2"
|
||||
class="vocab-list"
|
||||
>
|
||||
<h4>{{ $t('socialnetwork.vocab.courses.importantVocab') }}</h4>
|
||||
<p class="vocab-info-text">{{ $t('socialnetwork.vocab.courses.vocabInfoText') }}</p>
|
||||
<div class="vocab-items">
|
||||
@@ -230,27 +263,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Zwei Durchgänge vor dem aktiven Üben -->
|
||||
<div
|
||||
v-if="importantVocab && importantVocab.length > 0 && !vocabTrainerActive"
|
||||
class="vocab-prep-pass didactic-card"
|
||||
>
|
||||
<h4>{{ $t('socialnetwork.vocab.courses.vocabPrepTitle') }}</h4>
|
||||
<template v-if="lessonPrepStage === 0">
|
||||
<p>{{ $t('socialnetwork.vocab.courses.vocabPrepStep1') }}</p>
|
||||
<button type="button" class="btn-prep-pass" @click="lessonPrepStage = 1">
|
||||
{{ $t('socialnetwork.vocab.courses.vocabPrepConfirm1') }}
|
||||
</button>
|
||||
</template>
|
||||
<template v-else-if="lessonPrepStage === 1">
|
||||
<p>{{ $t('socialnetwork.vocab.courses.vocabPrepStep2') }}</p>
|
||||
<button type="button" class="btn-prep-pass" @click="lessonPrepStage = 2">
|
||||
{{ $t('socialnetwork.vocab.courses.vocabPrepConfirm2') }}
|
||||
</button>
|
||||
</template>
|
||||
<p v-else class="vocab-prep-pass__ready">{{ $t('socialnetwork.vocab.courses.vocabPrepReady') }}</p>
|
||||
</div>
|
||||
|
||||
<!-- Vokabeltrainer -->
|
||||
<div v-if="importantVocab && importantVocab.length > 0" class="vocab-trainer-section">
|
||||
<h4>{{ $t('socialnetwork.vocab.courses.vocabTrainer') }}</h4>
|
||||
@@ -764,6 +776,7 @@ export default {
|
||||
exercisePreparationCompleted: false,
|
||||
/** 0 = noch kein Durchgang, 1 = erste Durchsicht, 2 = zweite Durchsicht — dann Vokabeltrainer */
|
||||
lessonPrepStage: 0,
|
||||
lessonPrepIndex: 0,
|
||||
currentVocabQuestion: null,
|
||||
vocabTrainerAnswer: '',
|
||||
vocabTrainerSelectedChoice: null,
|
||||
@@ -933,11 +946,34 @@ export default {
|
||||
.map((p) => this.normalizeCorePatternEntry(p))
|
||||
.filter(Boolean);
|
||||
},
|
||||
prepItems() {
|
||||
if (this.normalizedCorePatterns.length > 0) {
|
||||
return this.normalizedCorePatterns;
|
||||
}
|
||||
return (this.importantVocab || [])
|
||||
.map((item) => ({
|
||||
target: String(item?.reference || '').trim(),
|
||||
gloss: String(item?.learning || '').trim()
|
||||
}))
|
||||
.filter((item) => item.target);
|
||||
},
|
||||
currentPrepItem() {
|
||||
return this.prepItems[this.lessonPrepIndex] || null;
|
||||
},
|
||||
isLastPrepItemInPass() {
|
||||
if (this.prepItems.length === 0) {
|
||||
return true;
|
||||
}
|
||||
return this.lessonPrepIndex >= this.prepItems.length - 1;
|
||||
},
|
||||
corePatternsHaveGloss() {
|
||||
return this.normalizedCorePatterns.some((p) => p.gloss);
|
||||
},
|
||||
canStartVocabTrainerPrep() {
|
||||
if (!this.importantVocab || this.lessonPrepStage < 2) {
|
||||
if (!this.importantVocab || this.importantVocab.length === 0) {
|
||||
return false;
|
||||
}
|
||||
if (this.prepItems.length > 0 && this.lessonPrepStage < 2) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -1011,6 +1047,17 @@ export default {
|
||||
if (!n) return '';
|
||||
return n.gloss ? `${n.target} (${n.gloss})` : n.target;
|
||||
},
|
||||
advancePrepPass() {
|
||||
if (this.lessonPrepStage >= 2 || this.prepItems.length === 0) {
|
||||
return;
|
||||
}
|
||||
if (!this.isLastPrepItemInPass) {
|
||||
this.lessonPrepIndex += 1;
|
||||
return;
|
||||
}
|
||||
this.lessonPrepStage += 1;
|
||||
this.lessonPrepIndex = 0;
|
||||
},
|
||||
openExercisesTab() {
|
||||
if (!this.canAccessExercises) {
|
||||
this.activeTab = 'learn';
|
||||
@@ -1202,6 +1249,7 @@ export default {
|
||||
this.assistantError = '';
|
||||
this.exercisePreparationCompleted = false;
|
||||
this.lessonPrepStage = 0;
|
||||
this.lessonPrepIndex = 0;
|
||||
this.vocabTrainerActive = false;
|
||||
this.vocabTrainerPool = [];
|
||||
this.vocabTrainerMixedPool = [];
|
||||
@@ -2423,6 +2471,33 @@ export default {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.vocab-prep-pass__step {
|
||||
margin: 0 0 10px;
|
||||
font-size: 0.9rem;
|
||||
color: #7a6848;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.vocab-prep-card {
|
||||
margin-top: 12px;
|
||||
padding: 16px 18px;
|
||||
background: #fff;
|
||||
border: 1px solid #e5dccf;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.vocab-prep-card__target {
|
||||
font-size: 1.2rem;
|
||||
font-weight: 700;
|
||||
color: #1f1a16;
|
||||
}
|
||||
|
||||
.vocab-prep-card__gloss {
|
||||
margin-top: 8px;
|
||||
font-size: 1rem;
|
||||
color: #6a5a44;
|
||||
}
|
||||
|
||||
.vocab-prep-pass .btn-prep-pass {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user