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;