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.",
|
"corePatternsHint": "Zuerst die Zielsprache lesen, darunter die deutsche Bedeutung — so lernst du jedes Muster bewusst in beiden Richtungen.",
|
||||||
"vocabPrepTitle": "Vorbereitung vor dem Vokabeltrainer",
|
"vocabPrepTitle": "Vorbereitung vor dem Vokabeltrainer",
|
||||||
"vocabPrepStep1": "Lies Kernmuster und Wortliste (Deutsch ↔ Zielsprache) einmal in Ruhe durch.",
|
"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",
|
"vocabPrepConfirm1": "Erste Durchsicht erledigt",
|
||||||
"vocabPrepStep2": "Gehe die gleichen Begriffe noch einmal durch (aktive Wiederholung, ohne zu üben).",
|
"vocabPrepStep2": "Gehe die gleichen Begriffe noch einmal durch (aktive Wiederholung, ohne zu üben).",
|
||||||
"vocabPrepConfirm2": "Zweite Durchsicht erledigt",
|
"vocabPrepConfirm2": "Zweite Durchsicht erledigt",
|
||||||
|
|||||||
@@ -450,6 +450,8 @@
|
|||||||
"corePatternsHint": "Read the target language first, then the meaning below — you learn each pattern both ways.",
|
"corePatternsHint": "Read the target language first, then the meaning below — you learn each pattern both ways.",
|
||||||
"vocabPrepTitle": "Preparation before the vocabulary trainer",
|
"vocabPrepTitle": "Preparation before the vocabulary trainer",
|
||||||
"vocabPrepStep1": "Read through core patterns and the word list (native language ↔ target language) once.",
|
"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",
|
"vocabPrepConfirm1": "First pass done",
|
||||||
"vocabPrepStep2": "Go through the same items again (active review, not testing yet).",
|
"vocabPrepStep2": "Go through the same items again (active review, not testing yet).",
|
||||||
"vocabPrepConfirm2": "Second pass done",
|
"vocabPrepConfirm2": "Second pass done",
|
||||||
|
|||||||
@@ -448,6 +448,8 @@
|
|||||||
"corePatternsHint": "Primero la lengua meta; debajo, el significado en tu idioma.",
|
"corePatternsHint": "Primero la lengua meta; debajo, el significado en tu idioma.",
|
||||||
"vocabPrepTitle": "Preparación antes del entrenador de vocabulario",
|
"vocabPrepTitle": "Preparación antes del entrenador de vocabulario",
|
||||||
"vocabPrepStep1": "Lee una vez los patrones clave y la lista de palabras (idioma nativo ↔ lengua meta).",
|
"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",
|
"vocabPrepConfirm1": "Primera lectura hecha",
|
||||||
"vocabPrepStep2": "Repasa los mismos elementos otra vez (repaso activo, aún sin practicar).",
|
"vocabPrepStep2": "Repasa los mismos elementos otra vez (repaso activo, aún sin practicar).",
|
||||||
"vocabPrepConfirm2": "Segunda lectura hecha",
|
"vocabPrepConfirm2": "Segunda lectura hecha",
|
||||||
|
|||||||
@@ -217,8 +217,41 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Wichtige Begriffe (vor dem Trainer: passiv, mit DE ↔ Zielsprache) -->
|
<!-- Zwei Durchgänge: dieselben Kernmuster schrittweise vor dem Trainer -->
|
||||||
<div v-if="lesson && importantVocab && importantVocab.length > 0 && !vocabTrainerActive" class="vocab-list">
|
<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>
|
<h4>{{ $t('socialnetwork.vocab.courses.importantVocab') }}</h4>
|
||||||
<p class="vocab-info-text">{{ $t('socialnetwork.vocab.courses.vocabInfoText') }}</p>
|
<p class="vocab-info-text">{{ $t('socialnetwork.vocab.courses.vocabInfoText') }}</p>
|
||||||
<div class="vocab-items">
|
<div class="vocab-items">
|
||||||
@@ -230,27 +263,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</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 -->
|
<!-- Vokabeltrainer -->
|
||||||
<div v-if="importantVocab && importantVocab.length > 0" class="vocab-trainer-section">
|
<div v-if="importantVocab && importantVocab.length > 0" class="vocab-trainer-section">
|
||||||
<h4>{{ $t('socialnetwork.vocab.courses.vocabTrainer') }}</h4>
|
<h4>{{ $t('socialnetwork.vocab.courses.vocabTrainer') }}</h4>
|
||||||
@@ -764,6 +776,7 @@ export default {
|
|||||||
exercisePreparationCompleted: false,
|
exercisePreparationCompleted: false,
|
||||||
/** 0 = noch kein Durchgang, 1 = erste Durchsicht, 2 = zweite Durchsicht — dann Vokabeltrainer */
|
/** 0 = noch kein Durchgang, 1 = erste Durchsicht, 2 = zweite Durchsicht — dann Vokabeltrainer */
|
||||||
lessonPrepStage: 0,
|
lessonPrepStage: 0,
|
||||||
|
lessonPrepIndex: 0,
|
||||||
currentVocabQuestion: null,
|
currentVocabQuestion: null,
|
||||||
vocabTrainerAnswer: '',
|
vocabTrainerAnswer: '',
|
||||||
vocabTrainerSelectedChoice: null,
|
vocabTrainerSelectedChoice: null,
|
||||||
@@ -933,11 +946,34 @@ export default {
|
|||||||
.map((p) => this.normalizeCorePatternEntry(p))
|
.map((p) => this.normalizeCorePatternEntry(p))
|
||||||
.filter(Boolean);
|
.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() {
|
corePatternsHaveGloss() {
|
||||||
return this.normalizedCorePatterns.some((p) => p.gloss);
|
return this.normalizedCorePatterns.some((p) => p.gloss);
|
||||||
},
|
},
|
||||||
canStartVocabTrainerPrep() {
|
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 false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -1011,6 +1047,17 @@ export default {
|
|||||||
if (!n) return '';
|
if (!n) return '';
|
||||||
return n.gloss ? `${n.target} (${n.gloss})` : n.target;
|
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() {
|
openExercisesTab() {
|
||||||
if (!this.canAccessExercises) {
|
if (!this.canAccessExercises) {
|
||||||
this.activeTab = 'learn';
|
this.activeTab = 'learn';
|
||||||
@@ -1202,6 +1249,7 @@ export default {
|
|||||||
this.assistantError = '';
|
this.assistantError = '';
|
||||||
this.exercisePreparationCompleted = false;
|
this.exercisePreparationCompleted = false;
|
||||||
this.lessonPrepStage = 0;
|
this.lessonPrepStage = 0;
|
||||||
|
this.lessonPrepIndex = 0;
|
||||||
this.vocabTrainerActive = false;
|
this.vocabTrainerActive = false;
|
||||||
this.vocabTrainerPool = [];
|
this.vocabTrainerPool = [];
|
||||||
this.vocabTrainerMixedPool = [];
|
this.vocabTrainerMixedPool = [];
|
||||||
@@ -2423,6 +2471,33 @@ export default {
|
|||||||
margin-bottom: 18px;
|
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 {
|
.vocab-prep-pass .btn-prep-pass {
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user