@vue-skuilder/platform-ui 0.1.1
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.
- package/LICENCE +661 -0
- package/README.md +64 -0
- package/dist/assets/Roboto-Black-B0ZKieaB.woff +0 -0
- package/dist/assets/Roboto-Black-VhoA2qKx.woff2 +0 -0
- package/dist/assets/Roboto-BlackItalic-D0gSnuIb.woff +0 -0
- package/dist/assets/Roboto-BlackItalic-D4yie1YO.woff2 +0 -0
- package/dist/assets/Roboto-Bold-D9plYbeK.woff +0 -0
- package/dist/assets/Roboto-Bold-hN3duQhD.woff2 +0 -0
- package/dist/assets/Roboto-BoldItalic-BWDm51uc.woff2 +0 -0
- package/dist/assets/Roboto-BoldItalic-CyLKvOHD.woff +0 -0
- package/dist/assets/Roboto-Light-Cu-PAxXt.woff +0 -0
- package/dist/assets/Roboto-Light-DHTugVNA.woff2 +0 -0
- package/dist/assets/Roboto-LightItalic-CZg5kHIB.woff +0 -0
- package/dist/assets/Roboto-LightItalic-JQyp2Y3P.woff2 +0 -0
- package/dist/assets/Roboto-Medium-ByKogCTi.woff2 +0 -0
- package/dist/assets/Roboto-Medium-b81vv18W.woff +0 -0
- package/dist/assets/Roboto-MediumItalic-DFQ-RYa0.woff +0 -0
- package/dist/assets/Roboto-MediumItalic-i1eR0KbF.woff2 +0 -0
- package/dist/assets/Roboto-Regular-BX5l9hRW.woff +0 -0
- package/dist/assets/Roboto-Regular-C6rbFxYz.woff2 +0 -0
- package/dist/assets/Roboto-RegularItalic-BjnLZsam.woff +0 -0
- package/dist/assets/Roboto-RegularItalic-CvPUdkvM.woff2 +0 -0
- package/dist/assets/Roboto-Thin-BfJvJcog.woff +0 -0
- package/dist/assets/Roboto-Thin-NicBC1pN.woff2 +0 -0
- package/dist/assets/Roboto-ThinItalic-CKlCjrO_.woff2 +0 -0
- package/dist/assets/Roboto-ThinItalic-DnIWFxRE.woff +0 -0
- package/dist/assets/index-CQ-sNKGW.css +14 -0
- package/dist/assets/index-EbqpUgvM.js +161 -0
- package/dist/assets/materialdesignicons-webfont-B7mPwVP_.ttf +0 -0
- package/dist/assets/materialdesignicons-webfont-CSr8KVlo.eot +0 -0
- package/dist/assets/materialdesignicons-webfont-Dp5v-WZN.woff2 +0 -0
- package/dist/assets/materialdesignicons-webfont-PXm3-2wK.woff +0 -0
- package/dist/assets/workbox-window.prod.es5-p40uij6f.js +1 -0
- package/dist/favicon.ico +0 -0
- package/dist/img/icons/safari-pinned-tab.svg +149 -0
- package/dist/index.html +19 -0
- package/dist/manifest.json +20 -0
- package/dist/manifest.webmanifest +1 -0
- package/dist/robots.txt +2 -0
- package/dist/sw.js +1 -0
- package/dist/workbox-1be04862.js +1 -0
- package/package.json +105 -0
- package/src/App.vue +156 -0
- package/src/ENVIRONMENT_VARS.ts +79 -0
- package/src/components/Classrooms/ClassroomCtrlPanel.vue +206 -0
- package/src/components/Classrooms/CreateClassroom.vue +159 -0
- package/src/components/Classrooms/JoinCode.vue +83 -0
- package/src/components/Courses/CourseCardBrowser.vue +365 -0
- package/src/components/Courses/CourseEditor.vue +164 -0
- package/src/components/Courses/CourseInformation.vue +164 -0
- package/src/components/Courses/CourseRouter.vue +116 -0
- package/src/components/Courses/CourseStubCard.vue +76 -0
- package/src/components/Courses/EloModeration.vue +122 -0
- package/src/components/Courses/TagInformation.vue +209 -0
- package/src/components/Edit/BulkImport/CardPreviewList.vue +345 -0
- package/src/components/Edit/BulkImportView.vue +633 -0
- package/src/components/Edit/CardBrowser.vue +79 -0
- package/src/components/Edit/ComponentRegistration/ComponentRegistration.vue +235 -0
- package/src/components/Edit/ComponentRegistration/UnregisteredComponentsTable.vue +19 -0
- package/src/components/Edit/CourseEditor.vue +162 -0
- package/src/components/Edit/NavigationStrategy/NavigationStrategyEditor.vue +170 -0
- package/src/components/Edit/NavigationStrategy/NavigationStrategyList.vue +92 -0
- package/src/components/Edit/TagsInput.vue +247 -0
- package/src/components/Edit/ViewableDataInputForm/DataInputForm.vue +524 -0
- package/src/components/Edit/ViewableDataInputForm/FieldInput.types.ts +33 -0
- package/src/components/Edit/ViewableDataInputForm/FieldInputs/AudioInput.vue +188 -0
- package/src/components/Edit/ViewableDataInputForm/FieldInputs/ChessPuzzleInput.vue +79 -0
- package/src/components/Edit/ViewableDataInputForm/FieldInputs/FieldInput.css +12 -0
- package/src/components/Edit/ViewableDataInputForm/FieldInputs/ImageInput.vue +231 -0
- package/src/components/Edit/ViewableDataInputForm/FieldInputs/IntegerInput.vue +49 -0
- package/src/components/Edit/ViewableDataInputForm/FieldInputs/MarkdownInput.vue +34 -0
- package/src/components/Edit/ViewableDataInputForm/FieldInputs/MediaDragDropUploader.vue +246 -0
- package/src/components/Edit/ViewableDataInputForm/FieldInputs/MidiInput.vue +113 -0
- package/src/components/Edit/ViewableDataInputForm/FieldInputs/NumberInput.vue +49 -0
- package/src/components/Edit/ViewableDataInputForm/FieldInputs/StringInput.vue +49 -0
- package/src/components/Edit/ViewableDataInputForm/FieldInputs/typeValidators.ts +49 -0
- package/src/components/Edit/ViewableDataInputForm/OptionsFieldInput.ts +161 -0
- package/src/components/Study/SessionConfiguration.vue +371 -0
- package/src/components/TextSwap.vue +65 -0
- package/src/components/User/UserStats.vue +30 -0
- package/src/dev/DataInputFormTester.vue +117 -0
- package/src/dev/readme.md +3 -0
- package/src/enums.ts +0 -0
- package/src/glyphs.txt +933 -0
- package/src/main.ts +45 -0
- package/src/plugins/vuetify.ts +41 -0
- package/src/registerServiceWorker.ts +18 -0
- package/src/router.ts +184 -0
- package/src/server/index.spec.ts +192 -0
- package/src/server/index.ts +71 -0
- package/src/shims-vue.d.ts +5 -0
- package/src/store.mock.ts +122 -0
- package/src/stores/useDataInputFormStore.ts +49 -0
- package/src/stores/useFieldInputStore.ts +191 -0
- package/src/types/shims-vuetify.d.ts +12 -0
- package/src/types/svg.d.ts +4 -0
- package/src/utils/bulkImport/index.ts +94 -0
- package/src/views/About.vue +29 -0
- package/src/views/Admin.vue +128 -0
- package/src/views/Classrooms.vue +258 -0
- package/src/views/Courses.vue +265 -0
- package/src/views/Home.vue +154 -0
- package/src/views/Login.vue +75 -0
- package/src/views/ReleaseNotes.vue +20 -0
- package/src/views/SignUp.vue +32 -0
- package/src/views/Study.vue +261 -0
- package/src/views/User.vue +109 -0
- package/src/vite-env.d.ts +1 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-container class="d-flex align-center login">
|
|
3
|
+
<user-login v-if="!isUserLoggedIn" />
|
|
4
|
+
<v-card v-else class="w-100 pa-4">
|
|
5
|
+
<v-card-title class="text-center"> Already logged in! </v-card-title>
|
|
6
|
+
<v-card-text class="text-center">
|
|
7
|
+
You are currently logged in as <strong>{{ username }}</strong
|
|
8
|
+
>.
|
|
9
|
+
</v-card-text>
|
|
10
|
+
<v-card-actions class="justify-center">
|
|
11
|
+
<v-btn color="primary" @click="logout">
|
|
12
|
+
<v-icon start>mdi-logout</v-icon>
|
|
13
|
+
Log out
|
|
14
|
+
</v-btn>
|
|
15
|
+
</v-card-actions>
|
|
16
|
+
</v-card>
|
|
17
|
+
</v-container>
|
|
18
|
+
</template>
|
|
19
|
+
|
|
20
|
+
<script lang="ts">
|
|
21
|
+
import { defineComponent } from 'vue';
|
|
22
|
+
import { UserLogin, getCurrentUser, useAuthStore, useConfigStore } from '@vue-skuilder/common-ui';
|
|
23
|
+
|
|
24
|
+
export default defineComponent({
|
|
25
|
+
name: 'LoginRoute',
|
|
26
|
+
|
|
27
|
+
components: {
|
|
28
|
+
UserLogin,
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
data() {
|
|
32
|
+
return {
|
|
33
|
+
username: '',
|
|
34
|
+
authStore: useAuthStore(),
|
|
35
|
+
configStore: useConfigStore(),
|
|
36
|
+
};
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
computed: {
|
|
40
|
+
isUserLoggedIn(): boolean {
|
|
41
|
+
return this.authStore.isLoggedIn;
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
created() {
|
|
46
|
+
if (this.isUserLoggedIn) {
|
|
47
|
+
getCurrentUser().then((u) => {
|
|
48
|
+
this.username = u.getUsername();
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
methods: {
|
|
54
|
+
async logout() {
|
|
55
|
+
const res = await this.authStore._user!.logout();
|
|
56
|
+
if (res.ok) {
|
|
57
|
+
this.authStore.loginAndRegistration = {
|
|
58
|
+
init: true,
|
|
59
|
+
loggedIn: false,
|
|
60
|
+
regDialogOpen: false,
|
|
61
|
+
loginDialogOpen: false,
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
this.configStore.resetDefaults();
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
</script>
|
|
70
|
+
|
|
71
|
+
<style lang="css" scoped>
|
|
72
|
+
.login {
|
|
73
|
+
max-width: 650px;
|
|
74
|
+
}
|
|
75
|
+
</style>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<h1 class="text-h2 mb-6">Release Notes</h1>
|
|
4
|
+
|
|
5
|
+
<h3 class="text-h4 mb-4">0.0.1 - April 21</h3>
|
|
6
|
+
<ul>
|
|
7
|
+
<li>Hello world, these are release notes.</li>
|
|
8
|
+
</ul>
|
|
9
|
+
</div>
|
|
10
|
+
</template>
|
|
11
|
+
|
|
12
|
+
<script lang="ts">
|
|
13
|
+
import { defineComponent } from 'vue';
|
|
14
|
+
|
|
15
|
+
export default defineComponent({
|
|
16
|
+
name: 'ReleaseNotes',
|
|
17
|
+
});
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<style scoped></style>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-container class="d-flex align-center maxWidth">
|
|
3
|
+
<user-registration v-if="!isUserLoggedIn" />
|
|
4
|
+
<div v-else>Already logged in! Welcome back!</div>
|
|
5
|
+
</v-container>
|
|
6
|
+
</template>
|
|
7
|
+
|
|
8
|
+
<script lang="ts">
|
|
9
|
+
import { defineComponent } from 'vue';
|
|
10
|
+
import { UserRegistration, useAuthStore } from '@vue-skuilder/common-ui';
|
|
11
|
+
|
|
12
|
+
export default defineComponent({
|
|
13
|
+
name: 'LoginRoute',
|
|
14
|
+
|
|
15
|
+
components: {
|
|
16
|
+
UserRegistration,
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
computed: {
|
|
20
|
+
isUserLoggedIn(): boolean {
|
|
21
|
+
const authStore = useAuthStore();
|
|
22
|
+
return authStore.isLoggedIn;
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<style lang="css" scoped>
|
|
29
|
+
.maxWidth {
|
|
30
|
+
max-width: 650px;
|
|
31
|
+
}
|
|
32
|
+
</style>
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div v-if="!inSession">
|
|
3
|
+
<SessionConfiguration
|
|
4
|
+
:initial-time-limit="sessionTimeLimit"
|
|
5
|
+
@init-study-session="(sources, timeLimit) => initStudySession(sources, timeLimit)"
|
|
6
|
+
/>
|
|
7
|
+
</div>
|
|
8
|
+
<div v-else>
|
|
9
|
+
<div v-if="previewMode && previewCourseConfig" align="center">
|
|
10
|
+
<v-row>
|
|
11
|
+
<v-col>
|
|
12
|
+
<span class="text-h5">
|
|
13
|
+
Quilt preview for <em>{{ previewCourseConfig.name }}</em>
|
|
14
|
+
</span>
|
|
15
|
+
<v-btn size="small" color="primary" @click="registerUserForPreviewCourse">Join</v-btn>
|
|
16
|
+
<router-link :to="`/quilts/${previewCourseConfig.courseID}`">
|
|
17
|
+
<v-btn size="small" color="secondary">More info</v-btn>
|
|
18
|
+
</router-link>
|
|
19
|
+
<v-spacer></v-spacer>
|
|
20
|
+
</v-col>
|
|
21
|
+
</v-row>
|
|
22
|
+
</div>
|
|
23
|
+
<div v-else-if="previewMode">
|
|
24
|
+
<v-row>
|
|
25
|
+
<v-col>
|
|
26
|
+
<span class="text-h5">... No course was specified for the preview.</span>
|
|
27
|
+
<div>(this shouldn't happen)...</div>
|
|
28
|
+
</v-col>
|
|
29
|
+
</v-row>
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
<!-- Study Session Component (may be in loading state) -->
|
|
33
|
+
<div v-if="inSession">
|
|
34
|
+
<!-- Loading indicator while session is being prepared -->
|
|
35
|
+
<div v-if="!sessionPrepared && !sessionError" class="session-loading">
|
|
36
|
+
<v-container class="text-center">
|
|
37
|
+
<v-row justify="center" align="center" style="min-height: 50vh">
|
|
38
|
+
<v-col cols="12">
|
|
39
|
+
<v-progress-circular
|
|
40
|
+
size="70"
|
|
41
|
+
width="7"
|
|
42
|
+
color="primary"
|
|
43
|
+
indeterminate
|
|
44
|
+
></v-progress-circular>
|
|
45
|
+
<div class="text-h5 mt-4">Preparing your study session...</div>
|
|
46
|
+
<div class="text-subtitle-1 mt-2">Getting your learning materials ready</div>
|
|
47
|
+
</v-col>
|
|
48
|
+
</v-row>
|
|
49
|
+
</v-container>
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
<!-- Error state -->
|
|
53
|
+
<div v-if="sessionError" class="session-error">
|
|
54
|
+
<v-container class="text-center">
|
|
55
|
+
<v-row justify="center" align="center" style="min-height: 50vh">
|
|
56
|
+
<v-col cols="12">
|
|
57
|
+
<v-icon size="64" color="error">mdi-alert-circle</v-icon>
|
|
58
|
+
<div class="text-h5 mt-4 text-error">Session Preparation Failed</div>
|
|
59
|
+
<div class="text-subtitle-1 mt-2">{{ errorMessage || 'There was a problem preparing your study session.' }}</div>
|
|
60
|
+
<v-btn color="primary" class="mt-6" @click="refreshRoute">Try Again</v-btn>
|
|
61
|
+
</v-col>
|
|
62
|
+
</v-row>
|
|
63
|
+
</v-container>
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
<StudySession
|
|
67
|
+
:content-sources="sessionContentSources"
|
|
68
|
+
:session-time-limit="sessionTimeLimit"
|
|
69
|
+
:user="user as UserDBInterface"
|
|
70
|
+
:session-config="studySessionConfig"
|
|
71
|
+
:data-layer="dataLayer"
|
|
72
|
+
:get-view-component="getViewComponent"
|
|
73
|
+
:class="{ 'hidden-session': !sessionPrepared }"
|
|
74
|
+
@session-finished="handleSessionFinished"
|
|
75
|
+
@session-prepared="handleSessionPrepared"
|
|
76
|
+
@session-error="handleSessionError"
|
|
77
|
+
/>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
</template>
|
|
81
|
+
|
|
82
|
+
<script lang="ts">
|
|
83
|
+
import SessionConfiguration from '@/components/Study/SessionConfiguration.vue';
|
|
84
|
+
import { getCurrentUser, useConfigStore } from '@vue-skuilder/common-ui';
|
|
85
|
+
import { useDataInputFormStore } from '@/stores/useDataInputFormStore';
|
|
86
|
+
import { CourseConfig } from '@vue-skuilder/common';
|
|
87
|
+
import { StudySession, type StudySessionConfig } from '@vue-skuilder/common-ui';
|
|
88
|
+
import { allCourses } from '@vue-skuilder/courses';
|
|
89
|
+
import { ContentSourceID, UserDBInterface, getDataLayer } from '@vue-skuilder/db';
|
|
90
|
+
import { defineComponent } from 'vue';
|
|
91
|
+
import { Router } from 'vue-router';
|
|
92
|
+
|
|
93
|
+
function randomInt(min: number, max: number): number {
|
|
94
|
+
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export default defineComponent({
|
|
98
|
+
name: 'StudyView',
|
|
99
|
+
|
|
100
|
+
components: {
|
|
101
|
+
SessionConfiguration,
|
|
102
|
+
StudySession,
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
inject: {
|
|
106
|
+
router: {
|
|
107
|
+
from: 'router',
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
props: {
|
|
112
|
+
/**
|
|
113
|
+
* If present, user will engage in a study session for the specified (non-registered) course.
|
|
114
|
+
*/
|
|
115
|
+
previewCourseID: {
|
|
116
|
+
type: String,
|
|
117
|
+
required: false,
|
|
118
|
+
default: '',
|
|
119
|
+
},
|
|
120
|
+
/**
|
|
121
|
+
* If true, the user will engage in a study session for a
|
|
122
|
+
* random (public) course they are not already registered for.
|
|
123
|
+
*/
|
|
124
|
+
randomPreview: {
|
|
125
|
+
type: Boolean,
|
|
126
|
+
required: false,
|
|
127
|
+
},
|
|
128
|
+
/**
|
|
129
|
+
* If present, user will engage in a study session for the specified (registered) course.
|
|
130
|
+
*/
|
|
131
|
+
focusCourseID: {
|
|
132
|
+
type: String,
|
|
133
|
+
required: false,
|
|
134
|
+
default: '',
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
data() {
|
|
139
|
+
return {
|
|
140
|
+
user: null as UserDBInterface | null,
|
|
141
|
+
studySessionConfig: undefined as StudySessionConfig | undefined,
|
|
142
|
+
previewCourseConfig: undefined as CourseConfig | undefined,
|
|
143
|
+
previewMode: false,
|
|
144
|
+
sessionTimeLimit: 5,
|
|
145
|
+
inSession: false,
|
|
146
|
+
sessionPrepared: false,
|
|
147
|
+
sessionError: false,
|
|
148
|
+
errorMessage: '',
|
|
149
|
+
sessionContentSources: [] as ContentSourceID[],
|
|
150
|
+
dataInputFormStore: useDataInputFormStore(),
|
|
151
|
+
getViewComponent: (view_id: string) => allCourses.getView(view_id),
|
|
152
|
+
dataLayer: getDataLayer(),
|
|
153
|
+
};
|
|
154
|
+
},
|
|
155
|
+
|
|
156
|
+
async created() {
|
|
157
|
+
this.user = await getCurrentUser();
|
|
158
|
+
this.studySessionConfig = {
|
|
159
|
+
likesConfetti: useConfigStore().config.likesConfetti,
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
let singletonStudyCourseID = '';
|
|
163
|
+
|
|
164
|
+
if (this.randomPreview) {
|
|
165
|
+
const userCourseRegDoc = await this.user.getCourseRegistrationsDoc();
|
|
166
|
+
const allCourses = (await getDataLayer().getCoursesDB().getCourseList()).map((r) => r.courseID);
|
|
167
|
+
const unRegisteredCourses = allCourses.filter((c) => {
|
|
168
|
+
return !userCourseRegDoc.courses.some((rc) => rc.courseID === c);
|
|
169
|
+
});
|
|
170
|
+
if (unRegisteredCourses.length > 0) {
|
|
171
|
+
singletonStudyCourseID = unRegisteredCourses[randomInt(0, unRegisteredCourses.length)]!;
|
|
172
|
+
} else {
|
|
173
|
+
singletonStudyCourseID = allCourses[randomInt(0, allCourses.length)]!;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (this.previewCourseID) {
|
|
178
|
+
this.previewMode = true;
|
|
179
|
+
getDataLayer()
|
|
180
|
+
.getCoursesDB()
|
|
181
|
+
.getCourseList()
|
|
182
|
+
.then((courses) => {
|
|
183
|
+
courses.forEach((c) => {
|
|
184
|
+
if (c.courseID === this.previewCourseID) {
|
|
185
|
+
this.previewCourseConfig = c;
|
|
186
|
+
this.previewCourseConfig!.courseID = c.courseID;
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
console.log(`[Study] COURSE PREVIEW MODE FOR ${this.previewCourseID}`);
|
|
192
|
+
await this.user!.registerForCourse(this.previewCourseID, true);
|
|
193
|
+
|
|
194
|
+
singletonStudyCourseID = this.previewCourseID;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (this.focusCourseID) {
|
|
198
|
+
console.log(`[Study] FOCUS study session: ${this.focusCourseID}`);
|
|
199
|
+
singletonStudyCourseID = this.focusCourseID;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (singletonStudyCourseID) {
|
|
203
|
+
this.initStudySession([{ type: 'course', id: singletonStudyCourseID }], this.sessionTimeLimit);
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
|
|
207
|
+
methods: {
|
|
208
|
+
refreshRoute() {
|
|
209
|
+
(this.router as Router).go(0);
|
|
210
|
+
},
|
|
211
|
+
|
|
212
|
+
async initStudySession(sources: ContentSourceID[], timeLimit: number) {
|
|
213
|
+
console.log(`[Study] starting study session w/ sources: ${JSON.stringify(sources)}`);
|
|
214
|
+
|
|
215
|
+
this.sessionContentSources = sources;
|
|
216
|
+
this.sessionTimeLimit = timeLimit;
|
|
217
|
+
this.inSession = true;
|
|
218
|
+
this.sessionPrepared = false;
|
|
219
|
+
|
|
220
|
+
// Adding a console log to debug event handling
|
|
221
|
+
console.log('[Study] Waiting for session-prepared event from StudySession component');
|
|
222
|
+
},
|
|
223
|
+
|
|
224
|
+
registerUserForPreviewCourse() {
|
|
225
|
+
this.user!.registerForCourse(this.previewCourseConfig!.courseID!).then(() =>
|
|
226
|
+
(this.router as Router).push(`/quilts/${this.previewCourseConfig!.courseID!}`)
|
|
227
|
+
);
|
|
228
|
+
},
|
|
229
|
+
|
|
230
|
+
handleSessionFinished() {
|
|
231
|
+
this.refreshRoute();
|
|
232
|
+
},
|
|
233
|
+
|
|
234
|
+
handleSessionPrepared() {
|
|
235
|
+
console.log('[Study] Session preparation complete - received session-prepared event');
|
|
236
|
+
this.sessionPrepared = true;
|
|
237
|
+
this.sessionError = false;
|
|
238
|
+
this.errorMessage = '';
|
|
239
|
+
},
|
|
240
|
+
|
|
241
|
+
handleSessionError({ message, error }) {
|
|
242
|
+
console.error('[Study] Session error:', message, error);
|
|
243
|
+
this.sessionError = true;
|
|
244
|
+
this.errorMessage = message || 'An error occurred while preparing your study session.';
|
|
245
|
+
this.sessionPrepared = false;
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
});
|
|
249
|
+
</script>
|
|
250
|
+
|
|
251
|
+
<style scoped>
|
|
252
|
+
.hidden-session {
|
|
253
|
+
visibility: hidden;
|
|
254
|
+
position: absolute;
|
|
255
|
+
z-index: -1;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.session-error {
|
|
259
|
+
color: var(--v-error-base);
|
|
260
|
+
}
|
|
261
|
+
</style>
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="text-subtitle-1">
|
|
3
|
+
<v-alert v-if="isNewUser" type="success" class="text-subtitle-1" variant="tonal" :prepend-icon="'mdi-check'">
|
|
4
|
+
Welcome, {{ username }}! Please take a moment to look through these settings:
|
|
5
|
+
</v-alert>
|
|
6
|
+
|
|
7
|
+
<h1 class="text-h3">Account Settings</h1>
|
|
8
|
+
<h2 class="text-h4">General:</h2>
|
|
9
|
+
|
|
10
|
+
<v-checkbox
|
|
11
|
+
v-model="configStore.config.likesConfetti"
|
|
12
|
+
label="I like confetti"
|
|
13
|
+
@update:model-value="updateConfetti"
|
|
14
|
+
/>
|
|
15
|
+
<v-checkbox v-model="configStore.config.darkMode" label="I like the dark" @update:model-value="updateDark" />
|
|
16
|
+
<!-- <h2 class="text-h4">Languages:</h2>
|
|
17
|
+
I am near-fluent or better in the following languages:
|
|
18
|
+
{{ selectedLanguages.toString() }}
|
|
19
|
+
<v-checkbox
|
|
20
|
+
v-for="(language, i) in configLanguages"
|
|
21
|
+
:key="i"
|
|
22
|
+
:label="language.name"
|
|
23
|
+
:value="language.code"
|
|
24
|
+
v-model="selectedLanguages"
|
|
25
|
+
@update:model-value="updateLanguage"
|
|
26
|
+
/> -->
|
|
27
|
+
</div>
|
|
28
|
+
</template>
|
|
29
|
+
|
|
30
|
+
<script lang="ts">
|
|
31
|
+
import { getCurrentUser, useConfigStore } from '@vue-skuilder/common-ui';
|
|
32
|
+
import { UserDBInterface } from '@vue-skuilder/db';
|
|
33
|
+
import confetti from 'canvas-confetti';
|
|
34
|
+
import { defineComponent, PropType } from 'vue';
|
|
35
|
+
import { useRoute } from 'vue-router';
|
|
36
|
+
|
|
37
|
+
interface Language {
|
|
38
|
+
name: string;
|
|
39
|
+
code: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export default defineComponent({
|
|
43
|
+
name: 'UserSettings',
|
|
44
|
+
|
|
45
|
+
props: {
|
|
46
|
+
username: {
|
|
47
|
+
type: String as PropType<string>,
|
|
48
|
+
required: true,
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
setup() {
|
|
53
|
+
const configStore = useConfigStore();
|
|
54
|
+
const route = useRoute();
|
|
55
|
+
|
|
56
|
+
const darkMode = configStore.config.darkMode;
|
|
57
|
+
const likesConfetti = configStore.config.likesConfetti;
|
|
58
|
+
|
|
59
|
+
const isNewUser = route.path.endsWith('new');
|
|
60
|
+
|
|
61
|
+
return { configStore, darkMode, likesConfetti, route, isNewUser };
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
data() {
|
|
65
|
+
return {
|
|
66
|
+
u: {} as UserDBInterface,
|
|
67
|
+
configLanguages: [
|
|
68
|
+
{
|
|
69
|
+
name: 'English',
|
|
70
|
+
code: 'en',
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
name: 'French',
|
|
74
|
+
code: 'fr',
|
|
75
|
+
},
|
|
76
|
+
] as Language[],
|
|
77
|
+
selectedLanguages: [] as string[],
|
|
78
|
+
};
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
async created() {
|
|
82
|
+
this.u = await getCurrentUser();
|
|
83
|
+
this.configLanguages.forEach((l) => {
|
|
84
|
+
console.log(`afweatifvwzeatfvwzeta` + l.name);
|
|
85
|
+
});
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
methods: {
|
|
89
|
+
updateDark(): void {
|
|
90
|
+
this.configStore.updateDarkMode(this.configStore.config.darkMode);
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
updateConfetti(): void {
|
|
94
|
+
this.configStore.updateLikesConfetti(this.configStore.config.likesConfetti);
|
|
95
|
+
|
|
96
|
+
if (this.configStore.config.likesConfetti) {
|
|
97
|
+
confetti({
|
|
98
|
+
origin: {
|
|
99
|
+
x: 0.5,
|
|
100
|
+
y: 1,
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
</script>
|
|
108
|
+
|
|
109
|
+
<style scoped></style>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="vite-plugin-pwa/client" />
|