feat(VocabPracticeDialog, VocabCourseView): enhance hard vocabulary management and UI
All checks were successful
Deploy to production / deploy (push) Successful in 2m6s

- Refactored methods in VocabPracticeDialog to improve the handling of hard vocabulary items, including the addition of `findMatchingHardKey` and `removeHardEntriesForItem` for better management of hard vocabulary entries.
- Updated the VocabCourseView to display a new section for hard vocabulary items, allowing users to view and remove difficult words easily.
- Enhanced the UI with new styles for the hard vocabulary list, improving user engagement and accessibility to challenging vocabulary practice.
This commit is contained in:
Torsten Schulz (local)
2026-04-26 23:32:18 +02:00
parent 7fd8e4dda8
commit d854200708
2 changed files with 151 additions and 16 deletions

View File

@@ -297,11 +297,11 @@ export default {
},
isCurrentMarkedHard() {
if (!this.current) return false;
return Boolean(this.hardVocabMap[this.getHardKey(this.current)]);
return this.isItemMarkedHard(this.current);
},
hardPoolItems() {
if (!Array.isArray(this.pool) || this.pool.length === 0) return [];
return this.pool.filter((item) => Boolean(this.hardVocabMap[this.getHardKey(item)]));
return this.pool.filter((item) => this.isItemMarkedHard(item));
},
hardRemainingCount() {
return this.hardPoolItems.filter((item) => {
@@ -344,8 +344,63 @@ export default {
const reference = this.normalize(item?.reference || '');
return `${learning}|${reference}`;
},
findMatchingHardKey(item) {
const exactKey = this.getHardKey(item);
if (this.hardVocabMap[exactKey]) return exactKey;
const itemLearning = this.normalize(item?.learning || '');
const itemReference = this.normalize(item?.reference || '');
if (!itemLearning || !itemReference) return null;
const entries = Object.entries(this.hardVocabMap || {});
for (const [key, value] of entries) {
const learningAlts = this.splitPhraseAlternatives(value?.learning || '')
.map((part) => this.normalize(part))
.filter(Boolean);
const referenceAlts = this.splitPhraseAlternatives(value?.reference || '')
.map((part) => this.normalize(part))
.filter(Boolean);
if (!learningAlts.length || !referenceAlts.length) continue;
if (learningAlts.includes(itemLearning) && referenceAlts.includes(itemReference)) {
return key;
}
}
return null;
},
isItemMarkedHard(item) {
return Boolean(this.findMatchingHardKey(item));
},
removeHardEntriesForItem(item) {
const itemLearning = this.normalize(item?.learning || '');
const itemReference = this.normalize(item?.reference || '');
if (!itemLearning || !itemReference) return;
const toDelete = [];
Object.entries(this.hardVocabMap || {}).forEach(([key, value]) => {
const learningAlts = this.splitPhraseAlternatives(value?.learning || '')
.map((part) => this.normalize(part))
.filter(Boolean);
const referenceAlts = this.splitPhraseAlternatives(value?.reference || '')
.map((part) => this.normalize(part))
.filter(Boolean);
if (learningAlts.includes(itemLearning) && referenceAlts.includes(itemReference)) {
toDelete.push(key);
}
});
if (!toDelete.length) return;
const next = { ...this.hardVocabMap };
const nextMastery = { ...this.hardMasteryByKey };
toDelete.forEach((key) => {
delete next[key];
delete nextMastery[key];
});
this.hardVocabMap = next;
this.hardMasteryByKey = nextMastery;
},
markCurrentAsHard() {
if (!this.current) return;
this.removeHardEntriesForItem(this.current);
const key = this.getHardKey(this.current);
this.hardVocabMap = {
...this.hardVocabMap,
@@ -362,16 +417,9 @@ export default {
},
unmarkCurrentAsHard() {
if (!this.current) return;
const key = this.getHardKey(this.current);
if (!this.hardVocabMap[key]) return;
const next = { ...this.hardVocabMap };
delete next[key];
this.hardVocabMap = next;
if (this.hardMasteryByKey[key] != null) {
const nextMastery = { ...this.hardMasteryByKey };
delete nextMastery[key];
this.hardMasteryByKey = nextMastery;
}
const matchingKey = this.findMatchingHardKey(this.current);
if (!matchingKey) return;
this.removeHardEntriesForItem(this.current);
if (this.hardPhaseActive && this.hardRemainingCount <= 0) {
this.hardPhaseActive = false;
}
@@ -986,9 +1034,10 @@ export default {
if (this.hardPhaseActive && this.current) {
const key = this.getHardKey(this.current);
this.hardMasteryByKey[key] = Math.max(0, Number(this.hardMasteryByKey[key]) || 0) + 1;
if ((Number(this.hardMasteryByKey[key]) || 0) >= HARD_REQUIRED_CONSECUTIVE_CORRECT && this.hardVocabMap[key]) {
const mapKey = this.findMatchingHardKey(this.current);
if ((Number(this.hardMasteryByKey[key]) || 0) >= HARD_REQUIRED_CONSECUTIVE_CORRECT && mapKey) {
const next = { ...this.hardVocabMap };
delete next[key];
delete next[mapKey];
this.hardVocabMap = next;
this.saveHardVocabMap();
}