From 3e08553a3c7cb4350eb2245b6d9e7cb02d513cc3 Mon Sep 17 00:00:00 2001 From: NiloCK Date: Sat, 17 May 2025 06:43:26 -0300 Subject: [PATCH 1/2] add wait spinner while fetching studySession items --- .../common-ui/src/components/StudySession.vue | 122 +++++++++++------- packages/platform-ui/src/views/Study.vue | 90 +++++++++++-- 2 files changed, 154 insertions(+), 58 deletions(-) diff --git a/packages/common-ui/src/components/StudySession.vue b/packages/common-ui/src/components/StudySession.vue index dc27b6f9f..1e2d772e1 100644 --- a/packages/common-ui/src/components/StudySession.vue +++ b/packages/common-ui/src/components/StudySession.vue @@ -158,7 +158,7 @@ export default defineComponent({ }, }, - emits: ['session-finished', 'session-started', 'card-loaded', 'card-response', 'time-changed'], + emits: ['session-finished', 'session-started', 'card-loaded', 'card-response', 'time-changed', 'session-prepared', 'session-error'], data() { return { @@ -224,7 +224,9 @@ export default defineComponent({ async created() { this.userCourseRegDoc = await this.user.getCourseRegistrationsDoc(); - this.initSession(); + console.log('[StudySession] Created lifecycle hook - starting initSession'); + await this.initSession(); + console.log('[StudySession] InitSession completed in created hook'); }, methods: { @@ -271,58 +273,84 @@ export default defineComponent({ }, async initSession() { - console.log(`[StudySession] starting study session w/ sources: ${JSON.stringify(this.contentSources)}`); - - this.sessionContentSources = ( - await Promise.all( - this.contentSources.map(async (s) => { - try { - return await getStudySource(s, this.user); - } catch (e) { - console.error(`Failed to load study source: ${s.type}/${s.id}`, e); - return null; - } - }) - ) - ).filter((s) => s !== null); - - this.timeRemaining = this.sessionTimeLimit * 60; - - const sessionClassroomDBs = await Promise.all( - this.contentSources - .filter((s) => s.type === 'classroom') - .map(async (c) => await this.dataLayer.getClassroomDB(c.id, 'student')) - ); - - sessionClassroomDBs.forEach((db) => { - // db.setChangeFcn(this.handleClassroomMessage()); - }); + let sessionClassroomDBs = []; + try { + console.log(`[StudySession] starting study session w/ sources: ${JSON.stringify(this.contentSources)}`); + console.log('[StudySession] Beginning preparation process'); + + this.sessionContentSources = ( + await Promise.all( + this.contentSources.map(async (s) => { + try { + return await getStudySource(s, this.user); + } catch (e) { + console.error(`Failed to load study source: ${s.type}/${s.id}`, e); + return null; + } + }) + ) + ).filter((s) => s !== null); - this.sessionController = new SessionController(this.sessionContentSources, 60 * this.sessionTimeLimit); - this.sessionController.sessionRecord = this.sessionRecord; + this.timeRemaining = this.sessionTimeLimit * 60; - await this.sessionController.prepareSession(); - this.intervalHandler = setInterval(this.tick, 1000); + sessionClassroomDBs = await Promise.all( + this.contentSources + .filter((s) => s.type === 'classroom') + .map(async (c) => await this.dataLayer.getClassroomDB(c.id, 'student')) + ); - this.sessionPrepared = true; + sessionClassroomDBs.forEach((db) => { + // db.setChangeFcn(this.handleClassroomMessage()); + }); - this.contentSources - .filter((s) => s.type === 'course') - .forEach( - async (c) => (this.courseNames[c.id] = (await this.dataLayer.getCoursesDB().getCourseConfig(c.id)).name) - ); + this.sessionController = new SessionController(this.sessionContentSources, 60 * this.sessionTimeLimit); + this.sessionController.sessionRecord = this.sessionRecord; + + await this.sessionController.prepareSession(); + this.intervalHandler = setInterval(this.tick, 1000); + + this.sessionPrepared = true; + + console.log('[StudySession] Session preparation complete, emitting session-prepared event'); + this.$emit('session-prepared'); + console.log('[StudySession] Event emission completed'); + } catch (error) { + console.error('[StudySession] Error during session preparation:', error); + // Notify parent component about the error + this.$emit('session-error', { message: 'Failed to prepare study session', error }); + } - console.log(`[StudySession] Session created: - ${this.sessionController.toString()} - User courses: ${this.contentSources + try { + this.contentSources .filter((s) => s.type === 'course') - .map((c) => c.id) - .toString()} - User classrooms: ${sessionClassroomDBs.map((db) => db._id)} - `); + .forEach( + async (c) => (this.courseNames[c.id] = (await this.dataLayer.getCoursesDB().getCourseConfig(c.id)).name) + ); + + console.log(`[StudySession] Session created: + ${this.sessionController?.toString() || 'Session controller not initialized'} + User courses: ${this.contentSources + .filter((s) => s.type === 'course') + .map((c) => c.id) + .toString()} + User classrooms: ${sessionClassroomDBs.map((db) => db._id).toString() || 'No classrooms'} + `); + } catch (error) { + console.error('[StudySession] Error during final session setup:', error); + } - this.$emit('session-started'); - this.loadCard(this.sessionController.nextCard()); + if (this.sessionController) { + try { + this.$emit('session-started'); + this.loadCard(this.sessionController.nextCard()); + } catch (error) { + console.error('[StudySession] Error loading next card:', error); + this.$emit('session-error', { message: 'Failed to load study card', error }); + } + } else { + console.error('[StudySession] Cannot load card: session controller not initialized'); + this.$emit('session-error', { message: 'Study session initialization failed' }); + } }, countCardViews(course_id: string, card_id: string): number { diff --git a/packages/platform-ui/src/views/Study.vue b/packages/platform-ui/src/views/Study.vue index 4cb0530eb..8c41d8e0a 100644 --- a/packages/platform-ui/src/views/Study.vue +++ b/packages/platform-ui/src/views/Study.vue @@ -29,16 +29,53 @@ - + +
+ +
+ + + + +
Preparing your study session...
+
Getting your learning materials ready
+
+
+
+
+ + +
+ + + + mdi-alert-circle +
Session Preparation Failed
+
{{ errorMessage || 'There was a problem preparing your study session.' }}
+ Try Again +
+
+
+
+ + +
@@ -107,6 +144,8 @@ export default defineComponent({ sessionTimeLimit: 5, inSession: false, sessionPrepared: false, + sessionError: false, + errorMessage: '', sessionContentSources: [] as ContentSourceID[], dataInputFormStore: useDataInputFormStore(), getViewComponent: (view_id: string) => allCourses.getView(view_id), @@ -176,7 +215,10 @@ export default defineComponent({ this.sessionContentSources = sources; this.sessionTimeLimit = timeLimit; this.inSession = true; - this.sessionPrepared = true; + this.sessionPrepared = false; + + // Adding a console log to debug event handling + console.log('[Study] Waiting for session-prepared event from StudySession component'); }, registerUserForPreviewCourse() { @@ -188,6 +230,32 @@ export default defineComponent({ handleSessionFinished() { this.refreshRoute(); }, + + handleSessionPrepared() { + console.log('[Study] Session preparation complete - received session-prepared event'); + this.sessionPrepared = true; + this.sessionError = false; + this.errorMessage = ''; + }, + + handleSessionError({ message, error }) { + console.error('[Study] Session error:', message, error); + this.sessionError = true; + this.errorMessage = message || 'An error occurred while preparing your study session.'; + this.sessionPrepared = false; + }, }, }); + + From 35a73cef74455fdc7248e22bde040523befdacb7 Mon Sep 17 00:00:00 2001 From: NiloCK Date: Sat, 17 May 2025 06:51:02 -0300 Subject: [PATCH 2/2] type nudges --- .../common-ui/src/components/StudySession.vue | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/packages/common-ui/src/components/StudySession.vue b/packages/common-ui/src/components/StudySession.vue index 1e2d772e1..aaff01d14 100644 --- a/packages/common-ui/src/components/StudySession.vue +++ b/packages/common-ui/src/components/StudySession.vue @@ -93,6 +93,7 @@ import { CourseRegistrationDoc, DataLayerProvider, UserDBInterface, + ClassroomDBInterface, } from '@vue-skuilder/db'; import { SessionController, StudySessionRecord } from '@vue-skuilder/db'; import { newInterval } from '@vue-skuilder/db'; @@ -158,7 +159,15 @@ export default defineComponent({ }, }, - emits: ['session-finished', 'session-started', 'card-loaded', 'card-response', 'time-changed', 'session-prepared', 'session-error'], + emits: [ + 'session-finished', + 'session-started', + 'card-loaded', + 'card-response', + 'time-changed', + 'session-prepared', + 'session-error', + ], data() { return { @@ -273,11 +282,11 @@ export default defineComponent({ }, async initSession() { - let sessionClassroomDBs = []; + let sessionClassroomDBs: ClassroomDBInterface[] = []; try { console.log(`[StudySession] starting study session w/ sources: ${JSON.stringify(this.contentSources)}`); console.log('[StudySession] Beginning preparation process'); - + this.sessionContentSources = ( await Promise.all( this.contentSources.map(async (s) => { @@ -310,7 +319,7 @@ export default defineComponent({ this.intervalHandler = setInterval(this.tick, 1000); this.sessionPrepared = true; - + console.log('[StudySession] Session preparation complete, emitting session-prepared event'); this.$emit('session-prepared'); console.log('[StudySession] Event emission completed'); @@ -333,7 +342,7 @@ export default defineComponent({ .filter((s) => s.type === 'course') .map((c) => c.id) .toString()} - User classrooms: ${sessionClassroomDBs.map((db) => db._id).toString() || 'No classrooms'} + User classrooms: ${sessionClassroomDBs.map((db: any) => db._id).toString() || 'No classrooms'} `); } catch (error) { console.error('[StudySession] Error during final session setup:', error);