@vue-skuilder/cli 0.1.8 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/dist/standalone-ui-template/package.json +61 -0
  2. package/dist/standalone-ui-template/src/App.vue +62 -0
  3. package/dist/standalone-ui-template/src/ENVIRONMENT_VARS.ts +76 -0
  4. package/dist/standalone-ui-template/src/components/CourseFooter.vue +33 -0
  5. package/dist/standalone-ui-template/src/components/CourseHeader.vue +55 -0
  6. package/dist/standalone-ui-template/src/composables/useCourseConfig.ts +33 -0
  7. package/dist/standalone-ui-template/src/main.ts +201 -0
  8. package/dist/standalone-ui-template/src/questions/MultipleChoiceQuestion.ts +46 -0
  9. package/dist/standalone-ui-template/src/questions/MultipleChoiceQuestionView.vue +50 -0
  10. package/dist/standalone-ui-template/src/questions/NumberRangeQuestion.ts +44 -0
  11. package/dist/standalone-ui-template/src/questions/NumberRangeQuestionView.vue +43 -0
  12. package/dist/standalone-ui-template/src/questions/README.md +129 -0
  13. package/dist/standalone-ui-template/src/questions/SimpleTextQuestion.test.ts +25 -0
  14. package/dist/standalone-ui-template/src/questions/SimpleTextQuestion.ts +40 -0
  15. package/dist/standalone-ui-template/src/questions/SimpleTextQuestionView.vue +46 -0
  16. package/dist/standalone-ui-template/src/questions/exampleCourse.ts +10 -0
  17. package/dist/standalone-ui-template/src/questions/index.ts +117 -0
  18. package/dist/standalone-ui-template/src/router/index.ts +59 -0
  19. package/dist/standalone-ui-template/src/views/BrowseView.vue +41 -0
  20. package/dist/standalone-ui-template/src/views/HomeView.vue +35 -0
  21. package/dist/standalone-ui-template/src/views/ProgressView.vue +18 -0
  22. package/dist/standalone-ui-template/src/views/StudyView.vue +84 -0
  23. package/dist/standalone-ui-template/src/views/UserSettingsView.vue +175 -0
  24. package/dist/standalone-ui-template/src/views/UserStatsView.vue +76 -0
  25. package/dist/standalone-ui-template/tsconfig.json +17 -0
  26. package/dist/standalone-ui-template/vite.config.ts +103 -0
  27. package/dist/utils/template.d.ts +1 -1
  28. package/dist/utils/template.d.ts.map +1 -1
  29. package/dist/utils/template.js +8 -3
  30. package/dist/utils/template.js.map +1 -1
  31. package/package.json +11 -11
  32. package/src/utils/template.ts +9 -4
@@ -0,0 +1,43 @@
1
+ <template>
2
+ <div>
3
+ <p>{{ questionText }}</p>
4
+ <input type="number" v-model.number="userAnswer" @keyup.enter="submitAnswer" placeholder="Enter a number" />
5
+ <button @click="submitAnswer">Submit</button>
6
+ </div>
7
+ </template>
8
+
9
+ <script setup lang="ts">
10
+ import { ref, PropType } from 'vue';
11
+ import { useViewable, useQuestionView } from '@vue-skuilder/courseware';
12
+ import { NumberRangeQuestion } from './NumberRangeQuestion';
13
+ import { ViewData } from '@vue-skuilder/common';
14
+
15
+ const props = defineProps({
16
+ questionText: {
17
+ type: String,
18
+ required: true,
19
+ },
20
+ data: {
21
+ type: Array as PropType<ViewData[]>,
22
+ required: true,
23
+ },
24
+ });
25
+
26
+ const userAnswer = ref<number | null>(null);
27
+
28
+ const viewableUtils = useViewable(props, () => {}, 'NumberRangeQuestionView');
29
+ const questionUtils = useQuestionView<NumberRangeQuestion>(viewableUtils);
30
+
31
+ // Initialize question
32
+ questionUtils.question.value = new NumberRangeQuestion(props.data);
33
+
34
+ const submitAnswer = () => {
35
+ if (userAnswer.value !== null) {
36
+ questionUtils.submitAnswer({ response: userAnswer.value });
37
+ }
38
+ };
39
+ </script>
40
+
41
+ <style scoped>
42
+ /* Add some basic styling if needed */
43
+ </style>
@@ -0,0 +1,129 @@
1
+ # Custom Questions in Standalone UI
2
+
3
+ This directory contains example implementations of custom question types for the Vue Skuilder platform. These examples demonstrate how to create new `Question` subclasses and their corresponding Vue components, and how to integrate them into your application.
4
+
5
+ ## Example Questions Provided
6
+
7
+ - **SimpleTextQuestion**: A basic question that asks for a text input and checks for an exact string match.
8
+ - **MultipleChoiceQuestion**: Presents a question with multiple options and checks for the correct selection.
9
+ - **NumberRangeQuestion**: Asks for a numeric input and validates if it falls within a specified range.
10
+
11
+ ## How to Use These Examples
12
+
13
+ Each question type consists of two main parts:
14
+ 1. A TypeScript file (`.ts`) defining the `Question` subclass, which handles the question logic, data shapes, and answer evaluation.
15
+ 2. A Vue component file (`.vue`) that provides the user interface for the question.
16
+
17
+ These examples are already integrated into the `exampleCourse.ts` file, which you can use to see them in action.
18
+
19
+ ## Integrating Custom Questions into Your Course at Runtime
20
+
21
+ To use your custom questions in a course, you need to:
22
+
23
+ 1. **Define your Question Class**: Create a new TypeScript file (e.g., `MyCustomQuestion.ts`) that extends the `Question` class from `@vue-skuilder/courseware`. Define its `dataShapes` and `views` static properties.
24
+
25
+ ```typescript
26
+ // MyCustomQuestion.ts
27
+ import { Question, DataShape, ViewData, Answer } from '@vue-skuilder/courseware';
28
+ import { FieldType } from '@vue-skuilder/common';
29
+ import MyCustomQuestionView from './MyCustomQuestionView.vue';
30
+
31
+ export class MyCustomQuestion extends Question {
32
+ public static dataShapes: DataShape[] = [
33
+ new DataShape('MyCustomQuestion', [
34
+ { name: 'myField', type: FieldType.STRING },
35
+ ]),
36
+ ];
37
+
38
+ public static views = [
39
+ { name: 'MyCustomQuestionView', component: MyCustomQuestionView },
40
+ ];
41
+
42
+ constructor(data: ViewData[]) {
43
+ super(data);
44
+ // Initialize your question data from `data`
45
+ }
46
+
47
+ public dataShapes(): DataShape[] {
48
+ return MyCustomQuestion.dataShapes;
49
+ }
50
+
51
+ public views() {
52
+ return MyCustomQuestion.views;
53
+ }
54
+
55
+ protected isCorrect(answer: Answer): boolean {
56
+ // Implement your answer evaluation logic here
57
+ return false;
58
+ }
59
+ }
60
+ ```
61
+
62
+ 2. **Create Your Vue Component**: Create a Vue component (e.g., `MyCustomQuestionView.vue`) that will render your question and allow user interaction. This component will receive props based on the `ViewData` you define for your question.
63
+
64
+ ```vue
65
+ <!-- MyCustomQuestionView.vue -->
66
+ <template>
67
+ <div>
68
+ <p>{{ questionData.myField }}</p>
69
+ <!-- Your input elements and UI -->
70
+ <button @click="submitAnswer">Submit</button>
71
+ </div>
72
+ </template>
73
+
74
+ <script setup lang="ts">
75
+ import { ref } from 'vue';
76
+ import { useStudySessionStore } from '@vue-skuilder/common-ui';
77
+
78
+ const props = defineProps({
79
+ // Define props based on your question's data
80
+ questionData: { type: Object, required: true },
81
+ });
82
+
83
+ const studySessionStore = useStudySessionStore();
84
+ const userAnswer = ref('');
85
+
86
+ const submitAnswer = () => {
87
+ // Collect user's answer and submit it
88
+ studySessionStore.submitAnswer({ response: userAnswer.value });
89
+ };
90
+ </script>
91
+ ```
92
+
93
+ 3. **Register Your Question and Course**: In your application's entry point (e.g., `src/main.ts` or `src/App.vue`), you need to import your custom question and include it in a `Course` instance. Then, register this course with the `allCourses` list.
94
+
95
+ ```typescript
96
+ // src/main.ts (example)
97
+ import { createApp } from 'vue';
98
+ import App from './App.vue';
99
+ import { createPinia } from 'pinia';
100
+ import { allCourses, Course } from '@vue-skuilder/courseware';
101
+
102
+ // Import your custom question
103
+ import { MyCustomQuestion } from './questions/MyCustomQuestion';
104
+
105
+ // Create a new Course instance with your custom question
106
+ const myCustomCourse = new Course('MyCustomCourse', [
107
+ new MyCustomQuestion([{ myField: 'Hello Custom Question!' }]),
108
+ ]);
109
+
110
+ // Add your custom course to the global allCourses list
111
+ allCourses.courses.push(myCustomCourse);
112
+
113
+ const app = createApp(App);
114
+ app.use(createPinia());
115
+ app.mount('#app');
116
+ ```
117
+
118
+ **Note**: The `allCourses` object is a singleton that manages all available courses and their associated questions and views. By adding your custom course to `allCourses.courses`, it becomes discoverable by the `CardViewer` and other components that rely on the course registry.
119
+
120
+ ## Developing New Questions
121
+
122
+ When developing new questions, consider the following:
123
+
124
+ - **DataShape Definition**: Carefully define the `DataShape` for your question. This dictates the structure of the data that will be passed to your question's constructor and Vue component.
125
+ - **Answer Evaluation**: Implement the `isCorrect` method in your `Question` subclass to define how user answers are evaluated.
126
+ - **Vue Component Props**: Ensure your Vue component's `props` match the data fields defined in your `DataShape` and any additional data you pass from your `Question` instance.
127
+ - **StudySessionStore**: Use the `useStudySessionStore()` composable from `@vue-skuilder/common-ui` to submit user answers and interact with the study session logic.
128
+
129
+ Feel free to modify and extend the provided examples to suit your needs.
@@ -0,0 +1,25 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { SimpleTextQuestion } from './SimpleTextQuestion';
3
+
4
+ describe('SimpleTextQuestion', () => {
5
+ it('should correctly evaluate a correct answer', () => {
6
+ const question = new SimpleTextQuestion([
7
+ { questionText: 'What is the capital of France?', correctAnswer: 'Paris' },
8
+ ]);
9
+ expect(question.evaluate({ response: 'Paris' }, 0).isCorrect).toBe(true);
10
+ });
11
+
12
+ it('should correctly evaluate an incorrect answer', () => {
13
+ const question = new SimpleTextQuestion([
14
+ { questionText: 'What is the capital of France?', correctAnswer: 'Paris' },
15
+ ]);
16
+ expect(question.evaluate({ response: 'London' }, 0).isCorrect).toBe(false);
17
+ });
18
+
19
+ it('should be case-insensitive', () => {
20
+ const question = new SimpleTextQuestion([
21
+ { questionText: 'What is the capital of France?', correctAnswer: 'Paris' },
22
+ ]);
23
+ expect(question.evaluate({ response: 'paris' }, 0).isCorrect).toBe(true);
24
+ });
25
+ });
@@ -0,0 +1,40 @@
1
+ import { ViewData, Answer, Question } from '@vue-skuilder/courseware';
2
+ import { FieldType, DataShape, DataShapeName } from '@vue-skuilder/common';
3
+ import SimpleTextQuestionView from './SimpleTextQuestionView.vue';
4
+
5
+ export class SimpleTextQuestion extends Question {
6
+ public static dataShapes: DataShape[] = [
7
+ {
8
+ name: 'SimpleTextQuestion' as DataShapeName,
9
+ fields: [
10
+ { name: 'questionText', type: FieldType.STRING },
11
+ { name: 'correctAnswer', type: FieldType.STRING },
12
+ ],
13
+ },
14
+ ];
15
+
16
+ public static views = [{ name: 'SimpleTextQuestionView', component: SimpleTextQuestionView }];
17
+
18
+ // @ts-expect-error TS6133: Used in Vue template
19
+ private questionText: string;
20
+ private correctAnswer: string;
21
+
22
+ constructor(data: ViewData[]) {
23
+ super(data);
24
+ this.questionText = data[0].questionText as string;
25
+ this.correctAnswer = data[0].correctAnswer as string;
26
+ }
27
+
28
+ public dataShapes(): DataShape[] {
29
+ return SimpleTextQuestion.dataShapes;
30
+ }
31
+
32
+ public views() {
33
+ // This will be dynamically populated or imported
34
+ return SimpleTextQuestion.views;
35
+ }
36
+
37
+ protected isCorrect(answer: Answer): boolean {
38
+ return (answer.response as string).toLowerCase() === this.correctAnswer.toLowerCase();
39
+ }
40
+ }
@@ -0,0 +1,46 @@
1
+ <template>
2
+ <div>
3
+ <p>{{ questionText }}</p>
4
+ <input v-model="userAnswer" @keyup.enter="submitAnswer" placeholder="Your answer" />
5
+ <button @click="submitAnswer">Submit</button>
6
+ </div>
7
+ </template>
8
+
9
+ <script setup lang="ts">
10
+ import { ref, onMounted, PropType } from 'vue';
11
+ import { useViewable, useQuestionView } from '@vue-skuilder/courseware';
12
+ import { SimpleTextQuestion } from './SimpleTextQuestion';
13
+ import { ViewData } from '@vue-skuilder/common';
14
+
15
+ const props = defineProps({
16
+ questionText: {
17
+ type: String,
18
+ required: true,
19
+ },
20
+ data: {
21
+ type: Array as PropType<ViewData[]>,
22
+ required: true,
23
+ },
24
+ });
25
+
26
+ const userAnswer = ref('');
27
+
28
+ const viewableUtils = useViewable(props, () => {}, 'SimpleTextQuestionView');
29
+ const questionUtils = useQuestionView<SimpleTextQuestion>(viewableUtils);
30
+
31
+ // Initialize question
32
+ questionUtils.question.value = new SimpleTextQuestion(props.data);
33
+
34
+ const submitAnswer = () => {
35
+ questionUtils.submitAnswer({ response: userAnswer.value });
36
+ };
37
+
38
+ onMounted(() => {
39
+ // Optionally, you might want to focus the input field on mount
40
+ // This requires a ref on the input element
41
+ });
42
+ </script>
43
+
44
+ <style scoped>
45
+ /* Add some basic styling if needed */
46
+ </style>
@@ -0,0 +1,10 @@
1
+ import { CourseWare } from '@vue-skuilder/courseware';
2
+ import { SimpleTextQuestion } from './SimpleTextQuestion';
3
+ import { MultipleChoiceQuestion } from './MultipleChoiceQuestion';
4
+ import { NumberRangeQuestion } from './NumberRangeQuestion';
5
+
6
+ export const exampleCourse = new CourseWare('ExampleCourse', [
7
+ SimpleTextQuestion,
8
+ MultipleChoiceQuestion,
9
+ NumberRangeQuestion,
10
+ ]);
@@ -0,0 +1,117 @@
1
+ // Library entry point for custom questions in standalone-ui
2
+ // This file exports question types and components for consumption by studio-ui
3
+
4
+ import { CourseWare } from '@vue-skuilder/courseware';
5
+ import { DataShape } from '@vue-skuilder/common';
6
+ import { ViewComponent } from '@vue-skuilder/common-ui';
7
+
8
+ // [ ] todo: simplify exports here. Only the final 'bundle' is strictly required.
9
+
10
+ // Export individual question classes
11
+ export { SimpleTextQuestion } from './SimpleTextQuestion';
12
+ export { MultipleChoiceQuestion } from './MultipleChoiceQuestion';
13
+ export { NumberRangeQuestion } from './NumberRangeQuestion';
14
+
15
+ // Export example course
16
+ export { exampleCourse } from './exampleCourse';
17
+
18
+ // Import components for re-export
19
+ import SimpleTextQuestionView from './SimpleTextQuestionView.vue';
20
+ import MultipleChoiceQuestionView from './MultipleChoiceQuestionView.vue';
21
+ import NumberRangeQuestionView from './NumberRangeQuestionView.vue';
22
+
23
+ // Export Vue components
24
+ export { SimpleTextQuestionView, MultipleChoiceQuestionView, NumberRangeQuestionView };
25
+
26
+ // Import classes for analysis
27
+ import { SimpleTextQuestion } from './SimpleTextQuestion';
28
+ import { MultipleChoiceQuestion } from './MultipleChoiceQuestion';
29
+ import { NumberRangeQuestion } from './NumberRangeQuestion';
30
+ import { exampleCourse } from './exampleCourse';
31
+
32
+ /**
33
+ * Main function to export all custom questions for studio-ui consumption
34
+ * This provides a standardized interface for the CLI to discover and integrate
35
+ * custom question types into studio-ui builds
36
+ */
37
+ export function allCustomQuestions() {
38
+ // Collect all question classes
39
+ const questionClasses = [SimpleTextQuestion, MultipleChoiceQuestion, NumberRangeQuestion];
40
+
41
+ // Collect all data shapes from questions
42
+ const dataShapes: DataShape[] = [];
43
+ questionClasses.forEach((questionClass) => {
44
+ if (questionClass.dataShapes) {
45
+ questionClass.dataShapes.forEach((shape) => {
46
+ // Avoid duplicates
47
+ if (!dataShapes.find((existing) => existing.name === shape.name)) {
48
+ dataShapes.push(shape);
49
+ }
50
+ });
51
+ }
52
+ });
53
+
54
+ // Collect all view components from questions
55
+ const views: ViewComponent[] = [];
56
+ questionClasses.forEach((questionClass) => {
57
+ if (questionClass.views) {
58
+ questionClass.views.forEach((view) => {
59
+ // Avoid duplicates by name
60
+ if (!views.find((existing) => existing.name === view.name)) {
61
+ views.push(view);
62
+ }
63
+ });
64
+ }
65
+ });
66
+
67
+ const courses = [exampleCourse];
68
+
69
+ // Return structured data for studio-ui integration
70
+ return {
71
+ // CourseWare instances with question instances
72
+ courses,
73
+
74
+ // Question class constructors for registration
75
+ questionClasses,
76
+
77
+ // Available data shapes for studio-ui CreateCardView
78
+ dataShapes,
79
+
80
+ // Vue components for runtime registration
81
+ views,
82
+
83
+ // Metadata for debugging and analysis
84
+ meta: {
85
+ questionCount: questionClasses.length,
86
+ dataShapeCount: dataShapes.length,
87
+ viewCount: views.length,
88
+ courseCount: courses.length,
89
+ packageName: '@vue-skuilder/standalone-ui',
90
+ sourceDirectory: 'src/questions',
91
+ },
92
+ };
93
+ }
94
+
95
+ /**
96
+ * Type definitions for the custom questions export structure
97
+ * This provides TypeScript support for CLI and studio-ui integration
98
+ */
99
+ export interface CustomQuestionsExport {
100
+ courses: CourseWare[];
101
+ questionClasses: Array<
102
+ typeof SimpleTextQuestion | typeof MultipleChoiceQuestion | typeof NumberRangeQuestion
103
+ >;
104
+ dataShapes: DataShape[];
105
+ views: ViewComponent[];
106
+ meta: {
107
+ questionCount: number;
108
+ dataShapeCount: number;
109
+ viewCount: number;
110
+ courseCount: number;
111
+ packageName: string;
112
+ sourceDirectory: string;
113
+ };
114
+ }
115
+
116
+ // Default export for convenience
117
+ export default allCustomQuestions;
@@ -0,0 +1,59 @@
1
+ import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
2
+ import HomeView from '../views/HomeView.vue';
3
+ import StudyView from '../views/StudyView.vue';
4
+ import ProgressView from '../views/ProgressView.vue';
5
+ import BrowseView from '../views/BrowseView.vue';
6
+ import UserStatsView from '../views/UserStatsView.vue';
7
+ import UserSettingsView from '../views/UserSettingsView.vue';
8
+ import { UserLogin, UserRegistration } from '@vue-skuilder/common-ui';
9
+
10
+ const routes: Array<RouteRecordRaw> = [
11
+ {
12
+ path: '/',
13
+ name: 'home',
14
+ component: HomeView,
15
+ },
16
+ {
17
+ path: '/study',
18
+ name: 'study',
19
+ component: StudyView,
20
+ },
21
+ {
22
+ path: '/progress',
23
+ name: 'progress',
24
+ component: ProgressView,
25
+ },
26
+ {
27
+ path: '/browse',
28
+ name: 'browse',
29
+ component: BrowseView,
30
+ },
31
+ {
32
+ path: '/login',
33
+ name: 'login',
34
+ component: UserLogin,
35
+ },
36
+ {
37
+ path: '/register',
38
+ alias: '/signup',
39
+ name: 'Register',
40
+ component: UserRegistration,
41
+ },
42
+ {
43
+ path: '/u/:username',
44
+ name: 'UserSettings',
45
+ component: UserSettingsView,
46
+ },
47
+ {
48
+ path: '/u/:username/stats',
49
+ name: 'UserStats',
50
+ component: UserStatsView,
51
+ },
52
+ ];
53
+
54
+ const router = createRouter({
55
+ history: createWebHistory(),
56
+ routes,
57
+ });
58
+
59
+ export default router;
@@ -0,0 +1,41 @@
1
+ <template>
2
+ <v-container>
3
+ <div v-if="courseId">
4
+ <CourseInformation :course-id="courseId" :view-lookup-function="viewLookup" :edit-mode="editMode">
5
+ <template #actions="{ editMode }">
6
+ <!-- Standalone-ui specific actions - no registration/trial buttons -->
7
+ <div v-if="editMode !== 'none'">
8
+ <v-btn color="success" class="me-2" to="/study">Start Study Session</v-btn>
9
+ </div>
10
+ </template>
11
+ </CourseInformation>
12
+ </div>
13
+ <div v-else class="text-center">
14
+ <v-alert type="error"> Course ID not found in configuration </v-alert>
15
+ </div>
16
+ </v-container>
17
+ </template>
18
+
19
+ <script setup lang="ts">
20
+ import { ref, onMounted } from 'vue';
21
+ import { CourseInformation } from '@vue-skuilder/common-ui';
22
+ import { allCourseWare } from '@vue-skuilder/courseware';
23
+ import { getDataLayer } from '@vue-skuilder/db';
24
+ import config from '../../skuilder.config.json';
25
+
26
+ const courseId = ref<string>('');
27
+ const editMode = ref<'none' | 'readonly' | 'full'>('full');
28
+
29
+ // Full view lookup using courses package (same as platform-ui)
30
+ const viewLookup = (x: unknown) => {
31
+ return allCourseWare.getView(x);
32
+ };
33
+
34
+ onMounted(() => {
35
+ courseId.value = config.course || '';
36
+
37
+ // Determine edit mode based on data layer capabilities
38
+ const dataLayer = getDataLayer();
39
+ editMode.value = dataLayer.isReadOnly() ? 'readonly' : 'full';
40
+ });
41
+ </script>
@@ -0,0 +1,35 @@
1
+ <template>
2
+ <v-container>
3
+ <v-row>
4
+ <v-col cols="12" class="text-center">
5
+ <h1 class="text-h3 mb-6">{{ courseConfig.title }}</h1>
6
+ <p class="text-body-1 mb-6">{{ courseConfig.description }}</p>
7
+
8
+ <div v-if="isLoggedIn">
9
+ <v-btn color="primary" size="large" to="/study"> Start Learning </v-btn>
10
+ </div>
11
+ <div v-else class="d-flex flex-column align-center">
12
+ <p class="text-body-1 mb-4">Please login to start learning</p>
13
+ <UserLoginAndRegistrationContainer v-model="authDialogOpen" redirect-to-path="/study">
14
+ <template #activator="{ props }">
15
+ <v-btn color="primary" size="large" v-bind="props"> Login / Sign Up </v-btn>
16
+ </template>
17
+ </UserLoginAndRegistrationContainer>
18
+ </div>
19
+ </v-col>
20
+ </v-row>
21
+ </v-container>
22
+ </template>
23
+
24
+ <script setup lang="ts">
25
+ import { ref, onMounted, computed } from 'vue';
26
+ import { useCourseConfig } from '../composables/useCourseConfig';
27
+ import { UserLoginAndRegistrationContainer, useAuthStore } from '@vue-skuilder/common-ui';
28
+ import { getDataLayer } from '@vue-skuilder/db';
29
+
30
+ const { courseConfig } = useCourseConfig();
31
+ const authStore = useAuthStore();
32
+ const authDialogOpen = ref(false);
33
+
34
+ const isLoggedIn = computed(() => authStore.isLoggedIn);
35
+ </script>
@@ -0,0 +1,18 @@
1
+ <template>
2
+ <v-container>
3
+ <v-row>
4
+ <v-col cols="12">
5
+ <h1 class="text-h4 mb-4">Your Progress</h1>
6
+
7
+ <!-- Placeholder for progress component -->
8
+ <v-card class="pa-4">
9
+ <p>Progress tracking will be shown here.</p>
10
+ </v-card>
11
+ </v-col>
12
+ </v-row>
13
+ </v-container>
14
+ </template>
15
+
16
+ <script setup lang="ts">
17
+ // Would import from common-ui or db package
18
+ </script>
@@ -0,0 +1,84 @@
1
+ <template>
2
+ <v-container>
3
+ <v-row>
4
+ <v-col cols="12">
5
+ <div v-if="sessionPrepared">
6
+ <StudySession
7
+ :content-sources="sessionContentSources"
8
+ :session-time-limit="sessionTimeLimit"
9
+ :user="user"
10
+ :session-config="studySessionConfig"
11
+ :data-layer="dataLayer"
12
+ :get-view-component="getViewComponent"
13
+ @session-finished="handleSessionFinished"
14
+ />
15
+ </div>
16
+ <div v-else>
17
+ <v-card class="pa-4">
18
+ <v-progress-circular indeterminate color="primary"></v-progress-circular>
19
+ <p class="mt-4">Preparing study session...</p>
20
+ </v-card>
21
+ </div>
22
+ </v-col>
23
+ </v-row>
24
+ </v-container>
25
+ </template>
26
+
27
+ <script setup lang="ts">
28
+ import { ref, onMounted } from 'vue';
29
+ import { ContentSourceID, getDataLayer } from '@vue-skuilder/db';
30
+ import { StudySession, type StudySessionConfig, useConfigStore } from '@vue-skuilder/common-ui';
31
+ import { allCourseWare } from '@vue-skuilder/courseware';
32
+ import ENV from '../ENVIRONMENT_VARS';
33
+
34
+ const user = getDataLayer().getUserDB();
35
+ const dataLayer = getDataLayer();
36
+ const configStore = useConfigStore();
37
+ const sessionPrepared = ref(false);
38
+ const sessionTimeLimit = ref(configStore.config.sessionTimeLimit);
39
+ const sessionContentSources = ref<ContentSourceID[]>([]);
40
+ const studySessionConfig = ref<StudySessionConfig>({
41
+ likesConfetti: configStore.config.likesConfetti,
42
+ });
43
+
44
+ // Function to get view component from courses
45
+ const getViewComponent = (view_id: string) => allCourseWare.getView(view_id);
46
+
47
+ // Initialize study session with course from environment vars
48
+ const initStudySession = async () => {
49
+ // Check if course ID is valid
50
+ if (!ENV.STATIC_COURSE_ID || ENV.STATIC_COURSE_ID === 'not_set') {
51
+ console.error('[StudyView] No course ID specified in environment vars!');
52
+ return;
53
+ }
54
+
55
+ console.log(`[StudyView] Starting study session for course: ${ENV.STATIC_COURSE_ID}`);
56
+
57
+ // Set the content source to the course ID from environment vars
58
+ sessionContentSources.value = [
59
+ {
60
+ type: 'course',
61
+ id: ENV.STATIC_COURSE_ID,
62
+ },
63
+ ];
64
+
65
+ // Mark the session as prepared
66
+ sessionPrepared.value = true;
67
+ };
68
+
69
+ // Handle session finished
70
+ const handleSessionFinished = () => {
71
+ // router.go(0); // Refresh the page
72
+ };
73
+
74
+ // Initialize on component mount
75
+ onMounted(async () => {
76
+ // user.value = await getCurrentUser();
77
+
78
+ // if (user.value) {
79
+ await initStudySession();
80
+ // } else {
81
+ // console.error('[StudyView] No user available!');
82
+ // }
83
+ });
84
+ </script>