feat(VocabPracticeDialog, VocabCourseView): enhance hard vocabulary management and UI
All checks were successful
Deploy to production / deploy (push) Successful in 2m6s
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:
@@ -29,6 +29,34 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<section v-if="hardVocabCount > 0" class="surface-card course-hard-list">
|
||||
<div class="course-hard-list__header">
|
||||
<h3>{{ $t('socialnetwork.vocab.courses.startHardVocabTrainer', { count: hardVocabCount }) }}</h3>
|
||||
<button
|
||||
type="button"
|
||||
class="button-secondary"
|
||||
@click="openHardPractice"
|
||||
>
|
||||
{{ $t('socialnetwork.vocab.courses.practiceInTrainer') }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="course-hard-list__items">
|
||||
<div v-for="entry in hardVocabList" :key="entry.key" class="course-hard-list__item">
|
||||
<div class="course-hard-list__texts">
|
||||
<strong>{{ entry.learning }}</strong>
|
||||
<span>{{ entry.reference }}</span>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="btn-delete"
|
||||
@click="removeHardVocabEntry(entry.key)"
|
||||
>
|
||||
{{ $t('socialnetwork.vocab.courses.unmarkVocabHard') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="surface-card course-assistant">
|
||||
<div>
|
||||
<span class="course-assistant__eyebrow">{{ $t('socialnetwork.vocab.courses.languageAssistantEyebrow') }}</span>
|
||||
@@ -550,14 +578,15 @@ export default {
|
||||
try {
|
||||
const raw = localStorage.getItem(key);
|
||||
const parsed = raw ? JSON.parse(raw) : {};
|
||||
const values = parsed && typeof parsed === 'object' ? Object.values(parsed) : [];
|
||||
const values = parsed && typeof parsed === 'object' ? Object.entries(parsed) : [];
|
||||
this.hardVocabList = values
|
||||
.map((entry, idx) => {
|
||||
.map(([entryKey, entry], idx) => {
|
||||
const learning = String(entry?.learning || '').trim();
|
||||
const reference = String(entry?.reference || '').trim();
|
||||
if (!learning || !reference) return null;
|
||||
return {
|
||||
id: `hard-${idx}-${learning}-${reference}`,
|
||||
key: String(entryKey || ''),
|
||||
learning,
|
||||
reference
|
||||
};
|
||||
@@ -567,6 +596,21 @@ export default {
|
||||
this.hardVocabList = [];
|
||||
}
|
||||
},
|
||||
removeHardVocabEntry(entryKey) {
|
||||
const key = this.hardStorageKey();
|
||||
if (!key || !entryKey) return;
|
||||
try {
|
||||
const raw = localStorage.getItem(key);
|
||||
const parsed = raw ? JSON.parse(raw) : {};
|
||||
if (!parsed || typeof parsed !== 'object' || !parsed[entryKey]) return;
|
||||
const next = { ...parsed };
|
||||
delete next[entryKey];
|
||||
localStorage.setItem(key, JSON.stringify(next));
|
||||
this.refreshHardVocabList();
|
||||
} catch (_) {
|
||||
// ignore storage parse/write errors
|
||||
}
|
||||
},
|
||||
handleWindowFocus() {
|
||||
this.refreshHardVocabList();
|
||||
},
|
||||
@@ -1040,6 +1084,48 @@ export default {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.course-hard-list {
|
||||
padding: 16px 18px;
|
||||
}
|
||||
|
||||
.course-hard-list__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.course-hard-list__header h3 {
|
||||
margin: 0;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.course-hard-list__items {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.course-hard-list__item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid var(--color-border, #e1dbd4);
|
||||
border-radius: 10px;
|
||||
background: rgba(255, 255, 255, 0.72);
|
||||
}
|
||||
|
||||
.course-hard-list__texts {
|
||||
display: grid;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.course-hard-list__texts span {
|
||||
color: var(--color-text-secondary, #6b625b);
|
||||
}
|
||||
|
||||
.course-assistant {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
|
||||
Reference in New Issue
Block a user