diff --git a/frontend/src/views/social/VocabCourseView.vue b/frontend/src/views/social/VocabCourseView.vue
index 0bdf53b..64e3c3a 100644
--- a/frontend/src/views/social/VocabCourseView.vue
+++ b/frontend/src/views/social/VocabCourseView.vue
@@ -16,6 +16,14 @@
{{ $t('socialnetwork.vocab.courses.shareCode') }}: {{ course.shareCode }}
+
@@ -345,6 +353,7 @@ export default {
srsLoading: false,
showAddLessonDialog: false,
assistantSettings: null,
+ hardVocabList: [],
lessonFormTouched: false,
newLesson: {
lessonNumber: 1,
@@ -390,6 +399,9 @@ export default {
}
return Array.isArray(this.srsDueItems) ? this.srsDueItems.length : 0;
},
+ hardVocabCount() {
+ return Array.isArray(this.hardVocabList) ? this.hardVocabList.length : 0;
+ },
dueReviewLessons() {
return this.sortedLessons
.filter((lesson) => {
@@ -519,12 +531,45 @@ export default {
watch: {
courseId() {
this.loadCourse();
+ this.refreshHardVocabList();
}
},
methods: {
displayCourseTitle(course) {
return localizeVocabCourseTitle(course?.title, this.$i18n?.locale) || '';
},
+ hardStorageKey() {
+ return this.courseId ? `yourpart:vocab:hardList:${this.courseId}` : null;
+ },
+ refreshHardVocabList() {
+ const key = this.hardStorageKey();
+ if (!key) {
+ this.hardVocabList = [];
+ return;
+ }
+ try {
+ const raw = localStorage.getItem(key);
+ const parsed = raw ? JSON.parse(raw) : {};
+ const values = parsed && typeof parsed === 'object' ? Object.values(parsed) : [];
+ this.hardVocabList = values
+ .map((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}`,
+ learning,
+ reference
+ };
+ })
+ .filter(Boolean);
+ } catch (_) {
+ this.hardVocabList = [];
+ }
+ },
+ handleWindowFocus() {
+ this.refreshHardVocabList();
+ },
async loadCourse() {
this.loading = true;
try {
@@ -858,6 +903,14 @@ export default {
lessonId: lesson.id
});
},
+ openHardPractice() {
+ if (!this.hardVocabCount) return;
+ this.$refs.practiceDialog?.open?.({
+ courseId: this.courseId,
+ initialPool: this.hardVocabList,
+ onClose: () => this.refreshHardVocabList()
+ });
+ },
openSrsPractice() {
if (!this.srsDueItems.length) {
return;
@@ -922,6 +975,11 @@ export default {
this.loadCourse(),
this.loadAssistantSettings()
]);
+ this.refreshHardVocabList();
+ window.addEventListener('focus', this.handleWindowFocus);
+ },
+ beforeUnmount() {
+ window.removeEventListener('focus', this.handleWindowFocus);
},
};
@@ -978,6 +1036,10 @@ export default {
margin-left: auto;
}
+.course-hard-practice-link {
+ flex-shrink: 0;
+}
+
.course-assistant {
display: flex;
align-items: flex-start;