@vue-skuilder/platform-ui 0.1.32-e → 0.1.32-f
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/dist/assets/About-C1hlB_VW.js +2 -0
- package/dist/assets/About-C1hlB_VW.js.map +1 -0
- package/dist/assets/AdminDashboard-BOyrJfAQ.js +2 -0
- package/dist/assets/AdminDashboard-BOyrJfAQ.js.map +1 -0
- package/dist/assets/ClassroomCtrlPanel-vV8w0uBc.js +2 -0
- package/dist/assets/ClassroomCtrlPanel-vV8w0uBc.js.map +1 -0
- package/dist/assets/Classrooms-BZmnwuIB.js +3 -0
- package/dist/assets/Classrooms-BZmnwuIB.js.map +1 -0
- package/dist/assets/CourseRouter-DFPpLiug.js +7 -0
- package/dist/assets/CourseRouter-DFPpLiug.js.map +1 -0
- package/dist/assets/{CourseRouter-DuzvSsqI.css → CourseRouter-DvfjFpWl.css} +1 -1
- package/dist/assets/Courses-CsGfVDf9.css +1 -0
- package/dist/assets/Courses-CyWX8Aq4.js +3 -0
- package/dist/assets/Courses-CyWX8Aq4.js.map +1 -0
- package/dist/assets/DataInputFormTester-DtEEvEZS.js +2 -0
- package/dist/assets/DataInputFormTester-DtEEvEZS.js.map +1 -0
- package/dist/assets/EloModeration-DgUqPPoO.css +1 -0
- package/dist/assets/EloModeration-_LQTP9qx.js +2 -0
- package/dist/assets/EloModeration-_LQTP9qx.js.map +1 -0
- package/dist/assets/JoinCode-BQeXXd8M.js +3 -0
- package/dist/assets/JoinCode-BQeXXd8M.js.map +1 -0
- package/dist/assets/JoinCode-DPSfXf5z.css +1 -0
- package/dist/assets/MarkdownRenderer-DoVbFpA6-BIbDxYC4.js +53 -0
- package/dist/assets/MarkdownRenderer-DoVbFpA6-BIbDxYC4.js.map +1 -0
- package/dist/assets/MarkdownRenderer-DoVbFpA6-CQGwEJFh.js +1 -0
- package/dist/assets/NewCourseDialog-lQEm3iJe.js +2 -0
- package/dist/assets/NewCourseDialog-lQEm3iJe.js.map +1 -0
- package/dist/assets/ReleaseNotes-BSxuZ5W7.js +2 -0
- package/dist/assets/ReleaseNotes-BSxuZ5W7.js.map +1 -0
- package/dist/assets/RequestPasswordReset-GPKssrPH.js +2 -0
- package/dist/assets/RequestPasswordReset-GPKssrPH.js.map +1 -0
- package/dist/assets/ResetPassword-By7RvJkB.js +2 -0
- package/dist/assets/ResetPassword-By7RvJkB.js.map +1 -0
- package/dist/assets/Study-R4LCAP7J.css +1 -0
- package/dist/assets/Study-yDCrOQ1i.js +2 -0
- package/dist/assets/Study-yDCrOQ1i.js.map +1 -0
- package/dist/assets/TagInformation-BjkMAGf0.js +2 -0
- package/dist/assets/TagInformation-BjkMAGf0.js.map +1 -0
- package/dist/assets/User-1-_6oW2W.js +3 -0
- package/dist/assets/User-1-_6oW2W.js.map +1 -0
- package/dist/assets/UserStats-BW2xTraR.js +2 -0
- package/dist/assets/UserStats-BW2xTraR.js.map +1 -0
- package/dist/assets/VerifyEmail-B5pYw6N1.js +2 -0
- package/dist/assets/VerifyEmail-B5pYw6N1.js.map +1 -0
- package/dist/assets/chunk-D6hFPZc3.js +1 -0
- package/dist/assets/common-ui-BY2Ut5jf.css +1 -0
- package/dist/assets/common-ui.es-Bc_4fHTb.js +1 -0
- package/dist/assets/common-ui.es-mrcutgtG.js +38 -0
- package/dist/assets/common-ui.es-mrcutgtG.js.map +1 -0
- package/dist/assets/dist-BlP52QF6.js +186 -0
- package/dist/assets/dist-BlP52QF6.js.map +1 -0
- package/dist/assets/dist-C2-hg8GS.js +1 -0
- package/dist/assets/dist-CFgc27Ho.js +1 -0
- package/dist/assets/dist-CrcJCHFk.js +12 -0
- package/dist/assets/dist-CrcJCHFk.js.map +1 -0
- package/dist/assets/dist-DkeESCP4.js +353 -0
- package/dist/assets/dist-DkeESCP4.js.map +1 -0
- package/dist/assets/edit-ui.es-CLyYUxsi.js +36 -0
- package/dist/assets/edit-ui.es-CLyYUxsi.js.map +1 -0
- package/dist/assets/edit-ui.es-DWg1GnhC.js +1 -0
- package/dist/assets/index-CCh27qXk.css +1 -0
- package/dist/assets/index-jZbLHVSV.js +4 -0
- package/dist/assets/index-jZbLHVSV.js.map +1 -0
- package/dist/assets/server-DvlG_kpF.js +2 -0
- package/dist/assets/server-DvlG_kpF.js.map +1 -0
- package/dist/assets/vue.runtime.esm-bundler-C3q8JS9f.js +4 -0
- package/dist/assets/vue.runtime.esm-bundler-C3q8JS9f.js.map +1 -0
- package/dist/assets/workbox-window.prod.es5-DBwpKi3B.js +3 -0
- package/dist/assets/workbox-window.prod.es5-DBwpKi3B.js.map +1 -0
- package/dist/index.html +9 -2
- package/dist/sw.js +1 -1
- package/dist/sw.js.map +1 -1
- package/package.json +9 -11
- package/dist/assets/About-C3u52t0d.js +0 -2
- package/dist/assets/About-C3u52t0d.js.map +0 -1
- package/dist/assets/AdminDashboard-Dz5u2cex.js +0 -2
- package/dist/assets/AdminDashboard-Dz5u2cex.js.map +0 -1
- package/dist/assets/ClassroomCtrlPanel-Dk6oR2Ns.js +0 -2
- package/dist/assets/ClassroomCtrlPanel-Dk6oR2Ns.js.map +0 -1
- package/dist/assets/Classrooms-vb9OkpYQ.js +0 -2
- package/dist/assets/Classrooms-vb9OkpYQ.js.map +0 -1
- package/dist/assets/CourseRouter-Blm2dYn-.js +0 -2
- package/dist/assets/CourseRouter-Blm2dYn-.js.map +0 -1
- package/dist/assets/Courses-CEw9ARaf.js +0 -2
- package/dist/assets/Courses-CEw9ARaf.js.map +0 -1
- package/dist/assets/Courses-DQW3kca9.css +0 -1
- package/dist/assets/DataInputFormTester-CGzywVuh.js +0 -2
- package/dist/assets/DataInputFormTester-CGzywVuh.js.map +0 -1
- package/dist/assets/EloModeration-B90G61YI.css +0 -1
- package/dist/assets/EloModeration-DAmUYf5F.js +0 -2
- package/dist/assets/EloModeration-DAmUYf5F.js.map +0 -1
- package/dist/assets/JoinCode-B3oyqgv9.js +0 -2
- package/dist/assets/JoinCode-B3oyqgv9.js.map +0 -1
- package/dist/assets/JoinCode-DFiral3R.css +0 -1
- package/dist/assets/NewCourseDialog-5K53pY-W.js +0 -2
- package/dist/assets/NewCourseDialog-5K53pY-W.js.map +0 -1
- package/dist/assets/ReleaseNotes-DJQ5CKxz.js +0 -2
- package/dist/assets/ReleaseNotes-DJQ5CKxz.js.map +0 -1
- package/dist/assets/RequestPasswordReset-1bvNIeyh.js +0 -2
- package/dist/assets/RequestPasswordReset-1bvNIeyh.js.map +0 -1
- package/dist/assets/ResetPassword-CDlcjyGc.js +0 -2
- package/dist/assets/ResetPassword-CDlcjyGc.js.map +0 -1
- package/dist/assets/Study-Ci2qFB3t.js +0 -2
- package/dist/assets/Study-Ci2qFB3t.js.map +0 -1
- package/dist/assets/Study-Cxxw_AYM.css +0 -1
- package/dist/assets/TagInformation-CtiIiC9l.js +0 -2
- package/dist/assets/TagInformation-CtiIiC9l.js.map +0 -1
- package/dist/assets/User-CWCez7if.js +0 -2
- package/dist/assets/User-CWCez7if.js.map +0 -1
- package/dist/assets/UserStats-BDj614VL.js +0 -2
- package/dist/assets/UserStats-BDj614VL.js.map +0 -1
- package/dist/assets/VerifyEmail-Bt6Mb6Q3.js +0 -2
- package/dist/assets/VerifyEmail-Bt6Mb6Q3.js.map +0 -1
- package/dist/assets/edit-ui.es-BE3brRCi.js +0 -2
- package/dist/assets/edit-ui.es-BE3brRCi.js.map +0 -1
- package/dist/assets/index-C8n-yF4Q.js +0 -3
- package/dist/assets/index-C8n-yF4Q.js.map +0 -1
- package/dist/assets/index-CfcuevG4.js +0 -3
- package/dist/assets/index-CfcuevG4.js.map +0 -1
- package/dist/assets/index-DpBS80zR.css +0 -1
- package/dist/assets/mousetrap-Ca0hLdT-.js +0 -2
- package/dist/assets/mousetrap-Ca0hLdT-.js.map +0 -1
- package/dist/assets/workbox-window.prod.es5-p40uij6f.js +0 -2
- package/dist/assets/workbox-window.prod.es5-p40uij6f.js.map +0 -1
- package/dist/manifest.webmanifest +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./vue.runtime.esm-bundler-C3q8JS9f.js";import"./dist-CrcJCHFk.js";import{w as e}from"./MarkdownRenderer-DoVbFpA6-BIbDxYC4.js";export{e as n};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{s as e}from"./chunk-D6hFPZc3.js";import{$ as t,C as n,H as r,K as i,S as a,T as o,_ as s,v as c}from"./vue.runtime.esm-bundler-C3q8JS9f.js";import{T as l,v as u}from"./common-ui.es-mrcutgtG.js";import{w as d}from"./dist-CrcJCHFk.js";import{t as f}from"./index-jZbLHVSV.js";import{n as p,t as m}from"./server-DvlG_kpF.js";var h=e(p()),g=o({name:`NewCourseDialog`,props:{name:{type:String,required:!1,default:``}},emits:[`CourseEditingComplete`],data(){return{mousetrap:new h.default(this.$el),id:``,courseName:``,description:``,publicCourse:!1,deleted:!1,admins:[],moderators:[],dataShapes:[],questionTypes:[],banner:void 0,thumb:void 0,updatePending:!1,nameRules:[e=>e.length>30?`Course name must be 30 characters or less`:!0]}},created(){this.mousetrap.bind(`esc`,this.clearFormAndDismiss)},methods:{async submit(){this.updatePending=!0;let e=await l(),t=await m({data:{name:this.courseName,description:this.description,public:this.publicCourse,deleted:this.deleted,creator:e.getUsername(),admins:this.admins,moderators:this.moderators,dataShapes:this.dataShapes,questionTypes:this.questionTypes},type:d.CREATE_COURSE,response:null,user:e.getUsername()});t.response&&t.response.ok?(u({text:`Course ${this.courseName} created.`,status:t.response.status}),this.clearFormAndDismiss()):(u({text:`Failed to create course ${this.courseName}.`,status:t.response.status}),console.warn(`Resp: ${JSON.stringify(t.response)}`)),this.updatePending=!1},clearFormAndDismiss(){this.description=``,this.publicCourse=!1,this.deleted=!1,this.admins=[],this.moderators=[],this.$emit(`CourseEditingComplete`)}}});function _sfc_render(e,o,l,u,d,f){let p=i(`v-card-title`),m=i(`v-spacer`),h=i(`v-icon`),g=i(`v-btn`),_=i(`v-toolbar`),v=i(`v-text-field`),y=i(`v-row`),b=i(`v-textarea`),x=i(`v-radio`),S=i(`v-radio-group`),C=i(`v-container`),w=i(`v-form`),T=i(`v-card`);return r(),c(T,null,{default:t(()=>[n(_,{flat:``,color:`primary`,dark:``},{default:t(()=>[n(p,{class:`text-h6 font-weight-regular`},{default:t(()=>o[3]||(o[3]=[a(` Start a New Quilt `)])),_:1}),n(m),n(g,{icon:``,onClick:e.clearFormAndDismiss},{default:t(()=>[n(h,{icon:`mdi-close`})]),_:1},8,[`onClick`])]),_:1}),n(w,null,{default:t(()=>[n(C,null,{default:t(()=>[n(y,{class:`cols sm md`},{default:t(()=>[n(v,{modelValue:e.courseName,"onUpdate:modelValue":o[0]||(o[0]=t=>e.courseName=t),counter:`30`,rules:e.nameRules,label:`Quilt Name`,required:``,hint:`Short and descriptive`,"data-cy":`course-name-input`},null,8,[`modelValue`,`rules`])]),_:1}),n(y,{class:`cols sm md`},{default:t(()=>[n(b,{modelValue:e.description,"onUpdate:modelValue":o[1]||(o[1]=t=>e.description=t),variant:`outlined`,counter:`300`,"auto-grow":``,label:`Quilt Description`,hint:`Describe the course. What subject is covered? Who might be interested?`,"data-cy":`course-description-input`},null,8,[`modelValue`])]),_:1}),n(y,{class:`cols sm md`},{default:t(()=>[o[4]||(o[4]=s(`label`,null,`Public or private quilt?`,-1)),n(S,{modelValue:e.publicCourse,"onUpdate:modelValue":o[2]||(o[2]=t=>e.publicCourse=t),required:``,hint:`Private quilts can be shared and collaborated on with other individual users, but will not be accessable without an invitation. A private quilt can be made public later.`,"persistent-hint":``,inline:``,"data-cy":`course-visibility-radio`},{default:t(()=>[n(x,{label:`Public`,value:!0,"data-cy":`public-radio`}),n(x,{label:`Private`,value:!1,"data-cy":`private-radio`})]),_:1},8,[`modelValue`])]),_:1}),n(y,{class:`cols sm md`},{default:t(()=>[n(g,{loading:e.updatePending,color:`primary`,"data-cy":`save-course-button`,onClick:e.submit},{default:t(()=>o[5]||(o[5]=[a(` Save Course Changes `)])),_:1},8,[`loading`,`onClick`])]),_:1})]),_:1})]),_:1})]),_:1})}var _=f(g,[[`render`,_sfc_render]]);export{_ as t};
|
|
2
|
+
//# sourceMappingURL=NewCourseDialog-lQEm3iJe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NewCourseDialog-lQEm3iJe.js","names":[],"sources":["../../src/components/Courses/NewCourseDialog.vue","../../src/components/Courses/NewCourseDialog.vue"],"sourcesContent":["<template>\n <v-card>\n <v-toolbar flat color=\"primary\" dark>\n <v-card-title class=\"text-h6 font-weight-regular\"> Start a New Quilt </v-card-title>\n <v-spacer></v-spacer>\n <v-btn icon @click=\"clearFormAndDismiss\">\n <v-icon icon=\"mdi-close\"></v-icon>\n </v-btn>\n </v-toolbar>\n <v-form>\n <v-container>\n <v-row class=\"cols sm md\">\n <v-text-field\n v-model=\"courseName\"\n counter=\"30\"\n :rules=\"nameRules\"\n label=\"Quilt Name\"\n required\n hint=\"Short and descriptive\"\n data-cy=\"course-name-input\"\n ></v-text-field>\n </v-row>\n <v-row class=\"cols sm md\">\n <v-textarea\n v-model=\"description\"\n variant=\"outlined\"\n counter=\"300\"\n auto-grow\n label=\"Quilt Description\"\n hint=\"Describe the course. What subject is covered? Who might be interested?\"\n data-cy=\"course-description-input\"\n >\n </v-textarea>\n </v-row>\n <v-row class=\"cols sm md\">\n <label>Public or private quilt?</label>\n <v-radio-group\n v-model=\"publicCourse\"\n required\n hint=\"Private quilts can be shared and collaborated on with other individual users, but will not be accessable without an invitation. A private quilt can be made public later.\"\n persistent-hint\n inline\n data-cy=\"course-visibility-radio\"\n >\n <v-radio label=\"Public\" :value=\"true\" data-cy=\"public-radio\"></v-radio>\n <v-radio label=\"Private\" :value=\"false\" data-cy=\"private-radio\"></v-radio>\n </v-radio-group>\n </v-row>\n <v-row class=\"cols sm md\">\n <v-btn :loading=\"updatePending\" color=\"primary\" data-cy=\"save-course-button\" @click=\"submit\">\n Save Course Changes\n </v-btn>\n </v-row>\n </v-container>\n </v-form>\n </v-card>\n</template>\n\n<script lang=\"ts\">\nimport { defineComponent } from 'vue';\nimport Mousetrap from 'mousetrap';\nimport serverRequest from '../../server';\nimport { CourseConfig, CreateCourse, DataShape55, QuestionType55, ServerRequestType } from '@vue-skuilder/common';\nimport { alertUser, getCurrentUser } from '@vue-skuilder/common-ui';\n\nexport default defineComponent({\n name: 'NewCourseDialog',\n\n props: {\n name: {\n type: String,\n required: false,\n default: '',\n },\n },\n\n emits: ['CourseEditingComplete'],\n\n data() {\n return {\n mousetrap: new Mousetrap(this.$el),\n id: '',\n courseName: '',\n description: '',\n publicCourse: false,\n deleted: false,\n admins: [] as string[],\n moderators: [] as string[],\n dataShapes: [] as DataShape55[],\n questionTypes: [] as QuestionType55[],\n banner: undefined as Blob | undefined,\n thumb: undefined as Blob | undefined,\n updatePending: false,\n nameRules: [\n (value: string): string | boolean => {\n const max = 30;\n if (value.length > max) {\n return `Course name must be ${max} characters or less`;\n } else {\n return true;\n }\n },\n ],\n };\n },\n\n created() {\n this.mousetrap.bind('esc', this.clearFormAndDismiss);\n },\n\n methods: {\n async submit() {\n this.updatePending = true;\n\n const u = await getCurrentUser();\n\n const config: CourseConfig = {\n name: this.courseName,\n description: this.description,\n public: this.publicCourse,\n deleted: this.deleted,\n creator: u.getUsername(),\n admins: this.admins,\n moderators: this.moderators,\n dataShapes: this.dataShapes,\n questionTypes: this.questionTypes,\n };\n\n const result = await serverRequest<CreateCourse>({\n data: config,\n type: ServerRequestType.CREATE_COURSE,\n response: null,\n user: u.getUsername(),\n });\n\n if (result.response && result.response.ok) {\n alertUser({\n text: `Course ${this.courseName} created.`,\n status: result.response!.status,\n });\n this.clearFormAndDismiss();\n } else {\n alertUser({\n text: `Failed to create course ${this.courseName}.`,\n status: result.response!.status,\n });\n console.warn(`Resp: ${JSON.stringify(result.response)}`);\n }\n\n this.updatePending = false;\n },\n\n clearFormAndDismiss() {\n this.description = '';\n this.publicCourse = false;\n this.deleted = false;\n this.admins = [];\n this.moderators = [];\n\n this.$emit('CourseEditingComplete');\n },\n },\n});\n</script>\n","<template>\n <v-card>\n <v-toolbar flat color=\"primary\" dark>\n <v-card-title class=\"text-h6 font-weight-regular\"> Start a New Quilt </v-card-title>\n <v-spacer></v-spacer>\n <v-btn icon @click=\"clearFormAndDismiss\">\n <v-icon icon=\"mdi-close\"></v-icon>\n </v-btn>\n </v-toolbar>\n <v-form>\n <v-container>\n <v-row class=\"cols sm md\">\n <v-text-field\n v-model=\"courseName\"\n counter=\"30\"\n :rules=\"nameRules\"\n label=\"Quilt Name\"\n required\n hint=\"Short and descriptive\"\n data-cy=\"course-name-input\"\n ></v-text-field>\n </v-row>\n <v-row class=\"cols sm md\">\n <v-textarea\n v-model=\"description\"\n variant=\"outlined\"\n counter=\"300\"\n auto-grow\n label=\"Quilt Description\"\n hint=\"Describe the course. What subject is covered? Who might be interested?\"\n data-cy=\"course-description-input\"\n >\n </v-textarea>\n </v-row>\n <v-row class=\"cols sm md\">\n <label>Public or private quilt?</label>\n <v-radio-group\n v-model=\"publicCourse\"\n required\n hint=\"Private quilts can be shared and collaborated on with other individual users, but will not be accessable without an invitation. A private quilt can be made public later.\"\n persistent-hint\n inline\n data-cy=\"course-visibility-radio\"\n >\n <v-radio label=\"Public\" :value=\"true\" data-cy=\"public-radio\"></v-radio>\n <v-radio label=\"Private\" :value=\"false\" data-cy=\"private-radio\"></v-radio>\n </v-radio-group>\n </v-row>\n <v-row class=\"cols sm md\">\n <v-btn :loading=\"updatePending\" color=\"primary\" data-cy=\"save-course-button\" @click=\"submit\">\n Save Course Changes\n </v-btn>\n </v-row>\n </v-container>\n </v-form>\n </v-card>\n</template>\n\n<script lang=\"ts\">\nimport { defineComponent } from 'vue';\nimport Mousetrap from 'mousetrap';\nimport serverRequest from '../../server';\nimport { CourseConfig, CreateCourse, DataShape55, QuestionType55, ServerRequestType } from '@vue-skuilder/common';\nimport { alertUser, getCurrentUser } from '@vue-skuilder/common-ui';\n\nexport default defineComponent({\n name: 'NewCourseDialog',\n\n props: {\n name: {\n type: String,\n required: false,\n default: '',\n },\n },\n\n emits: ['CourseEditingComplete'],\n\n data() {\n return {\n mousetrap: new Mousetrap(this.$el),\n id: '',\n courseName: '',\n description: '',\n publicCourse: false,\n deleted: false,\n admins: [] as string[],\n moderators: [] as string[],\n dataShapes: [] as DataShape55[],\n questionTypes: [] as QuestionType55[],\n banner: undefined as Blob | undefined,\n thumb: undefined as Blob | undefined,\n updatePending: false,\n nameRules: [\n (value: string): string | boolean => {\n const max = 30;\n if (value.length > max) {\n return `Course name must be ${max} characters or less`;\n } else {\n return true;\n }\n },\n ],\n };\n },\n\n created() {\n this.mousetrap.bind('esc', this.clearFormAndDismiss);\n },\n\n methods: {\n async submit() {\n this.updatePending = true;\n\n const u = await getCurrentUser();\n\n const config: CourseConfig = {\n name: this.courseName,\n description: this.description,\n public: this.publicCourse,\n deleted: this.deleted,\n creator: u.getUsername(),\n admins: this.admins,\n moderators: this.moderators,\n dataShapes: this.dataShapes,\n questionTypes: this.questionTypes,\n };\n\n const result = await serverRequest<CreateCourse>({\n data: config,\n type: ServerRequestType.CREATE_COURSE,\n response: null,\n user: u.getUsername(),\n });\n\n if (result.response && result.response.ok) {\n alertUser({\n text: `Course ${this.courseName} created.`,\n status: result.response!.status,\n });\n this.clearFormAndDismiss();\n } else {\n alertUser({\n text: `Failed to create course ${this.courseName}.`,\n status: result.response!.status,\n });\n console.warn(`Resp: ${JSON.stringify(result.response)}`);\n }\n\n this.updatePending = false;\n },\n\n clearFormAndDismiss() {\n this.description = '';\n this.publicCourse = false;\n this.deleted = false;\n this.admins = [];\n this.moderators = [];\n\n this.$emit('CourseEditingComplete');\n },\n },\n});\n</script>\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import"./chunk-D6hFPZc3.js";import{H as e,T as t,_ as n,b as r}from"./vue.runtime.esm-bundler-C3q8JS9f.js";import{t as i}from"./index-jZbLHVSV.js";var a=t({name:`ReleaseNotes`});function _sfc_render(t,i,a,o,s,c){return e(),r(`div`,null,i[0]||(i[0]=[n(`h1`,{class:`text-h2 mb-6`},`Release Notes`,-1),n(`h3`,{class:`text-h4 mb-4`},`0.0.1 - April 21`,-1),n(`ul`,null,[n(`li`,null,`Hello world, these are release notes.`)],-1)]))}var o=i(a,[[`render`,_sfc_render]]);export{o as default};
|
|
2
|
+
//# sourceMappingURL=ReleaseNotes-BSxuZ5W7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReleaseNotes-BSxuZ5W7.js","names":[],"sources":["../../src/views/ReleaseNotes.vue","../../src/views/ReleaseNotes.vue"],"sourcesContent":["<template>\n <div>\n <h1 class=\"text-h2 mb-6\">Release Notes</h1>\n\n <h3 class=\"text-h4 mb-4\">0.0.1 - April 21</h3>\n <ul>\n <li>Hello world, these are release notes.</li>\n </ul>\n </div>\n</template>\n\n<script lang=\"ts\">\nimport { defineComponent } from 'vue';\n\nexport default defineComponent({\n name: 'ReleaseNotes',\n});\n</script>\n\n<style scoped></style>\n","<template>\n <div>\n <h1 class=\"text-h2 mb-6\">Release Notes</h1>\n\n <h3 class=\"text-h4 mb-4\">0.0.1 - April 21</h3>\n <ul>\n <li>Hello world, these are release notes.</li>\n </ul>\n </div>\n</template>\n\n<script lang=\"ts\">\nimport { defineComponent } from 'vue';\n\nexport default defineComponent({\n name: 'ReleaseNotes',\n});\n</script>\n\n<style scoped></style>\n"],"mappings":"mJCcA,IAAA,EAAe,EAAgB,CAC7B,KAAM,eACP,CAAC,8CAfA,EAOM,MAAA,KAAA,EAAA,KAAA,EAAA,GAAA,CANJ,EAA2C,KAAA,CAAvC,MAAM,eAAc,CAAC,gBAAa,GAAA,CAEtC,EAA8C,KAAA,CAA1C,MAAM,eAAc,CAAC,mBAAgB,GAAA,CACzC,EAEK,KAAA,KAAA,CADH,EAA8C,KAAA,KAA1C,wCAAqC,CAAA,CAAA,GAAA"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import"./chunk-D6hFPZc3.js";import{$ as e,C as t,H as n,K as r,T as i,v as a}from"./vue.runtime.esm-bundler-C3q8JS9f.js";import{d as o}from"./common-ui.es-mrcutgtG.js";import"./dist-CrcJCHFk.js";import"./MarkdownRenderer-DoVbFpA6-BIbDxYC4.js";import"./dist-DkeESCP4.js";import{t as s}from"./index-jZbLHVSV.js";var c=i({name:`RequestPasswordResetView`,components:{RequestPasswordReset:o},methods:{handleSuccess(e){console.log(`Password reset email sent to ${e}`)},goToLogin(){this.$router.push({name:`login`})}}});function _sfc_render(i,o,s,c,l,u){let d=r(`request-password-reset`,!0),f=r(`v-container`);return n(),a(f,{class:`d-flex align-center request-reset`},{default:e(()=>[t(d,{onCancel:i.goToLogin,onSuccess:i.handleSuccess},null,8,[`onCancel`,`onSuccess`])]),_:1})}var l=s(c,[[`render`,_sfc_render],[`__scopeId`,`data-v-111f280a`]]);export{l as default};
|
|
2
|
+
//# sourceMappingURL=RequestPasswordReset-GPKssrPH.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RequestPasswordReset-GPKssrPH.js","names":[],"sources":["../../src/views/RequestPasswordReset.vue","../../src/views/RequestPasswordReset.vue"],"sourcesContent":["<template>\n <v-container class=\"d-flex align-center request-reset\">\n <request-password-reset @cancel=\"goToLogin\" @success=\"handleSuccess\" />\n </v-container>\n</template>\n\n<script lang=\"ts\">\nimport { defineComponent } from 'vue';\nimport { RequestPasswordReset } from '@vue-skuilder/common-ui';\n\nexport default defineComponent({\n name: 'RequestPasswordResetView',\n\n components: {\n RequestPasswordReset,\n },\n\n methods: {\n handleSuccess(email: string) {\n console.log(`Password reset email sent to ${email}`);\n },\n\n goToLogin() {\n this.$router.push({ name: 'login' });\n },\n },\n});\n</script>\n\n<style lang=\"css\" scoped>\n.request-reset {\n max-width: 650px;\n}\n</style>\n","<template>\n <v-container class=\"d-flex align-center request-reset\">\n <request-password-reset @cancel=\"goToLogin\" @success=\"handleSuccess\" />\n </v-container>\n</template>\n\n<script lang=\"ts\">\nimport { defineComponent } from 'vue';\nimport { RequestPasswordReset } from '@vue-skuilder/common-ui';\n\nexport default defineComponent({\n name: 'RequestPasswordResetView',\n\n components: {\n RequestPasswordReset,\n },\n\n methods: {\n handleSuccess(email: string) {\n console.log(`Password reset email sent to ${email}`);\n },\n\n goToLogin() {\n this.$router.push({ name: 'login' });\n },\n },\n});\n</script>\n\n<style lang=\"css\" scoped>\n.request-reset {\n max-width: 650px;\n}\n</style>\n"],"mappings":"sTCUA,IAAA,EAAe,EAAgB,CAC7B,KAAM,2BAEN,WAAY,CACV,qBAAA,EACD,CAED,QAAS,CACP,cAAc,EAAe,CAC3B,QAAQ,IAAI,gCAAgC,IAAQ,EAGtD,WAAY,CACV,KAAK,QAAQ,KAAK,CAAE,KAAM,QAAS,CAAC,EAEvC,CACF,CAAC,sGAzBA,EAEc,EAAA,CAFD,MAAM,oCAAmC,CAAA,CADxD,QAAA,MAE2E,CAAvE,EAAuE,EAAA,CAA9C,SAAQ,EAAA,UAAY,UAAS,EAAA,kDAF1D,EAAA"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import"./chunk-D6hFPZc3.js";import{$ as e,C as t,H as n,K as r,S as i,T as a,v as o}from"./vue.runtime.esm-bundler-C3q8JS9f.js";import{s}from"./common-ui.es-mrcutgtG.js";import"./dist-CrcJCHFk.js";import"./MarkdownRenderer-DoVbFpA6-BIbDxYC4.js";import"./dist-DkeESCP4.js";import{t as c}from"./index-jZbLHVSV.js";var l=a({name:`ResetPasswordView`,components:{ResetPassword:s},methods:{handleComplete(){console.log(`Password reset successfully`)},handleError(e){console.error(`Password reset error:`,e)},goToLogin(){this.$router.push({name:`login`})}}});function _sfc_render(a,s,c,l,u,d){let f=r(`v-btn`),p=r(`reset-password`,!0);return n(),o(p,{onComplete:a.handleComplete,onError:a.handleError},{"success-action":e(()=>[t(f,{color:`primary`,onClick:a.goToLogin},{default:e(()=>s[0]||(s[0]=[i(` Continue to Login `)])),_:1},8,[`onClick`])]),_:1},8,[`onComplete`,`onError`])}var u=c(l,[[`render`,_sfc_render]]);export{u as default};
|
|
2
|
+
//# sourceMappingURL=ResetPassword-By7RvJkB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ResetPassword-By7RvJkB.js","names":[],"sources":["../../src/views/ResetPassword.vue","../../src/views/ResetPassword.vue"],"sourcesContent":["<template>\n <reset-password @complete=\"handleComplete\" @error=\"handleError\">\n <template #success-action>\n <v-btn color=\"primary\" @click=\"goToLogin\">\n Continue to Login\n </v-btn>\n </template>\n </reset-password>\n</template>\n\n<script lang=\"ts\">\nimport { defineComponent } from 'vue';\nimport { ResetPassword } from '@vue-skuilder/common-ui';\n\nexport default defineComponent({\n name: 'ResetPasswordView',\n\n components: {\n ResetPassword,\n },\n\n methods: {\n handleComplete() {\n console.log('Password reset successfully');\n },\n\n handleError(error: string) {\n console.error('Password reset error:', error);\n },\n\n goToLogin() {\n this.$router.push({ name: 'login' });\n },\n },\n});\n</script>\n","<template>\n <reset-password @complete=\"handleComplete\" @error=\"handleError\">\n <template #success-action>\n <v-btn color=\"primary\" @click=\"goToLogin\">\n Continue to Login\n </v-btn>\n </template>\n </reset-password>\n</template>\n\n<script lang=\"ts\">\nimport { defineComponent } from 'vue';\nimport { ResetPassword } from '@vue-skuilder/common-ui';\n\nexport default defineComponent({\n name: 'ResetPasswordView',\n\n components: {\n ResetPassword,\n },\n\n methods: {\n handleComplete() {\n console.log('Password reset successfully');\n },\n\n handleError(error: string) {\n console.error('Password reset error:', error);\n },\n\n goToLogin() {\n this.$router.push({ name: 'login' });\n },\n },\n});\n</script>\n"],"mappings":"wTCcA,IAAA,EAAe,EAAgB,CAC7B,KAAM,oBAEN,WAAY,CACV,cAAA,EACD,CAED,QAAS,CACP,gBAAiB,CACf,QAAQ,IAAI,8BAA8B,EAG5C,YAAY,EAAe,CACzB,QAAQ,MAAM,wBAAyB,EAAM,EAG/C,WAAY,CACV,KAAK,QAAQ,KAAK,CAAE,KAAM,QAAS,CAAC,EAEvC,CACF,CAAC,wFAjCA,EAMiB,EAAA,CANA,WAAU,EAAA,eAAiB,QAAO,EAAA,cACtC,iBAAc,MAGf,CAFR,EAEQ,EAAA,CAFD,MAAM,UAAW,QAAO,EAAA,YAHrC,QAAA,MAKM,EAAA,KAAA,EAAA,GAAA,CALN,EAGgD,sBAE1C,CAAA,EAAA,CALN,EAAA,oBAAA,EAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.session-layout[data-v-c5ff4b5f]{flex-direction:column;display:flex}.fixed-controls-container[data-v-c5ff4b5f]{width:100%;margin-bottom:20px}.fixed-controls[data-v-c5ff4b5f]{flex-direction:column;display:flex}.time-limit-field[data-v-c5ff4b5f]{width:100%;margin-bottom:16px}.start-btn[data-v-c5ff4b5f]{max-height:150px;margin-top:8px}.course-selection-container[data-v-c5ff4b5f]{width:100%}.course-row[data-v-c5ff4b5f]{border-bottom:1px solid rgba(var(--v-border-color), .12);padding:8px 0}.course-row-header[data-v-c5ff4b5f]{justify-content:space-between;align-items:center;gap:16px;display:flex}.course-row-actions[data-v-c5ff4b5f]{align-items:center;gap:12px;display:flex}.reviews-count[data-v-c5ff4b5f]{text-align:center;min-width:32px;color:rgba(var(--v-theme-on-surface), .6)}.tag-filter-container[data-v-c5ff4b5f]{background-color:rgba(var(--v-theme-surface-variant), .3);border-radius:4px;margin-top:8px;padding:12px 0 12px 32px}.select-all-row[data-v-c5ff4b5f]{border-top:2px solid rgba(var(--v-border-color), .24);padding-top:8px}.filter-summary[data-v-c5ff4b5f]{background-color:rgba(var(--v-theme-primary), .08);border-radius:4px;align-items:center;padding:8px 12px;display:flex}@media (min-width:960px){.session-layout[data-v-c5ff4b5f]{flex-direction:row;gap:40px}.fixed-controls-container[data-v-c5ff4b5f]{flex-shrink:0;width:300px}.fixed-controls[data-v-c5ff4b5f]{padding-left:20px;position:sticky;top:20px}.course-selection-container[data-v-c5ff4b5f]{border-right:1px solid #0000001f;flex-grow:1;padding-right:20px}}.hidden-session[data-v-dcbc1fde]{visibility:hidden;z-index:-1;position:absolute}.session-error[data-v-dcbc1fde]{color:var(--v-error-base)}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{i as e}from"./chunk-D6hFPZc3.js";import{$ as t,C as n,H as r,It as i,K as a,S as o,T as s,Vt as c,W as l,_ as u,b as d,f,v as p,y as m}from"./vue.runtime.esm-bundler-C3q8JS9f.js";import{I as h,M as g,T as _,o as v,z as y}from"./common-ui.es-mrcutgtG.js";import{o as b}from"./dist-CrcJCHFk.js";import{n as x}from"./MarkdownRenderer-DoVbFpA6-BIbDxYC4.js";import{k as S}from"./dist-DkeESCP4.js";import{t as C}from"./index-jZbLHVSV.js";import{g as w}from"./dist-BlP52QF6.js";import{i as T}from"./edit-ui.es-CLyYUxsi.js";var E=s({name:`SessionConfiguration`,components:{SkMouseTrapToolTip:v,CourseTagFilterWidget:y},props:{initialTimeLimit:{type:Number,required:!0,default:5}},emits:[`initStudySession`],data(){return{registeredHotkeys:[],allSelected:!0,activeCourses:[],activeClasses:[],hasRegistrations:!0,user:null,timeLimit:this.initialTimeLimit,courseLoadError:!1,classroomLoadError:!1,expandedFilters:{}}},computed:{hasSelectedSources(){return this.activeCourses.some(e=>e.selected)||this.activeClasses.some(e=>e.selected)},hasAnyActiveFilter(){return this.activeCourses.some(e=>e.selected&&this.hasActiveTagFilter(e))},activeFilterCount(){return this.activeCourses.filter(e=>e.selected&&this.hasActiveTagFilter(e)).length}},watch:{timeLimit:{handler(){this.timeLimit<=0&&(this.timeLimit=1)}}},beforeUnmount(){this.registeredHotkeys&&this.registeredHotkeys.forEach(e=>{x.removeBinding(e)})},async created(){this.user=await _(),this.timeLimit=this.initialTimeLimit,this.setHotkeys();let[e,t]=await Promise.allSettled([this.getActiveCourses(),this.getActiveClassrooms()]);e.status===`rejected`&&(console.error(`Failed to load courses:`,e.reason),this.courseLoadError=!0),t.status===`rejected`&&(console.error(`Failed to load classrooms:`,t.reason),this.classroomLoadError=!0),this.activeCourses.length===0&&this.activeClasses.length===0&&!this.courseLoadError&&!this.classroomLoadError&&(this.hasRegistrations=!1)},mounted(){document.getElementById(`SelectAll`)?.focus()},unmounted(){this.registeredHotkeys&&this.registeredHotkeys.forEach(e=>{x.removeBinding(e)})},methods:{inc(){this.timeLimit+=1,console.log(`inc to ${this.timeLimit}`)},dec(){this.timeLimit--,console.log(`dec to ${this.timeLimit}`)},update(){console.log(JSON.stringify(this.activeCourses)),console.log(JSON.stringify(this.activeClasses))},toggleAll(){console.log(`Toggling all courses`),this.activeCourses.forEach(e=>{e.selected=this.allSelected}),this.activeClasses.forEach(e=>{e.selected=this.allSelected}),console.log(JSON.stringify(this.activeCourses))},toggleTagFilter(e){this.expandedFilters[e]=!this.expandedFilters[e]},hasActiveTagFilter(e){return b(e.tagFilter)},startSession(){this.registeredHotkeys&&this.registeredHotkeys.forEach(e=>{x.removeBinding(e)});let e=this.activeCourses.filter(e=>e.selected).map(e=>{let t={type:`course`,id:e.courseID};return b(e.tagFilter)&&(t.tagFilter=e.tagFilter),t}),t=this.activeClasses.filter(e=>e.selected).map(e=>({type:`classroom`,id:e.classID})),n=[...e,...t];this.$emit(`initStudySession`,n,this.timeLimit)},async getActiveClassrooms(){let e=await(await _()).getActiveClasses(),t=[];console.log(`Active classes: ${JSON.stringify(e)}`),await Promise.all(e.map(e=>(async e=>{let n=await S().getClassroomDB(e,`student`);t.push({classID:e,name:n.getConfig().name,selected:!0,reviews:0})})(e))),this.activeClasses=t},async getActiveCourses(){this.activeCourses=(await this.user.getActiveCourses()).map(e=>({...e,selected:!0,name:``,reviews:0,tagFilter:void 0})),Promise.all(this.activeCourses.map(async(e,t)=>{let n=await S().getCoursesDB().getCourseConfig(e.courseID),r=await this.user?.getCourseInterface(e.courseID);return(async()=>Promise.all([this.activeCourses[t].name=n.name,this.activeCourses[t].reviews=await r.getScheduledReviewCount()]))()}))},setHotkeys(){let e=[{hotkey:`right`,callback:()=>{this.timeLimit++},command:`Increase time limit`},{hotkey:`left`,callback:()=>{this.timeLimit--},command:`Decrease time limit`}];x.addBinding(e),this.registeredHotkeys=e.map(e=>e.hotkey)}}}),D={key:0},O={class:`session-layout`},k={class:`course-selection-container`},A={class:`course-row-header`},j={class:`course-row-header`},M={class:`course-row-actions`},N={class:`reviews-count`},P={key:0,class:`tag-filter-container`},F={class:`select-all-row mt-3`},I={class:`fixed-controls-container`},L={class:`fixed-controls`},R={class:`mb-5`},z={key:0,class:`filter-summary mb-4`},B={class:`text-caption`},V={key:1,class:`text-h4`},H={key:2,class:`text-h4`};function _sfc_render$1(e,i,s,h,g,_){let v=a(`v-alert`),y=a(`v-checkbox`),b=a(`v-icon`),x=a(`v-btn`),S=a(`CourseTagFilterWidget`),C=a(`v-expand-transition`),w=a(`v-text-field`),T=a(`SkMouseTrapToolTip`),E=a(`router-link`);return e.hasRegistrations||e.courseLoadError||e.classroomLoadError?(r(),d(`div`,D,[i[11]||(i[11]=u(`div`,{"data-cy":`select-quilts-header`,class:`text-h4 mb-4`},`Study Session Setup`,-1)),e.courseLoadError?(r(),p(v,{key:0,type:`warning`,class:`mb-4`},{default:t(()=>i[2]||(i[2]=[o(` Unable to load course data. Some features may be unavailable. `)])),_:1})):m(``,!0),e.classroomLoadError?(r(),p(v,{key:1,type:`warning`,class:`mb-4`},{default:t(()=>i[3]||(i[3]=[o(` Unable to load classroom data. You can still study individual courses. `)])),_:1})):m(``,!0),u(`div`,O,[u(`div`,k,[i[6]||(i[6]=u(`div`,{class:`text-h6 mb-3`},`Select Quilts to Study`,-1)),(r(!0),d(f,null,l(e.activeClasses,t=>(r(),d(`div`,{key:t.classID,class:`course-row`},[u(`div`,A,[n(y,{modelValue:t.selected,"onUpdate:modelValue":e=>t.selected=e,label:`Class: ${t.name}`,"hide-details":``,onClickCapture:e.update},null,8,[`modelValue`,`onUpdate:modelValue`,`label`,`onClickCapture`]),i[4]||(i[4]=u(`span`,{class:`reviews-count`},`-`,-1))])]))),128)),(r(!0),d(f,null,l(e.activeCourses,a=>(r(),d(`div`,{key:a.courseID,class:`course-row`},[u(`div`,j,[n(y,{modelValue:a.selected,"onUpdate:modelValue":e=>a.selected=e,"data-cy":`course-checkbox`,label:`q/${a.name}`,"hide-details":``,onClickCapture:e.update},null,8,[`modelValue`,`onUpdate:modelValue`,`label`,`onClickCapture`]),u(`div`,M,[u(`span`,N,c(a.reviews),1),a.selected?(r(),p(x,{key:0,variant:`text`,size:`small`,color:e.hasActiveTagFilter(a)?`primary`:`default`,onClick:t=>e.toggleTagFilter(a.courseID)},{default:t(()=>[n(b,{start:``,size:`small`},{default:t(()=>i[5]||(i[5]=[o(`mdi-filter-variant`)])),_:1}),o(` `+c(e.hasActiveTagFilter(a)?`Filtered`:`Filter`)+` `,1),n(b,{end:``,size:`small`},{default:t(()=>[o(c(e.expandedFilters[a.courseID]?`mdi-chevron-up`:`mdi-chevron-down`),1)]),_:2},1024)]),_:2},1032,[`color`,`onClick`])):m(``,!0)])]),n(C,null,{default:t(()=>[a.selected&&e.expandedFilters[a.courseID]?(r(),d(`div`,P,[n(S,{modelValue:a.tagFilter,"onUpdate:modelValue":e=>a.tagFilter=e,"course-id":a.courseID},null,8,[`modelValue`,`onUpdate:modelValue`,`course-id`])])):m(``,!0)]),_:2},1024)]))),128)),u(`div`,F,[n(y,{id:`SelectAll`,ref:`selectAll`,modelValue:e.allSelected,"onUpdate:modelValue":[i[0]||(i[0]=t=>e.allSelected=t),e.toggleAll],autofocus:``,label:`Select All`,"hide-details":``},null,8,[`modelValue`,`onUpdate:modelValue`])])]),u(`div`,I,[u(`div`,L,[i[10]||(i[10]=u(`div`,{class:`text-h6 mb-3`},`Session Settings`,-1)),u(`div`,R,[n(w,{ref:`numberField`,modelValue:e.timeLimit,"onUpdate:modelValue":i[1]||(i[1]=t=>e.timeLimit=t),class:`time-limit-field`,variant:`outlined`,label:`Study Session Timelimit`,"prepend-inner-icon":`mdi-clock-outline`,"prepend-icon":`mdi-minus`,"append-icon":`mdi-plus`,suffix:e.timeLimit>1?`minutes`:`minute`,mask:`##`,type:`number`,"onClick:prepend":e.dec,"onClick:append":e.inc},null,8,[`modelValue`,`suffix`,`onClick:prepend`,`onClick:append`])]),e.hasAnyActiveFilter?(r(),d(`div`,z,[n(b,{size:`small`,color:`primary`,class:`mr-1`},{default:t(()=>i[7]||(i[7]=[o(`mdi-filter`)])),_:1}),u(`span`,B,c(e.activeFilterCount)+` course`+c(e.activeFilterCount>1?`s`:``)+` with tag filters `,1)])):m(``,!0),n(T,{hotkey:`enter`,command:`Start Session`,disabled:!e.hasSelectedSources,"highlight-effect":`scale`},{default:t(()=>[n(x,{"data-cy":`start-studying-button`,color:`success`,size:`large`,block:``,class:`start-btn`,disabled:!e.hasSelectedSources,onClick:e.startSession},{default:t(()=>[n(b,{start:``},{default:t(()=>i[8]||(i[8]=[o(`mdi-play`)])),_:1}),i[9]||(i[9]=o(` Start! `))]),_:1},8,[`disabled`,`onClick`])]),_:1},8,[`disabled`])])])])])):!e.courseLoadError&&!e.classroomLoadError?(r(),d(`div`,V,[i[15]||(i[15]=u(`p`,null,`You don't have anything to study!`,-1)),u(`p`,null,[i[13]||(i[13]=o(`Head over to the `)),n(E,{to:`/quilts`},{default:t(()=>i[12]||(i[12]=[o(`Quilts`)])),_:1}),i[14]||(i[14]=o(` page to find something for you.`))])])):(r(),d(`div`,H,i[16]||(i[16]=[u(`p`,null,`Unable to load study data due to technical issues.`,-1),u(`p`,null,`Please try refreshing the page or contact support if the problem persists.`,-1)])))}e(_sfc_render$1,`_sfc_render`);var U=C(E,[[`render`,_sfc_render$1],[`__scopeId`,`data-v-c5ff4b5f`]]);function randomInt(e,t){return Math.floor(Math.random()*(t-e+1))+e}var W=s({name:`StudyView`,components:{SessionConfiguration:U,StudySession:g},inject:{router:{from:`router`}},props:{previewCourseID:{type:String,required:!1,default:``},randomPreview:{type:Boolean,required:!1},focusCourseID:{type:String,required:!1,default:``}},data(){return{user:null,studySessionConfig:void 0,previewCourseConfig:void 0,previewMode:!1,sessionTimeLimit:5,inSession:!1,sessionPrepared:!1,sessionError:!1,errorMessage:``,sessionContentSources:[],dataInputFormStore:T(),getViewComponent:e=>w.getView(e),dataLayer:S()}},async created(){this.user=await _(),this.studySessionConfig={likesConfetti:h().config.likesConfetti};let e=``;if(this.randomPreview){let t=await this.user.getCourseRegistrationsDoc(),n=(await S().getCoursesDB().getCourseList()).map(e=>e.courseID),r=n.filter(e=>!t.courses.some(t=>t.courseID===e));e=r.length>0?r[randomInt(0,r.length)]:n[randomInt(0,n.length)]}if(this.previewCourseID&&(this.previewMode=!0,S().getCoursesDB().getCourseList().then(e=>{e.forEach(e=>{e.courseID===this.previewCourseID&&(this.previewCourseConfig=e,this.previewCourseConfig.courseID=e.courseID)})}),console.log(`[Study] COURSE PREVIEW MODE FOR ${this.previewCourseID}`),await this.user.registerForCourse(this.previewCourseID,!0),e=this.previewCourseID),this.focusCourseID&&(console.log(`[Study] FOCUS study session: ${this.focusCourseID}`),e=this.focusCourseID),e){let t=this.parseTagFilterFromQuery(),n={type:`course`,id:e};b(t)&&(n.tagFilter=t,console.log(`[Study] Tag filter from query params:`,t)),this.initStudySession([n],this.sessionTimeLimit)}},methods:{refreshRoute(){this.router.go(0)},async initStudySession(e,t){console.log(`[Study] starting study session w/ sources: ${JSON.stringify(e)}`),this.sessionContentSources=e,this.sessionTimeLimit=t,this.inSession=!0,this.sessionPrepared=!1,console.log(`[Study] Waiting for session-prepared event from StudySession component`)},registerUserForPreviewCourse(){this.user.registerForCourse(this.previewCourseConfig.courseID).then(()=>this.router.push(`/quilts/${this.previewCourseConfig.courseID}`))},handleSessionFinished(){this.refreshRoute()},handleSessionPrepared(){console.log(`[Study] Session preparation complete - received session-prepared event`),this.sessionPrepared=!0,this.sessionError=!1,this.errorMessage=``},handleSessionError({message:e,error:t}){console.error(`[Study] Session error:`,e,t),this.sessionError=!0,this.errorMessage=e||`An error occurred while preparing your study session.`,this.sessionPrepared=!1},parseTagFilterFromQuery(){let e=this.$route.query.include,t=this.$route.query.exclude,parseParam=e=>e?Array.isArray(e)?e.flatMap(e=>e?e.split(`,`).map(e=>e.trim()):[]).filter(Boolean):e.split(`,`).map(e=>e.trim()).filter(Boolean):[];return{include:parseParam(e),exclude:parseParam(t)}}}}),G={key:0},K={key:1},q={key:0,align:`center`},J={class:`text-h5`},Y={key:1},X={key:2},Z={key:0,class:`session-loading`},Q={key:1,class:`session-error`},$={class:`text-subtitle-1 mt-2`};function _sfc_render(e,s,l,f,p,h){let g=a(`SessionConfiguration`),_=a(`v-btn`),v=a(`router-link`),y=a(`v-spacer`),b=a(`v-col`),x=a(`v-row`),S=a(`v-progress-circular`),C=a(`v-container`),w=a(`v-icon`),T=a(`StudySession`);return e.inSession?(r(),d(`div`,K,[e.previewMode&&e.previewCourseConfig?(r(),d(`div`,q,[n(x,null,{default:t(()=>[n(b,null,{default:t(()=>[u(`span`,J,[s[1]||(s[1]=o(` Quilt preview for `)),u(`em`,null,c(e.previewCourseConfig.name),1)]),n(_,{size:`small`,color:`primary`,onClick:e.registerUserForPreviewCourse},{default:t(()=>s[2]||(s[2]=[o(`Join`)])),_:1},8,[`onClick`]),n(v,{to:`/quilts/${e.previewCourseConfig.courseID}`},{default:t(()=>[n(_,{size:`small`,color:`secondary`},{default:t(()=>s[3]||(s[3]=[o(`More info`)])),_:1})]),_:1},8,[`to`]),n(y)]),_:1})]),_:1})])):e.previewMode?(r(),d(`div`,Y,[n(x,null,{default:t(()=>[n(b,null,{default:t(()=>s[4]||(s[4]=[u(`span`,{class:`text-h5`},`... No course was specified for the preview.`,-1),u(`div`,null,`(this shouldn't happen)...`,-1)])),_:1})]),_:1})])):m(``,!0),e.inSession?(r(),d(`div`,X,[!e.sessionPrepared&&!e.sessionError?(r(),d(`div`,Z,[n(C,{class:`text-center`},{default:t(()=>[n(x,{justify:`center`,align:`center`,style:{"min-height":`50vh`}},{default:t(()=>[n(b,{cols:`12`},{default:t(()=>[n(S,{size:`70`,width:`7`,color:`primary`,indeterminate:``}),s[5]||(s[5]=u(`div`,{class:`text-h5 mt-4`},`Preparing your study session...`,-1)),s[6]||(s[6]=u(`div`,{class:`text-subtitle-1 mt-2`},`Getting your learning materials ready`,-1))]),_:1})]),_:1})]),_:1})])):m(``,!0),e.sessionError?(r(),d(`div`,Q,[n(C,{class:`text-center`},{default:t(()=>[n(x,{justify:`center`,align:`center`,style:{"min-height":`50vh`}},{default:t(()=>[n(b,{cols:`12`},{default:t(()=>[n(w,{size:`64`,color:`error`},{default:t(()=>s[7]||(s[7]=[o(`mdi-alert-circle`)])),_:1}),s[9]||(s[9]=u(`div`,{class:`text-h5 mt-4 text-error`},`Session Preparation Failed`,-1)),u(`div`,$,c(e.errorMessage||`There was a problem preparing your study session.`),1),n(_,{color:`primary`,class:`mt-6`,onClick:e.refreshRoute},{default:t(()=>s[8]||(s[8]=[o(`Try Again`)])),_:1},8,[`onClick`])]),_:1})]),_:1})]),_:1})])):m(``,!0),n(T,{"content-sources":e.sessionContentSources,"session-time-limit":e.sessionTimeLimit,user:e.user,"session-config":e.studySessionConfig,"data-layer":e.dataLayer,"get-view-component":e.getViewComponent,class:i({"hidden-session":!e.sessionPrepared}),onSessionFinished:e.handleSessionFinished,onSessionPrepared:e.handleSessionPrepared,onSessionError:e.handleSessionError},null,8,[`content-sources`,`session-time-limit`,`user`,`session-config`,`data-layer`,`get-view-component`,`class`,`onSessionFinished`,`onSessionPrepared`,`onSessionError`])])):m(``,!0)])):(r(),d(`div`,G,[n(g,{"initial-time-limit":e.sessionTimeLimit,onInitStudySession:s[0]||(s[0]=(t,n)=>e.initStudySession(t,n))},null,8,[`initial-time-limit`])]))}var ee=C(W,[[`render`,_sfc_render],[`__scopeId`,`data-v-dcbc1fde`]]);export{ee as default};
|
|
2
|
+
//# sourceMappingURL=Study-yDCrOQ1i.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Study-yDCrOQ1i.js","names":[],"sources":["../../src/components/Study/SessionConfiguration.vue","../../src/components/Study/SessionConfiguration.vue","../../src/views/Study.vue","../../src/views/Study.vue"],"sourcesContent":["<template>\n <div v-if=\"hasRegistrations || courseLoadError || classroomLoadError\">\n <div data-cy=\"select-quilts-header\" class=\"text-h4 mb-4\">Study Session Setup</div>\n\n <!-- Error Messages -->\n <v-alert v-if=\"courseLoadError\" type=\"warning\" class=\"mb-4\">\n Unable to load course data. Some features may be unavailable.\n </v-alert>\n\n <v-alert v-if=\"classroomLoadError\" type=\"warning\" class=\"mb-4\">\n Unable to load classroom data. You can still study individual courses.\n </v-alert>\n\n <div class=\"session-layout\">\n <!-- Left Column: Course Selection -->\n <div class=\"course-selection-container\">\n <div class=\"text-h6 mb-3\">Select Quilts to Study</div>\n\n <!-- Classrooms -->\n <div v-for=\"classroom in activeClasses\" :key=\"classroom.classID\" class=\"course-row\">\n <div class=\"course-row-header\">\n <v-checkbox\n v-model=\"classroom.selected\"\n :label=\"`Class: ${classroom.name}`\"\n hide-details\n @click.capture=\"update\"\n />\n <span class=\"reviews-count\">-</span>\n </div>\n </div>\n\n <!-- Courses -->\n <div v-for=\"course in activeCourses\" :key=\"course.courseID\" class=\"course-row\">\n <div class=\"course-row-header\">\n <v-checkbox\n v-model=\"course.selected\"\n data-cy=\"course-checkbox\"\n :label=\"`q/${course.name}`\"\n hide-details\n @click.capture=\"update\"\n />\n <div class=\"course-row-actions\">\n <span class=\"reviews-count\">{{ course.reviews }}</span>\n <v-btn\n v-if=\"course.selected\"\n variant=\"text\"\n size=\"small\"\n :color=\"hasActiveTagFilter(course) ? 'primary' : 'default'\"\n @click=\"toggleTagFilter(course.courseID)\"\n >\n <v-icon start size=\"small\">mdi-filter-variant</v-icon>\n {{ hasActiveTagFilter(course) ? 'Filtered' : 'Filter' }}\n <v-icon end size=\"small\">\n {{ expandedFilters[course.courseID] ? 'mdi-chevron-up' : 'mdi-chevron-down' }}\n </v-icon>\n </v-btn>\n </div>\n </div>\n\n <!-- Tag Filter Widget (expandable) -->\n <v-expand-transition>\n <div v-if=\"course.selected && expandedFilters[course.courseID]\" class=\"tag-filter-container\">\n <CourseTagFilterWidget v-model=\"course.tagFilter\" :course-id=\"course.courseID\" />\n </div>\n </v-expand-transition>\n </div>\n\n <!-- Select All -->\n <div class=\"select-all-row mt-3\">\n <v-checkbox\n id=\"SelectAll\"\n ref=\"selectAll\"\n v-model=\"allSelected\"\n autofocus\n label=\"Select All\"\n hide-details\n @update:model-value=\"toggleAll\"\n ></v-checkbox>\n </div>\n </div>\n\n <!-- Right Column: Time Configuration and Start Button -->\n <div class=\"fixed-controls-container\">\n <div class=\"fixed-controls\">\n <div class=\"text-h6 mb-3\">Session Settings</div>\n\n <div class=\"mb-5\">\n <v-text-field\n ref=\"numberField\"\n v-model=\"timeLimit\"\n class=\"time-limit-field\"\n variant=\"outlined\"\n label=\"Study Session Timelimit\"\n prepend-inner-icon=\"mdi-clock-outline\"\n prepend-icon=\"mdi-minus\"\n append-icon=\"mdi-plus\"\n :suffix=\"timeLimit > 1 ? 'minutes' : 'minute'\"\n mask=\"##\"\n type=\"number\"\n @click:prepend=\"dec\"\n @click:append=\"inc\"\n />\n </div>\n\n <!-- Filter Summary -->\n <div v-if=\"hasAnyActiveFilter\" class=\"filter-summary mb-4\">\n <v-icon size=\"small\" color=\"primary\" class=\"mr-1\">mdi-filter</v-icon>\n <span class=\"text-caption\">\n {{ activeFilterCount }} course{{ activeFilterCount > 1 ? 's' : '' }} with tag filters\n </span>\n </div>\n\n <SkMouseTrapToolTip\n hotkey=\"enter\"\n command=\"Start Session\"\n :disabled=\"!hasSelectedSources\"\n highlight-effect=\"scale\"\n >\n <v-btn\n data-cy=\"start-studying-button\"\n color=\"success\"\n size=\"large\"\n block\n class=\"start-btn\"\n :disabled=\"!hasSelectedSources\"\n @click=\"startSession\"\n >\n <v-icon start>mdi-play</v-icon>\n Start!\n </v-btn>\n </SkMouseTrapToolTip>\n </div>\n </div>\n </div>\n </div>\n <div v-else-if=\"!courseLoadError && !classroomLoadError\" class=\"text-h4\">\n <p>You don't have anything to study!</p>\n <p>Head over to the <router-link to=\"/quilts\">Quilts</router-link> page to find something for you.</p>\n </div>\n <div v-else class=\"text-h4\">\n <p>Unable to load study data due to technical issues.</p>\n <p>Please try refreshing the page or contact support if the problem persists.</p>\n </div>\n</template>\n\n<script lang=\"ts\">\nimport { defineComponent } from 'vue';\nimport { SkldrMouseTrap, getCurrentUser, SkMouseTrapToolTip, CourseTagFilterWidget } from '@vue-skuilder/common-ui';\nimport { CourseRegistration, UserDBInterface, getDataLayer, ContentSourceID } from '@vue-skuilder/db';\nimport { TagFilter, hasActiveFilter } from '@vue-skuilder/common';\n\nexport interface SessionConfigMetaData {\n selected: boolean;\n name: string;\n reviews: number;\n tagFilter?: TagFilter;\n}\n\n// Extended ContentSourceID that can carry tag filter configuration\nexport interface TagFilteredContentSourceID extends ContentSourceID {\n tagFilter?: TagFilter;\n}\n\nexport default defineComponent({\n name: 'SessionConfiguration',\n\n components: {\n SkMouseTrapToolTip,\n CourseTagFilterWidget,\n },\n\n props: {\n initialTimeLimit: {\n type: Number,\n required: true,\n default: 5,\n },\n },\n\n emits: ['initStudySession'],\n\n data() {\n return {\n registeredHotkeys: [] as (string | string[])[],\n allSelected: true,\n activeCourses: [] as (CourseRegistration & SessionConfigMetaData)[],\n activeClasses: [] as ({ classID: string } & SessionConfigMetaData)[],\n hasRegistrations: true,\n user: null as UserDBInterface | null,\n timeLimit: this.initialTimeLimit,\n courseLoadError: false,\n classroomLoadError: false,\n expandedFilters: {} as Record<string, boolean>,\n };\n },\n\n computed: {\n hasSelectedSources(): boolean {\n return this.activeCourses.some((c) => c.selected) || this.activeClasses.some((c) => c.selected);\n },\n\n hasAnyActiveFilter(): boolean {\n return this.activeCourses.some((c) => c.selected && this.hasActiveTagFilter(c));\n },\n\n activeFilterCount(): number {\n return this.activeCourses.filter((c) => c.selected && this.hasActiveTagFilter(c)).length;\n },\n },\n\n watch: {\n timeLimit: {\n handler() {\n if (this.timeLimit <= 0) {\n this.timeLimit = 1;\n }\n },\n },\n },\n\n beforeUnmount() {\n // Clean up registered hotkeys when component unmounts\n if (this.registeredHotkeys) {\n this.registeredHotkeys.forEach((key) => {\n SkldrMouseTrap.removeBinding(key);\n });\n }\n },\n\n async created() {\n this.user = await getCurrentUser();\n this.timeLimit = this.initialTimeLimit;\n\n this.setHotkeys();\n const [coursesResult, classroomsResult] = await Promise.allSettled([\n this.getActiveCourses(),\n this.getActiveClassrooms(),\n ]);\n\n // Handle course loading failure\n if (coursesResult.status === 'rejected') {\n console.error('Failed to load courses:', coursesResult.reason);\n this.courseLoadError = true;\n }\n\n // Handle classroom loading failure\n if (classroomsResult.status === 'rejected') {\n console.error('Failed to load classrooms:', classroomsResult.reason);\n this.classroomLoadError = true;\n }\n\n if (\n this.activeCourses.length === 0 &&\n this.activeClasses.length === 0 &&\n !this.courseLoadError &&\n !this.classroomLoadError\n ) {\n this.hasRegistrations = false;\n }\n },\n\n mounted() {\n document.getElementById('SelectAll')?.focus();\n },\n\n unmounted() {\n // Clean up registered hotkeys when component unmounts\n if (this.registeredHotkeys) {\n this.registeredHotkeys.forEach((key) => {\n SkldrMouseTrap.removeBinding(key);\n });\n }\n },\n\n methods: {\n inc() {\n this.timeLimit = this.timeLimit + 1;\n console.log(`inc to ${this.timeLimit}`);\n },\n\n dec() {\n this.timeLimit--;\n console.log(`dec to ${this.timeLimit}`);\n },\n\n update() {\n console.log(JSON.stringify(this.activeCourses));\n console.log(JSON.stringify(this.activeClasses));\n },\n\n toggleAll(): void {\n console.log(`Toggling all courses`);\n this.activeCourses.forEach((crs) => {\n crs.selected = this.allSelected;\n });\n this.activeClasses.forEach((cl) => {\n cl.selected = this.allSelected;\n });\n console.log(JSON.stringify(this.activeCourses));\n },\n\n toggleTagFilter(courseID: string): void {\n this.expandedFilters[courseID] = !this.expandedFilters[courseID];\n },\n\n hasActiveTagFilter(course: CourseRegistration & SessionConfigMetaData): boolean {\n return hasActiveFilter(course.tagFilter);\n },\n\n startSession() {\n // Clean up any registered hotkeys before starting session\n if (this.registeredHotkeys) {\n this.registeredHotkeys.forEach((key) => {\n SkldrMouseTrap.removeBinding(key);\n });\n }\n\n // Build sources with optional tag filters\n const selectedCourses: TagFilteredContentSourceID[] = this.activeCourses\n .filter((c) => c.selected)\n .map((c) => {\n const source: TagFilteredContentSourceID = {\n type: 'course',\n id: c.courseID,\n };\n // Only include tagFilter if it has active constraints\n if (hasActiveFilter(c.tagFilter)) {\n source.tagFilter = c.tagFilter;\n }\n return source;\n });\n\n const selectedClassrooms: ContentSourceID[] = this.activeClasses\n .filter((cl) => cl.selected)\n .map((cl) => ({\n type: 'classroom',\n id: cl.classID,\n }));\n\n const allSelectedSources = [...selectedCourses, ...selectedClassrooms];\n this.$emit('initStudySession', allSelectedSources, this.timeLimit);\n },\n\n async getActiveClassrooms() {\n const classes = await (await getCurrentUser()).getActiveClasses();\n const activeClasses: ({ classID: string } & SessionConfigMetaData)[] = [];\n\n console.log(`Active classes: ${JSON.stringify(classes)}`);\n\n await Promise.all(\n classes.map((c) =>\n (async (classID: string) => {\n const classDb = await getDataLayer().getClassroomDB(classID, `student`);\n activeClasses.push({\n classID,\n name: classDb.getConfig().name,\n selected: true,\n reviews: 0,\n });\n })(c)\n )\n );\n this.activeClasses = activeClasses;\n },\n\n async getActiveCourses() {\n this.activeCourses = (await this.user!.getActiveCourses()).map((c) => ({\n ...c,\n selected: true,\n name: '',\n reviews: 0,\n tagFilter: undefined,\n }));\n\n Promise.all(\n this.activeCourses.map(async (c, i) => {\n const cfg = await getDataLayer().getCoursesDB().getCourseConfig(c.courseID);\n const crsInterface = await this.user?.getCourseInterface(c.courseID);\n return (async () => {\n return Promise.all([\n (this.activeCourses[i].name = cfg.name),\n (this.activeCourses[i].reviews = await crsInterface!.getScheduledReviewCount()),\n ]);\n })();\n })\n );\n },\n\n setHotkeys() {\n const hotkeys = [\n {\n hotkey: 'right',\n callback: () => {\n this.timeLimit++;\n },\n command: 'Increase time limit',\n },\n {\n hotkey: 'left',\n callback: () => {\n this.timeLimit--;\n },\n command: 'Decrease time limit',\n },\n ];\n SkldrMouseTrap.addBinding(hotkeys);\n this.registeredHotkeys = hotkeys.map((k) => k.hotkey);\n },\n },\n});\n</script>\n\n<style scoped>\n/* Layout for session configuration */\n.session-layout {\n display: flex;\n flex-direction: column;\n}\n\n/* Fixed controls container */\n.fixed-controls-container {\n width: 100%;\n margin-bottom: 20px;\n}\n\n.fixed-controls {\n display: flex;\n flex-direction: column;\n}\n\n.time-limit-field {\n width: 100%;\n margin-bottom: 16px;\n}\n\n.start-btn {\n margin-top: 8px;\n max-height: 150px;\n}\n\n/* Course selection styles */\n.course-selection-container {\n width: 100%;\n}\n\n.course-row {\n border-bottom: 1px solid rgba(var(--v-border-color), 0.12);\n padding: 8px 0;\n}\n\n.course-row-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n}\n\n.course-row-actions {\n display: flex;\n align-items: center;\n gap: 12px;\n}\n\n.reviews-count {\n min-width: 32px;\n text-align: center;\n color: rgba(var(--v-theme-on-surface), 0.6);\n}\n\n.tag-filter-container {\n padding: 12px 0 12px 32px;\n background-color: rgba(var(--v-theme-surface-variant), 0.3);\n border-radius: 4px;\n margin-top: 8px;\n}\n\n.select-all-row {\n padding-top: 8px;\n border-top: 2px solid rgba(var(--v-border-color), 0.24);\n}\n\n.filter-summary {\n display: flex;\n align-items: center;\n padding: 8px 12px;\n background-color: rgba(var(--v-theme-primary), 0.08);\n border-radius: 4px;\n}\n\n/* Media queries for desktop layout */\n@media (min-width: 960px) {\n .session-layout {\n flex-direction: row;\n gap: 40px;\n }\n\n .fixed-controls-container {\n width: 300px;\n flex-shrink: 0;\n }\n\n .fixed-controls {\n position: sticky;\n top: 20px;\n padding-left: 20px;\n }\n\n .course-selection-container {\n flex-grow: 1;\n border-right: 1px solid rgba(0, 0, 0, 0.12);\n padding-right: 20px;\n }\n}\n</style>\n","<template>\n <div v-if=\"hasRegistrations || courseLoadError || classroomLoadError\">\n <div data-cy=\"select-quilts-header\" class=\"text-h4 mb-4\">Study Session Setup</div>\n\n <!-- Error Messages -->\n <v-alert v-if=\"courseLoadError\" type=\"warning\" class=\"mb-4\">\n Unable to load course data. Some features may be unavailable.\n </v-alert>\n\n <v-alert v-if=\"classroomLoadError\" type=\"warning\" class=\"mb-4\">\n Unable to load classroom data. You can still study individual courses.\n </v-alert>\n\n <div class=\"session-layout\">\n <!-- Left Column: Course Selection -->\n <div class=\"course-selection-container\">\n <div class=\"text-h6 mb-3\">Select Quilts to Study</div>\n\n <!-- Classrooms -->\n <div v-for=\"classroom in activeClasses\" :key=\"classroom.classID\" class=\"course-row\">\n <div class=\"course-row-header\">\n <v-checkbox\n v-model=\"classroom.selected\"\n :label=\"`Class: ${classroom.name}`\"\n hide-details\n @click.capture=\"update\"\n />\n <span class=\"reviews-count\">-</span>\n </div>\n </div>\n\n <!-- Courses -->\n <div v-for=\"course in activeCourses\" :key=\"course.courseID\" class=\"course-row\">\n <div class=\"course-row-header\">\n <v-checkbox\n v-model=\"course.selected\"\n data-cy=\"course-checkbox\"\n :label=\"`q/${course.name}`\"\n hide-details\n @click.capture=\"update\"\n />\n <div class=\"course-row-actions\">\n <span class=\"reviews-count\">{{ course.reviews }}</span>\n <v-btn\n v-if=\"course.selected\"\n variant=\"text\"\n size=\"small\"\n :color=\"hasActiveTagFilter(course) ? 'primary' : 'default'\"\n @click=\"toggleTagFilter(course.courseID)\"\n >\n <v-icon start size=\"small\">mdi-filter-variant</v-icon>\n {{ hasActiveTagFilter(course) ? 'Filtered' : 'Filter' }}\n <v-icon end size=\"small\">\n {{ expandedFilters[course.courseID] ? 'mdi-chevron-up' : 'mdi-chevron-down' }}\n </v-icon>\n </v-btn>\n </div>\n </div>\n\n <!-- Tag Filter Widget (expandable) -->\n <v-expand-transition>\n <div v-if=\"course.selected && expandedFilters[course.courseID]\" class=\"tag-filter-container\">\n <CourseTagFilterWidget v-model=\"course.tagFilter\" :course-id=\"course.courseID\" />\n </div>\n </v-expand-transition>\n </div>\n\n <!-- Select All -->\n <div class=\"select-all-row mt-3\">\n <v-checkbox\n id=\"SelectAll\"\n ref=\"selectAll\"\n v-model=\"allSelected\"\n autofocus\n label=\"Select All\"\n hide-details\n @update:model-value=\"toggleAll\"\n ></v-checkbox>\n </div>\n </div>\n\n <!-- Right Column: Time Configuration and Start Button -->\n <div class=\"fixed-controls-container\">\n <div class=\"fixed-controls\">\n <div class=\"text-h6 mb-3\">Session Settings</div>\n\n <div class=\"mb-5\">\n <v-text-field\n ref=\"numberField\"\n v-model=\"timeLimit\"\n class=\"time-limit-field\"\n variant=\"outlined\"\n label=\"Study Session Timelimit\"\n prepend-inner-icon=\"mdi-clock-outline\"\n prepend-icon=\"mdi-minus\"\n append-icon=\"mdi-plus\"\n :suffix=\"timeLimit > 1 ? 'minutes' : 'minute'\"\n mask=\"##\"\n type=\"number\"\n @click:prepend=\"dec\"\n @click:append=\"inc\"\n />\n </div>\n\n <!-- Filter Summary -->\n <div v-if=\"hasAnyActiveFilter\" class=\"filter-summary mb-4\">\n <v-icon size=\"small\" color=\"primary\" class=\"mr-1\">mdi-filter</v-icon>\n <span class=\"text-caption\">\n {{ activeFilterCount }} course{{ activeFilterCount > 1 ? 's' : '' }} with tag filters\n </span>\n </div>\n\n <SkMouseTrapToolTip\n hotkey=\"enter\"\n command=\"Start Session\"\n :disabled=\"!hasSelectedSources\"\n highlight-effect=\"scale\"\n >\n <v-btn\n data-cy=\"start-studying-button\"\n color=\"success\"\n size=\"large\"\n block\n class=\"start-btn\"\n :disabled=\"!hasSelectedSources\"\n @click=\"startSession\"\n >\n <v-icon start>mdi-play</v-icon>\n Start!\n </v-btn>\n </SkMouseTrapToolTip>\n </div>\n </div>\n </div>\n </div>\n <div v-else-if=\"!courseLoadError && !classroomLoadError\" class=\"text-h4\">\n <p>You don't have anything to study!</p>\n <p>Head over to the <router-link to=\"/quilts\">Quilts</router-link> page to find something for you.</p>\n </div>\n <div v-else class=\"text-h4\">\n <p>Unable to load study data due to technical issues.</p>\n <p>Please try refreshing the page or contact support if the problem persists.</p>\n </div>\n</template>\n\n<script lang=\"ts\">\nimport { defineComponent } from 'vue';\nimport { SkldrMouseTrap, getCurrentUser, SkMouseTrapToolTip, CourseTagFilterWidget } from '@vue-skuilder/common-ui';\nimport { CourseRegistration, UserDBInterface, getDataLayer, ContentSourceID } from '@vue-skuilder/db';\nimport { TagFilter, hasActiveFilter } from '@vue-skuilder/common';\n\nexport interface SessionConfigMetaData {\n selected: boolean;\n name: string;\n reviews: number;\n tagFilter?: TagFilter;\n}\n\n// Extended ContentSourceID that can carry tag filter configuration\nexport interface TagFilteredContentSourceID extends ContentSourceID {\n tagFilter?: TagFilter;\n}\n\nexport default defineComponent({\n name: 'SessionConfiguration',\n\n components: {\n SkMouseTrapToolTip,\n CourseTagFilterWidget,\n },\n\n props: {\n initialTimeLimit: {\n type: Number,\n required: true,\n default: 5,\n },\n },\n\n emits: ['initStudySession'],\n\n data() {\n return {\n registeredHotkeys: [] as (string | string[])[],\n allSelected: true,\n activeCourses: [] as (CourseRegistration & SessionConfigMetaData)[],\n activeClasses: [] as ({ classID: string } & SessionConfigMetaData)[],\n hasRegistrations: true,\n user: null as UserDBInterface | null,\n timeLimit: this.initialTimeLimit,\n courseLoadError: false,\n classroomLoadError: false,\n expandedFilters: {} as Record<string, boolean>,\n };\n },\n\n computed: {\n hasSelectedSources(): boolean {\n return this.activeCourses.some((c) => c.selected) || this.activeClasses.some((c) => c.selected);\n },\n\n hasAnyActiveFilter(): boolean {\n return this.activeCourses.some((c) => c.selected && this.hasActiveTagFilter(c));\n },\n\n activeFilterCount(): number {\n return this.activeCourses.filter((c) => c.selected && this.hasActiveTagFilter(c)).length;\n },\n },\n\n watch: {\n timeLimit: {\n handler() {\n if (this.timeLimit <= 0) {\n this.timeLimit = 1;\n }\n },\n },\n },\n\n beforeUnmount() {\n // Clean up registered hotkeys when component unmounts\n if (this.registeredHotkeys) {\n this.registeredHotkeys.forEach((key) => {\n SkldrMouseTrap.removeBinding(key);\n });\n }\n },\n\n async created() {\n this.user = await getCurrentUser();\n this.timeLimit = this.initialTimeLimit;\n\n this.setHotkeys();\n const [coursesResult, classroomsResult] = await Promise.allSettled([\n this.getActiveCourses(),\n this.getActiveClassrooms(),\n ]);\n\n // Handle course loading failure\n if (coursesResult.status === 'rejected') {\n console.error('Failed to load courses:', coursesResult.reason);\n this.courseLoadError = true;\n }\n\n // Handle classroom loading failure\n if (classroomsResult.status === 'rejected') {\n console.error('Failed to load classrooms:', classroomsResult.reason);\n this.classroomLoadError = true;\n }\n\n if (\n this.activeCourses.length === 0 &&\n this.activeClasses.length === 0 &&\n !this.courseLoadError &&\n !this.classroomLoadError\n ) {\n this.hasRegistrations = false;\n }\n },\n\n mounted() {\n document.getElementById('SelectAll')?.focus();\n },\n\n unmounted() {\n // Clean up registered hotkeys when component unmounts\n if (this.registeredHotkeys) {\n this.registeredHotkeys.forEach((key) => {\n SkldrMouseTrap.removeBinding(key);\n });\n }\n },\n\n methods: {\n inc() {\n this.timeLimit = this.timeLimit + 1;\n console.log(`inc to ${this.timeLimit}`);\n },\n\n dec() {\n this.timeLimit--;\n console.log(`dec to ${this.timeLimit}`);\n },\n\n update() {\n console.log(JSON.stringify(this.activeCourses));\n console.log(JSON.stringify(this.activeClasses));\n },\n\n toggleAll(): void {\n console.log(`Toggling all courses`);\n this.activeCourses.forEach((crs) => {\n crs.selected = this.allSelected;\n });\n this.activeClasses.forEach((cl) => {\n cl.selected = this.allSelected;\n });\n console.log(JSON.stringify(this.activeCourses));\n },\n\n toggleTagFilter(courseID: string): void {\n this.expandedFilters[courseID] = !this.expandedFilters[courseID];\n },\n\n hasActiveTagFilter(course: CourseRegistration & SessionConfigMetaData): boolean {\n return hasActiveFilter(course.tagFilter);\n },\n\n startSession() {\n // Clean up any registered hotkeys before starting session\n if (this.registeredHotkeys) {\n this.registeredHotkeys.forEach((key) => {\n SkldrMouseTrap.removeBinding(key);\n });\n }\n\n // Build sources with optional tag filters\n const selectedCourses: TagFilteredContentSourceID[] = this.activeCourses\n .filter((c) => c.selected)\n .map((c) => {\n const source: TagFilteredContentSourceID = {\n type: 'course',\n id: c.courseID,\n };\n // Only include tagFilter if it has active constraints\n if (hasActiveFilter(c.tagFilter)) {\n source.tagFilter = c.tagFilter;\n }\n return source;\n });\n\n const selectedClassrooms: ContentSourceID[] = this.activeClasses\n .filter((cl) => cl.selected)\n .map((cl) => ({\n type: 'classroom',\n id: cl.classID,\n }));\n\n const allSelectedSources = [...selectedCourses, ...selectedClassrooms];\n this.$emit('initStudySession', allSelectedSources, this.timeLimit);\n },\n\n async getActiveClassrooms() {\n const classes = await (await getCurrentUser()).getActiveClasses();\n const activeClasses: ({ classID: string } & SessionConfigMetaData)[] = [];\n\n console.log(`Active classes: ${JSON.stringify(classes)}`);\n\n await Promise.all(\n classes.map((c) =>\n (async (classID: string) => {\n const classDb = await getDataLayer().getClassroomDB(classID, `student`);\n activeClasses.push({\n classID,\n name: classDb.getConfig().name,\n selected: true,\n reviews: 0,\n });\n })(c)\n )\n );\n this.activeClasses = activeClasses;\n },\n\n async getActiveCourses() {\n this.activeCourses = (await this.user!.getActiveCourses()).map((c) => ({\n ...c,\n selected: true,\n name: '',\n reviews: 0,\n tagFilter: undefined,\n }));\n\n Promise.all(\n this.activeCourses.map(async (c, i) => {\n const cfg = await getDataLayer().getCoursesDB().getCourseConfig(c.courseID);\n const crsInterface = await this.user?.getCourseInterface(c.courseID);\n return (async () => {\n return Promise.all([\n (this.activeCourses[i].name = cfg.name),\n (this.activeCourses[i].reviews = await crsInterface!.getScheduledReviewCount()),\n ]);\n })();\n })\n );\n },\n\n setHotkeys() {\n const hotkeys = [\n {\n hotkey: 'right',\n callback: () => {\n this.timeLimit++;\n },\n command: 'Increase time limit',\n },\n {\n hotkey: 'left',\n callback: () => {\n this.timeLimit--;\n },\n command: 'Decrease time limit',\n },\n ];\n SkldrMouseTrap.addBinding(hotkeys);\n this.registeredHotkeys = hotkeys.map((k) => k.hotkey);\n },\n },\n});\n</script>\n\n<style scoped>\n/* Layout for session configuration */\n.session-layout {\n display: flex;\n flex-direction: column;\n}\n\n/* Fixed controls container */\n.fixed-controls-container {\n width: 100%;\n margin-bottom: 20px;\n}\n\n.fixed-controls {\n display: flex;\n flex-direction: column;\n}\n\n.time-limit-field {\n width: 100%;\n margin-bottom: 16px;\n}\n\n.start-btn {\n margin-top: 8px;\n max-height: 150px;\n}\n\n/* Course selection styles */\n.course-selection-container {\n width: 100%;\n}\n\n.course-row {\n border-bottom: 1px solid rgba(var(--v-border-color), 0.12);\n padding: 8px 0;\n}\n\n.course-row-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n}\n\n.course-row-actions {\n display: flex;\n align-items: center;\n gap: 12px;\n}\n\n.reviews-count {\n min-width: 32px;\n text-align: center;\n color: rgba(var(--v-theme-on-surface), 0.6);\n}\n\n.tag-filter-container {\n padding: 12px 0 12px 32px;\n background-color: rgba(var(--v-theme-surface-variant), 0.3);\n border-radius: 4px;\n margin-top: 8px;\n}\n\n.select-all-row {\n padding-top: 8px;\n border-top: 2px solid rgba(var(--v-border-color), 0.24);\n}\n\n.filter-summary {\n display: flex;\n align-items: center;\n padding: 8px 12px;\n background-color: rgba(var(--v-theme-primary), 0.08);\n border-radius: 4px;\n}\n\n/* Media queries for desktop layout */\n@media (min-width: 960px) {\n .session-layout {\n flex-direction: row;\n gap: 40px;\n }\n\n .fixed-controls-container {\n width: 300px;\n flex-shrink: 0;\n }\n\n .fixed-controls {\n position: sticky;\n top: 20px;\n padding-left: 20px;\n }\n\n .course-selection-container {\n flex-grow: 1;\n border-right: 1px solid rgba(0, 0, 0, 0.12);\n padding-right: 20px;\n }\n}\n</style>\n","<template>\n <div v-if=\"!inSession\">\n <SessionConfiguration\n :initial-time-limit=\"sessionTimeLimit\"\n @init-study-session=\"(sources, timeLimit) => initStudySession(sources, timeLimit)\"\n />\n </div>\n <div v-else>\n <div v-if=\"previewMode && previewCourseConfig\" align=\"center\">\n <v-row>\n <v-col>\n <span class=\"text-h5\">\n Quilt preview for <em>{{ previewCourseConfig.name }}</em>\n </span>\n <v-btn size=\"small\" color=\"primary\" @click=\"registerUserForPreviewCourse\">Join</v-btn>\n <router-link :to=\"`/quilts/${previewCourseConfig.courseID}`\">\n <v-btn size=\"small\" color=\"secondary\">More info</v-btn>\n </router-link>\n <v-spacer></v-spacer>\n </v-col>\n </v-row>\n </div>\n <div v-else-if=\"previewMode\">\n <v-row>\n <v-col>\n <span class=\"text-h5\">... No course was specified for the preview.</span>\n <div>(this shouldn't happen)...</div>\n </v-col>\n </v-row>\n </div>\n\n <!-- Study Session Component (may be in loading state) -->\n <div v-if=\"inSession\">\n <!-- Loading indicator while session is being prepared -->\n <div v-if=\"!sessionPrepared && !sessionError\" class=\"session-loading\">\n <v-container class=\"text-center\">\n <v-row justify=\"center\" align=\"center\" style=\"min-height: 50vh\">\n <v-col cols=\"12\">\n <v-progress-circular size=\"70\" width=\"7\" color=\"primary\" indeterminate></v-progress-circular>\n <div class=\"text-h5 mt-4\">Preparing your study session...</div>\n <div class=\"text-subtitle-1 mt-2\">Getting your learning materials ready</div>\n </v-col>\n </v-row>\n </v-container>\n </div>\n\n <!-- Error state -->\n <div v-if=\"sessionError\" class=\"session-error\">\n <v-container class=\"text-center\">\n <v-row justify=\"center\" align=\"center\" style=\"min-height: 50vh\">\n <v-col cols=\"12\">\n <v-icon size=\"64\" color=\"error\">mdi-alert-circle</v-icon>\n <div class=\"text-h5 mt-4 text-error\">Session Preparation Failed</div>\n <div class=\"text-subtitle-1 mt-2\">\n {{ errorMessage || 'There was a problem preparing your study session.' }}\n </div>\n <v-btn color=\"primary\" class=\"mt-6\" @click=\"refreshRoute\">Try Again</v-btn>\n </v-col>\n </v-row>\n </v-container>\n </div>\n\n <StudySession\n :content-sources=\"sessionContentSources\"\n :session-time-limit=\"sessionTimeLimit\"\n :user=\"user as UserDBInterface\"\n :session-config=\"studySessionConfig\"\n :data-layer=\"dataLayer\"\n :get-view-component=\"getViewComponent\"\n :class=\"{ 'hidden-session': !sessionPrepared }\"\n @session-finished=\"handleSessionFinished\"\n @session-prepared=\"handleSessionPrepared\"\n @session-error=\"handleSessionError\"\n />\n </div>\n </div>\n</template>\n\n<script lang=\"ts\">\nimport SessionConfiguration from '@pui/components/Study/SessionConfiguration.vue';\nimport { getCurrentUser, useConfigStore } from '@vue-skuilder/common-ui';\nimport { useDataInputFormStore } from '@vue-skuilder/edit-ui';\nimport { CourseConfig, TagFilter, hasActiveFilter } from '@vue-skuilder/common';\nimport { StudySession, type StudySessionConfig } from '@vue-skuilder/common-ui';\nimport { allCourseWare } from '@vue-skuilder/courseware';\nimport { ContentSourceID, UserDBInterface, getDataLayer } from '@vue-skuilder/db';\nimport { defineComponent } from 'vue';\nimport { Router } from 'vue-router';\n\nfunction randomInt(min: number, max: number): number {\n return Math.floor(Math.random() * (max - min + 1)) + min;\n}\n\nexport default defineComponent({\n name: 'StudyView',\n\n components: {\n SessionConfiguration,\n StudySession,\n },\n\n inject: {\n router: {\n from: 'router',\n },\n },\n\n props: {\n /**\n * If present, user will engage in a study session for the specified (non-registered) course.\n */\n previewCourseID: {\n type: String,\n required: false,\n default: '',\n },\n /**\n * If true, the user will engage in a study session for a\n * random (public) course they are not already registered for.\n */\n randomPreview: {\n type: Boolean,\n required: false,\n },\n /**\n * If present, user will engage in a study session for the specified (registered) course.\n */\n focusCourseID: {\n type: String,\n required: false,\n default: '',\n },\n },\n\n data() {\n return {\n user: null as UserDBInterface | null,\n studySessionConfig: undefined as StudySessionConfig | undefined,\n previewCourseConfig: undefined as CourseConfig | undefined,\n previewMode: false,\n sessionTimeLimit: 5,\n inSession: false,\n sessionPrepared: false,\n sessionError: false,\n errorMessage: '',\n sessionContentSources: [] as ContentSourceID[],\n dataInputFormStore: useDataInputFormStore(),\n getViewComponent: (view_id: string) => allCourseWare.getView(view_id),\n dataLayer: getDataLayer(),\n };\n },\n\n async created() {\n this.user = await getCurrentUser();\n this.studySessionConfig = {\n likesConfetti: useConfigStore().config.likesConfetti,\n };\n\n let singletonStudyCourseID = '';\n\n if (this.randomPreview) {\n const userCourseRegDoc = await this.user.getCourseRegistrationsDoc();\n const allCourseWare = (await getDataLayer().getCoursesDB().getCourseList()).map((r) => r.courseID);\n const unRegisteredCourses = allCourseWare.filter((c) => {\n return !userCourseRegDoc.courses.some((rc) => rc.courseID === c);\n });\n if (unRegisteredCourses.length > 0) {\n singletonStudyCourseID = unRegisteredCourses[randomInt(0, unRegisteredCourses.length)]!;\n } else {\n singletonStudyCourseID = allCourseWare[randomInt(0, allCourseWare.length)]!;\n }\n }\n\n if (this.previewCourseID) {\n this.previewMode = true;\n getDataLayer()\n .getCoursesDB()\n .getCourseList()\n .then((courses) => {\n courses.forEach((c) => {\n if (c.courseID === this.previewCourseID) {\n this.previewCourseConfig = c;\n this.previewCourseConfig!.courseID = c.courseID;\n }\n });\n });\n\n console.log(`[Study] COURSE PREVIEW MODE FOR ${this.previewCourseID}`);\n await this.user!.registerForCourse(this.previewCourseID, true);\n\n singletonStudyCourseID = this.previewCourseID;\n }\n\n if (this.focusCourseID) {\n console.log(`[Study] FOCUS study session: ${this.focusCourseID}`);\n singletonStudyCourseID = this.focusCourseID;\n }\n\n if (singletonStudyCourseID) {\n // Parse tag filter from query params\n const tagFilter = this.parseTagFilterFromQuery();\n const source: ContentSourceID = {\n type: 'course',\n id: singletonStudyCourseID,\n };\n\n if (hasActiveFilter(tagFilter)) {\n source.tagFilter = tagFilter;\n console.log(`[Study] Tag filter from query params:`, tagFilter);\n }\n\n this.initStudySession([source], this.sessionTimeLimit);\n }\n },\n\n methods: {\n refreshRoute() {\n (this.router as Router).go(0);\n },\n\n async initStudySession(sources: ContentSourceID[], timeLimit: number) {\n console.log(`[Study] starting study session w/ sources: ${JSON.stringify(sources)}`);\n\n this.sessionContentSources = sources;\n this.sessionTimeLimit = timeLimit;\n this.inSession = true;\n this.sessionPrepared = false;\n\n // Adding a console log to debug event handling\n console.log('[Study] Waiting for session-prepared event from StudySession component');\n },\n\n registerUserForPreviewCourse() {\n this.user!.registerForCourse(this.previewCourseConfig!.courseID!).then(() =>\n (this.router as Router).push(`/quilts/${this.previewCourseConfig!.courseID!}`)\n );\n },\n\n handleSessionFinished() {\n this.refreshRoute();\n },\n\n handleSessionPrepared() {\n console.log('[Study] Session preparation complete - received session-prepared event');\n this.sessionPrepared = true;\n this.sessionError = false;\n this.errorMessage = '';\n },\n\n handleSessionError({ message, error }) {\n console.error('[Study] Session error:', message, error);\n this.sessionError = true;\n this.errorMessage = message || 'An error occurred while preparing your study session.';\n this.sessionPrepared = false;\n },\n\n /**\n * Parse tag filter from URL query parameters.\n *\n * Supports:\n * - ?include=tagA,tagB&exclude=tagC\n * - Comma-separated values for multiple tags\n *\n * @returns TagFilter parsed from query, or empty filter if no params\n */\n parseTagFilterFromQuery(): TagFilter {\n const includeParam = this.$route.query.include;\n const excludeParam = this.$route.query.exclude;\n\n const parseParam = (param: string | string[] | undefined | null): string[] => {\n if (!param) return [];\n if (Array.isArray(param)) {\n // Handle repeated params: ?include=a&include=b\n return param.flatMap((p) => (p ? p.split(',').map((t) => t.trim()) : [])).filter(Boolean);\n }\n // Handle comma-separated: ?include=a,b\n return param\n .split(',')\n .map((t) => t.trim())\n .filter(Boolean);\n };\n\n return {\n include: parseParam(includeParam as string | string[] | undefined),\n exclude: parseParam(excludeParam as string | string[] | undefined),\n };\n },\n },\n});\n</script>\n\n<style scoped>\n.hidden-session {\n visibility: hidden;\n position: absolute;\n z-index: -1;\n}\n\n.session-error {\n color: var(--v-error-base);\n}\n</style>\n","<template>\n <div v-if=\"!inSession\">\n <SessionConfiguration\n :initial-time-limit=\"sessionTimeLimit\"\n @init-study-session=\"(sources, timeLimit) => initStudySession(sources, timeLimit)\"\n />\n </div>\n <div v-else>\n <div v-if=\"previewMode && previewCourseConfig\" align=\"center\">\n <v-row>\n <v-col>\n <span class=\"text-h5\">\n Quilt preview for <em>{{ previewCourseConfig.name }}</em>\n </span>\n <v-btn size=\"small\" color=\"primary\" @click=\"registerUserForPreviewCourse\">Join</v-btn>\n <router-link :to=\"`/quilts/${previewCourseConfig.courseID}`\">\n <v-btn size=\"small\" color=\"secondary\">More info</v-btn>\n </router-link>\n <v-spacer></v-spacer>\n </v-col>\n </v-row>\n </div>\n <div v-else-if=\"previewMode\">\n <v-row>\n <v-col>\n <span class=\"text-h5\">... No course was specified for the preview.</span>\n <div>(this shouldn't happen)...</div>\n </v-col>\n </v-row>\n </div>\n\n <!-- Study Session Component (may be in loading state) -->\n <div v-if=\"inSession\">\n <!-- Loading indicator while session is being prepared -->\n <div v-if=\"!sessionPrepared && !sessionError\" class=\"session-loading\">\n <v-container class=\"text-center\">\n <v-row justify=\"center\" align=\"center\" style=\"min-height: 50vh\">\n <v-col cols=\"12\">\n <v-progress-circular size=\"70\" width=\"7\" color=\"primary\" indeterminate></v-progress-circular>\n <div class=\"text-h5 mt-4\">Preparing your study session...</div>\n <div class=\"text-subtitle-1 mt-2\">Getting your learning materials ready</div>\n </v-col>\n </v-row>\n </v-container>\n </div>\n\n <!-- Error state -->\n <div v-if=\"sessionError\" class=\"session-error\">\n <v-container class=\"text-center\">\n <v-row justify=\"center\" align=\"center\" style=\"min-height: 50vh\">\n <v-col cols=\"12\">\n <v-icon size=\"64\" color=\"error\">mdi-alert-circle</v-icon>\n <div class=\"text-h5 mt-4 text-error\">Session Preparation Failed</div>\n <div class=\"text-subtitle-1 mt-2\">\n {{ errorMessage || 'There was a problem preparing your study session.' }}\n </div>\n <v-btn color=\"primary\" class=\"mt-6\" @click=\"refreshRoute\">Try Again</v-btn>\n </v-col>\n </v-row>\n </v-container>\n </div>\n\n <StudySession\n :content-sources=\"sessionContentSources\"\n :session-time-limit=\"sessionTimeLimit\"\n :user=\"user as UserDBInterface\"\n :session-config=\"studySessionConfig\"\n :data-layer=\"dataLayer\"\n :get-view-component=\"getViewComponent\"\n :class=\"{ 'hidden-session': !sessionPrepared }\"\n @session-finished=\"handleSessionFinished\"\n @session-prepared=\"handleSessionPrepared\"\n @session-error=\"handleSessionError\"\n />\n </div>\n </div>\n</template>\n\n<script lang=\"ts\">\nimport SessionConfiguration from '@pui/components/Study/SessionConfiguration.vue';\nimport { getCurrentUser, useConfigStore } from '@vue-skuilder/common-ui';\nimport { useDataInputFormStore } from '@vue-skuilder/edit-ui';\nimport { CourseConfig, TagFilter, hasActiveFilter } from '@vue-skuilder/common';\nimport { StudySession, type StudySessionConfig } from '@vue-skuilder/common-ui';\nimport { allCourseWare } from '@vue-skuilder/courseware';\nimport { ContentSourceID, UserDBInterface, getDataLayer } from '@vue-skuilder/db';\nimport { defineComponent } from 'vue';\nimport { Router } from 'vue-router';\n\nfunction randomInt(min: number, max: number): number {\n return Math.floor(Math.random() * (max - min + 1)) + min;\n}\n\nexport default defineComponent({\n name: 'StudyView',\n\n components: {\n SessionConfiguration,\n StudySession,\n },\n\n inject: {\n router: {\n from: 'router',\n },\n },\n\n props: {\n /**\n * If present, user will engage in a study session for the specified (non-registered) course.\n */\n previewCourseID: {\n type: String,\n required: false,\n default: '',\n },\n /**\n * If true, the user will engage in a study session for a\n * random (public) course they are not already registered for.\n */\n randomPreview: {\n type: Boolean,\n required: false,\n },\n /**\n * If present, user will engage in a study session for the specified (registered) course.\n */\n focusCourseID: {\n type: String,\n required: false,\n default: '',\n },\n },\n\n data() {\n return {\n user: null as UserDBInterface | null,\n studySessionConfig: undefined as StudySessionConfig | undefined,\n previewCourseConfig: undefined as CourseConfig | undefined,\n previewMode: false,\n sessionTimeLimit: 5,\n inSession: false,\n sessionPrepared: false,\n sessionError: false,\n errorMessage: '',\n sessionContentSources: [] as ContentSourceID[],\n dataInputFormStore: useDataInputFormStore(),\n getViewComponent: (view_id: string) => allCourseWare.getView(view_id),\n dataLayer: getDataLayer(),\n };\n },\n\n async created() {\n this.user = await getCurrentUser();\n this.studySessionConfig = {\n likesConfetti: useConfigStore().config.likesConfetti,\n };\n\n let singletonStudyCourseID = '';\n\n if (this.randomPreview) {\n const userCourseRegDoc = await this.user.getCourseRegistrationsDoc();\n const allCourseWare = (await getDataLayer().getCoursesDB().getCourseList()).map((r) => r.courseID);\n const unRegisteredCourses = allCourseWare.filter((c) => {\n return !userCourseRegDoc.courses.some((rc) => rc.courseID === c);\n });\n if (unRegisteredCourses.length > 0) {\n singletonStudyCourseID = unRegisteredCourses[randomInt(0, unRegisteredCourses.length)]!;\n } else {\n singletonStudyCourseID = allCourseWare[randomInt(0, allCourseWare.length)]!;\n }\n }\n\n if (this.previewCourseID) {\n this.previewMode = true;\n getDataLayer()\n .getCoursesDB()\n .getCourseList()\n .then((courses) => {\n courses.forEach((c) => {\n if (c.courseID === this.previewCourseID) {\n this.previewCourseConfig = c;\n this.previewCourseConfig!.courseID = c.courseID;\n }\n });\n });\n\n console.log(`[Study] COURSE PREVIEW MODE FOR ${this.previewCourseID}`);\n await this.user!.registerForCourse(this.previewCourseID, true);\n\n singletonStudyCourseID = this.previewCourseID;\n }\n\n if (this.focusCourseID) {\n console.log(`[Study] FOCUS study session: ${this.focusCourseID}`);\n singletonStudyCourseID = this.focusCourseID;\n }\n\n if (singletonStudyCourseID) {\n // Parse tag filter from query params\n const tagFilter = this.parseTagFilterFromQuery();\n const source: ContentSourceID = {\n type: 'course',\n id: singletonStudyCourseID,\n };\n\n if (hasActiveFilter(tagFilter)) {\n source.tagFilter = tagFilter;\n console.log(`[Study] Tag filter from query params:`, tagFilter);\n }\n\n this.initStudySession([source], this.sessionTimeLimit);\n }\n },\n\n methods: {\n refreshRoute() {\n (this.router as Router).go(0);\n },\n\n async initStudySession(sources: ContentSourceID[], timeLimit: number) {\n console.log(`[Study] starting study session w/ sources: ${JSON.stringify(sources)}`);\n\n this.sessionContentSources = sources;\n this.sessionTimeLimit = timeLimit;\n this.inSession = true;\n this.sessionPrepared = false;\n\n // Adding a console log to debug event handling\n console.log('[Study] Waiting for session-prepared event from StudySession component');\n },\n\n registerUserForPreviewCourse() {\n this.user!.registerForCourse(this.previewCourseConfig!.courseID!).then(() =>\n (this.router as Router).push(`/quilts/${this.previewCourseConfig!.courseID!}`)\n );\n },\n\n handleSessionFinished() {\n this.refreshRoute();\n },\n\n handleSessionPrepared() {\n console.log('[Study] Session preparation complete - received session-prepared event');\n this.sessionPrepared = true;\n this.sessionError = false;\n this.errorMessage = '';\n },\n\n handleSessionError({ message, error }) {\n console.error('[Study] Session error:', message, error);\n this.sessionError = true;\n this.errorMessage = message || 'An error occurred while preparing your study session.';\n this.sessionPrepared = false;\n },\n\n /**\n * Parse tag filter from URL query parameters.\n *\n * Supports:\n * - ?include=tagA,tagB&exclude=tagC\n * - Comma-separated values for multiple tags\n *\n * @returns TagFilter parsed from query, or empty filter if no params\n */\n parseTagFilterFromQuery(): TagFilter {\n const includeParam = this.$route.query.include;\n const excludeParam = this.$route.query.exclude;\n\n const parseParam = (param: string | string[] | undefined | null): string[] => {\n if (!param) return [];\n if (Array.isArray(param)) {\n // Handle repeated params: ?include=a&include=b\n return param.flatMap((p) => (p ? p.split(',').map((t) => t.trim()) : [])).filter(Boolean);\n }\n // Handle comma-separated: ?include=a,b\n return param\n .split(',')\n .map((t) => t.trim())\n .filter(Boolean);\n };\n\n return {\n include: parseParam(includeParam as string | string[] | undefined),\n exclude: parseParam(excludeParam as string | string[] | undefined),\n };\n },\n },\n});\n</script>\n\n<style scoped>\n.hidden-session {\n visibility: hidden;\n position: absolute;\n z-index: -1;\n}\n\n.session-error {\n color: var(--v-error-base);\n}\n</style>\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import"./chunk-D6hFPZc3.js";import{$ as e,C as t,H as n,K as r,S as i,T as a,Vt as o,_ as s,b as c,v as l,y as u}from"./vue.runtime.esm-bundler-C3q8JS9f.js";import{P as d,v as f}from"./common-ui.es-mrcutgtG.js";import{T as p}from"./dist-CrcJCHFk.js";import"./MarkdownRenderer-DoVbFpA6-BIbDxYC4.js";import{k as m,r as h}from"./dist-DkeESCP4.js";import{t as g}from"./index-jZbLHVSV.js";import{g as _}from"./dist-BlP52QF6.js";var v=a({name:`TagInformation`,components:{CourseCardBrowser:d},props:{tagId:{type:String,required:!0},courseId:{type:String,required:!0}},data(){return{snippetModel:``,editingSnippet:!1,snippetSaving:!1,wikiModel:``,editingWiki:!1,wikiSaving:!1,courseDB:null,tag:{course:this.courseId,name:this.tagId,snippet:``,wiki:``,taggedCards:[],docType:h.TAG},course:{courseID:this.courseId,name:``,description:``,public:!1,deleted:!1,dataShapes:[],questionTypes:[],creator:``,admins:[],moderators:[]}}},async created(){this.courseDB=m().getCourseDB(this.courseId),this.tag=await this.courseDB.getTag(this.tagId),this.snippetModel=this.tag.snippet,this.wikiModel=this.tag.wiki,this.course=await this.courseDB.getCourseConfig()},methods:{viewLookup(e){return _.getView(e)},editSnippet(){console.log(`[TagInformation] EditSnip`),this.editingSnippet=!0,this.$refs.snippetEditor.focus()},editWiki(){console.log(`[TagInformation] EditWiki`),this.editingWiki=!0,this.$refs.wikiEditor.focus()},async saveSnippet(){this.snippetSaving=!0,(await this.courseDB.updateTag({...this.tag,snippet:this.snippetModel})).ok?(console.log(`[TagInformation] OK`),this.tag.snippet=this.snippetModel,f({text:`Updated applied - thanks!`,status:p.ok})):f({text:`error in applying update!`,status:p.error}),this.editingSnippet=!1,this.snippetSaving=!1},async saveWiki(){this.wikiSaving=!0,(await this.courseDB.updateTag({...this.tag,wiki:this.wikiModel})).ok?(this.tag.wiki=this.wikiModel,f({text:`Updated applied - thanks!`,status:p.ok})):f({text:`error in applying update!`,status:p.error}),this.editingWiki=!1,this.wikiSaving=!1},cancelEditSnippet(){console.log(`[TagInformation] Cancelling EditSnip`),this.editingSnippet=!1,this.snippetModel=this.tag.snippet},cancelEditWiki(){this.editingWiki=!1,this.wikiModel=this.tag.wiki}}}),y={key:0},b={key:0};function _sfc_render(a,d,f,p,m,h){let g=r(`router-link`),_=r(`v-icon`),v=r(`v-progress-circular`),x=r(`v-fade-transition`),S=r(`v-text-field`),C=r(`course-card-browser`);return n(),c(`div`,null,[s(`h1`,null,[t(g,{to:`/q/${a.course.name}`},{default:e(()=>[i(o(a.course.name),1)]),_:1},8,[`to`]),i(` > Tag: `+o(a.tag.name),1)]),d[8]||(d[8]=s(`br`,null,null,-1)),s(`p`,null,o(a.tag.taggedCards.length)+` card`+o(a.tag.taggedCards.length===1?``:`s`),1),t(S,{ref:`snippetEditor`,modelValue:a.snippetModel,"onUpdate:modelValue":d[0]||(d[0]=e=>a.snippetModel=e),variant:`outlined`,readonly:!a.editingSnippet,counter:a.editingSnippet,label:`Brief tag description:`,placeholder:`No snippet yet - add one!`,type:`text`},{prepend:e(()=>[a.editingSnippet?(n(),c(`span`,y,[t(_,{color:`primary`,onClick:a.saveSnippet},{default:e(()=>d[2]||(d[2]=[i(`mdi-content-save`)])),_:1},8,[`onClick`])])):(n(),l(_,{key:1,color:`primary`,onClick:a.editSnippet},{default:e(()=>d[3]||(d[3]=[i(`mdi-pencil`)])),_:1},8,[`onClick`]))]),append:e(()=>[a.editingSnippet?(n(),l(_,{key:0,onClick:a.cancelEditSnippet},{default:e(()=>d[4]||(d[4]=[i(`mdi-cancel`)])),_:1},8,[`onClick`])):u(``,!0),t(x,{"leave-absolute":``},{default:e(()=>[a.snippetSaving?(n(),l(v,{key:0,size:`20`,color:`info`,indeterminate:``})):u(``,!0)]),_:1})]),_:1},8,[`modelValue`,`readonly`,`counter`]),t(S,{ref:`wikiEditor`,modelValue:a.wikiModel,"onUpdate:modelValue":d[1]||(d[1]=e=>a.wikiModel=e),variant:`outlined`,readonly:!a.editingWiki,counter:a.editingWiki,label:`Extended tag description:`,placeholder:`No wiki yet - consider adding one!`,textarea:``},{prepend:e(()=>[a.editingWiki?(n(),c(`span`,b,[t(_,{color:`primary`,onClick:a.saveWiki},{default:e(()=>d[5]||(d[5]=[i(`mdi-content-save`)])),_:1},8,[`onClick`])])):(n(),l(_,{key:1,color:`primary`,onClick:a.editWiki},{default:e(()=>d[6]||(d[6]=[i(`mdi-pencil`)])),_:1},8,[`onClick`]))]),append:e(()=>[a.editingWiki?(n(),l(_,{key:0,onClick:a.cancelEditWiki},{default:e(()=>d[7]||(d[7]=[i(`mdi-cancel`)])),_:1},8,[`onClick`])):u(``,!0),t(x,{"leave-absolute":``},{default:e(()=>[a.wikiSaving?(n(),l(v,{key:0,size:`20`,color:`info`,indeterminate:``})):u(``,!0)]),_:1})]),_:1},8,[`modelValue`,`readonly`,`counter`]),t(C,{"course-id":a.courseId,"tag-id":a.tagId,"view-lookup-function":a.viewLookup},null,8,[`course-id`,`tag-id`,`view-lookup-function`])])}var x=g(v,[[`render`,_sfc_render]]);export{x as default};
|
|
2
|
+
//# sourceMappingURL=TagInformation-BjkMAGf0.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TagInformation-BjkMAGf0.js","names":[],"sources":["../../src/components/Courses/TagInformation.vue","../../src/components/Courses/TagInformation.vue"],"sourcesContent":["<template>\n <div>\n <!-- todo: -->\n <h1>\n <router-link :to=\"`/q/${course.name}`\">{{ course.name }}</router-link> > Tag: {{ tag.name }}\n </h1>\n <br />\n <p>{{ tag.taggedCards.length }} card{{ tag.taggedCards.length === 1 ? '' : 's' }}</p>\n\n <v-text-field\n ref=\"snippetEditor\"\n v-model=\"snippetModel\"\n variant=\"outlined\"\n :readonly=\"!editingSnippet\"\n :counter=\"editingSnippet\"\n label=\"Brief tag description:\"\n placeholder=\"No snippet yet - add one!\"\n type=\"text\"\n >\n <template #prepend>\n <span v-if=\"editingSnippet\">\n <v-icon color=\"primary\" @click=\"saveSnippet\">mdi-content-save</v-icon>\n </span>\n <v-icon v-else color=\"primary\" @click=\"editSnippet\">mdi-pencil</v-icon>\n </template>\n <template #append>\n <v-icon v-if=\"editingSnippet\" @click=\"cancelEditSnippet\">mdi-cancel</v-icon>\n <v-fade-transition leave-absolute>\n <!-- spinner while awaiting async write of edits -->\n <v-progress-circular v-if=\"snippetSaving\" size=\"20\" color=\"info\" indeterminate></v-progress-circular>\n </v-fade-transition>\n </template>\n </v-text-field>\n\n <v-text-field\n ref=\"wikiEditor\"\n v-model=\"wikiModel\"\n variant=\"outlined\"\n :readonly=\"!editingWiki\"\n :counter=\"editingWiki\"\n label=\"Extended tag description:\"\n placeholder=\"No wiki yet - consider adding one!\"\n textarea\n >\n <template #prepend>\n <span v-if=\"editingWiki\">\n <v-icon color=\"primary\" @click=\"saveWiki\">mdi-content-save</v-icon>\n </span>\n <v-icon v-else color=\"primary\" @click=\"editWiki\">mdi-pencil</v-icon>\n </template>\n <template #append>\n <v-icon v-if=\"editingWiki\" @click=\"cancelEditWiki\">mdi-cancel</v-icon>\n <v-fade-transition leave-absolute>\n <!-- spinner while awaiting async write of edits -->\n <v-progress-circular v-if=\"wikiSaving\" size=\"20\" color=\"info\" indeterminate></v-progress-circular>\n </v-fade-transition>\n </template>\n </v-text-field>\n\n <course-card-browser :course-id=\"courseId\" :tag-id=\"tagId\" :view-lookup-function=\"viewLookup\" />\n </div>\n</template>\n\n<script lang=\"ts\">\nimport { defineComponent } from 'vue';\nimport { DocType, Tag, getDataLayer, CourseDBInterface } from '@vue-skuilder/db';\nimport { Status, CourseConfig } from '@vue-skuilder/common';\nimport { CourseCardBrowser } from '@vue-skuilder/common-ui';\nimport { alertUser } from '@vue-skuilder/common-ui';\nimport { allCourseWare } from '@vue-skuilder/courseware';\n\nexport default defineComponent({\n name: 'TagInformation',\n\n components: {\n CourseCardBrowser,\n },\n\n props: {\n tagId: {\n type: String,\n required: true,\n },\n courseId: {\n type: String,\n required: true,\n },\n },\n\n data() {\n return {\n snippetModel: '',\n editingSnippet: false,\n snippetSaving: false,\n\n wikiModel: '',\n editingWiki: false,\n wikiSaving: false,\n\n courseDB: null as CourseDBInterface | null,\n\n tag: {\n course: this.courseId,\n name: this.tagId,\n snippet: '',\n wiki: '',\n taggedCards: [],\n docType: DocType.TAG,\n } as Tag,\n\n course: {\n courseID: this.courseId,\n name: '',\n description: '',\n public: false,\n deleted: false,\n dataShapes: [],\n questionTypes: [],\n creator: '',\n admins: [],\n moderators: [],\n } as CourseConfig,\n };\n },\n\n async created() {\n this.courseDB = getDataLayer().getCourseDB(this.courseId);\n this.tag = await this.courseDB.getTag(this.tagId);\n this.snippetModel = this.tag.snippet;\n this.wikiModel = this.tag.wiki;\n this.course = await this.courseDB.getCourseConfig();\n },\n\n methods: {\n viewLookup(x: unknown) {\n return allCourseWare.getView(x);\n },\n\n editSnippet() {\n console.log('[TagInformation] EditSnip');\n this.editingSnippet = true;\n (this.$refs.snippetEditor as HTMLInputElement).focus();\n },\n\n editWiki() {\n console.log('[TagInformation] EditWiki');\n this.editingWiki = true;\n (this.$refs.wikiEditor as HTMLInputElement).focus();\n },\n\n async saveSnippet() {\n this.snippetSaving = true;\n\n const update = await this.courseDB!.updateTag({\n ...this.tag,\n snippet: this.snippetModel,\n });\n\n if (update.ok) {\n console.log('[TagInformation] OK');\n this.tag.snippet = this.snippetModel;\n alertUser({\n text: `Updated applied - thanks!`,\n status: Status.ok,\n });\n } else {\n alertUser({\n text: `error in applying update!`,\n status: Status.error,\n });\n }\n\n this.editingSnippet = false;\n this.snippetSaving = false;\n },\n\n async saveWiki() {\n this.wikiSaving = true;\n\n const update = await this.courseDB!.updateTag({\n ...this.tag,\n wiki: this.wikiModel,\n });\n\n if (update.ok) {\n this.tag.wiki = this.wikiModel;\n alertUser({\n text: `Updated applied - thanks!`,\n status: Status.ok,\n });\n } else {\n alertUser({\n text: `error in applying update!`,\n status: Status.error,\n });\n }\n\n this.editingWiki = false;\n this.wikiSaving = false;\n },\n\n cancelEditSnippet() {\n console.log('[TagInformation] Cancelling EditSnip');\n this.editingSnippet = false;\n this.snippetModel = this.tag.snippet;\n },\n\n cancelEditWiki() {\n this.editingWiki = false;\n this.wikiModel = this.tag.wiki;\n },\n },\n});\n</script>\n","<template>\n <div>\n <!-- todo: -->\n <h1>\n <router-link :to=\"`/q/${course.name}`\">{{ course.name }}</router-link> > Tag: {{ tag.name }}\n </h1>\n <br />\n <p>{{ tag.taggedCards.length }} card{{ tag.taggedCards.length === 1 ? '' : 's' }}</p>\n\n <v-text-field\n ref=\"snippetEditor\"\n v-model=\"snippetModel\"\n variant=\"outlined\"\n :readonly=\"!editingSnippet\"\n :counter=\"editingSnippet\"\n label=\"Brief tag description:\"\n placeholder=\"No snippet yet - add one!\"\n type=\"text\"\n >\n <template #prepend>\n <span v-if=\"editingSnippet\">\n <v-icon color=\"primary\" @click=\"saveSnippet\">mdi-content-save</v-icon>\n </span>\n <v-icon v-else color=\"primary\" @click=\"editSnippet\">mdi-pencil</v-icon>\n </template>\n <template #append>\n <v-icon v-if=\"editingSnippet\" @click=\"cancelEditSnippet\">mdi-cancel</v-icon>\n <v-fade-transition leave-absolute>\n <!-- spinner while awaiting async write of edits -->\n <v-progress-circular v-if=\"snippetSaving\" size=\"20\" color=\"info\" indeterminate></v-progress-circular>\n </v-fade-transition>\n </template>\n </v-text-field>\n\n <v-text-field\n ref=\"wikiEditor\"\n v-model=\"wikiModel\"\n variant=\"outlined\"\n :readonly=\"!editingWiki\"\n :counter=\"editingWiki\"\n label=\"Extended tag description:\"\n placeholder=\"No wiki yet - consider adding one!\"\n textarea\n >\n <template #prepend>\n <span v-if=\"editingWiki\">\n <v-icon color=\"primary\" @click=\"saveWiki\">mdi-content-save</v-icon>\n </span>\n <v-icon v-else color=\"primary\" @click=\"editWiki\">mdi-pencil</v-icon>\n </template>\n <template #append>\n <v-icon v-if=\"editingWiki\" @click=\"cancelEditWiki\">mdi-cancel</v-icon>\n <v-fade-transition leave-absolute>\n <!-- spinner while awaiting async write of edits -->\n <v-progress-circular v-if=\"wikiSaving\" size=\"20\" color=\"info\" indeterminate></v-progress-circular>\n </v-fade-transition>\n </template>\n </v-text-field>\n\n <course-card-browser :course-id=\"courseId\" :tag-id=\"tagId\" :view-lookup-function=\"viewLookup\" />\n </div>\n</template>\n\n<script lang=\"ts\">\nimport { defineComponent } from 'vue';\nimport { DocType, Tag, getDataLayer, CourseDBInterface } from '@vue-skuilder/db';\nimport { Status, CourseConfig } from '@vue-skuilder/common';\nimport { CourseCardBrowser } from '@vue-skuilder/common-ui';\nimport { alertUser } from '@vue-skuilder/common-ui';\nimport { allCourseWare } from '@vue-skuilder/courseware';\n\nexport default defineComponent({\n name: 'TagInformation',\n\n components: {\n CourseCardBrowser,\n },\n\n props: {\n tagId: {\n type: String,\n required: true,\n },\n courseId: {\n type: String,\n required: true,\n },\n },\n\n data() {\n return {\n snippetModel: '',\n editingSnippet: false,\n snippetSaving: false,\n\n wikiModel: '',\n editingWiki: false,\n wikiSaving: false,\n\n courseDB: null as CourseDBInterface | null,\n\n tag: {\n course: this.courseId,\n name: this.tagId,\n snippet: '',\n wiki: '',\n taggedCards: [],\n docType: DocType.TAG,\n } as Tag,\n\n course: {\n courseID: this.courseId,\n name: '',\n description: '',\n public: false,\n deleted: false,\n dataShapes: [],\n questionTypes: [],\n creator: '',\n admins: [],\n moderators: [],\n } as CourseConfig,\n };\n },\n\n async created() {\n this.courseDB = getDataLayer().getCourseDB(this.courseId);\n this.tag = await this.courseDB.getTag(this.tagId);\n this.snippetModel = this.tag.snippet;\n this.wikiModel = this.tag.wiki;\n this.course = await this.courseDB.getCourseConfig();\n },\n\n methods: {\n viewLookup(x: unknown) {\n return allCourseWare.getView(x);\n },\n\n editSnippet() {\n console.log('[TagInformation] EditSnip');\n this.editingSnippet = true;\n (this.$refs.snippetEditor as HTMLInputElement).focus();\n },\n\n editWiki() {\n console.log('[TagInformation] EditWiki');\n this.editingWiki = true;\n (this.$refs.wikiEditor as HTMLInputElement).focus();\n },\n\n async saveSnippet() {\n this.snippetSaving = true;\n\n const update = await this.courseDB!.updateTag({\n ...this.tag,\n snippet: this.snippetModel,\n });\n\n if (update.ok) {\n console.log('[TagInformation] OK');\n this.tag.snippet = this.snippetModel;\n alertUser({\n text: `Updated applied - thanks!`,\n status: Status.ok,\n });\n } else {\n alertUser({\n text: `error in applying update!`,\n status: Status.error,\n });\n }\n\n this.editingSnippet = false;\n this.snippetSaving = false;\n },\n\n async saveWiki() {\n this.wikiSaving = true;\n\n const update = await this.courseDB!.updateTag({\n ...this.tag,\n wiki: this.wikiModel,\n });\n\n if (update.ok) {\n this.tag.wiki = this.wikiModel;\n alertUser({\n text: `Updated applied - thanks!`,\n status: Status.ok,\n });\n } else {\n alertUser({\n text: `error in applying update!`,\n status: Status.error,\n });\n }\n\n this.editingWiki = false;\n this.wikiSaving = false;\n },\n\n cancelEditSnippet() {\n console.log('[TagInformation] Cancelling EditSnip');\n this.editingSnippet = false;\n this.snippetModel = this.tag.snippet;\n },\n\n cancelEditWiki() {\n this.editingWiki = false;\n this.wikiModel = this.tag.wiki;\n },\n },\n});\n</script>\n"],"mappings":"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"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import"./chunk-D6hFPZc3.js";import{$ as e,C as t,D as n,H as r,K as i,Lt as a,S as o,T as s,Vt as c,_ as l,b as u,v as d,y as f}from"./vue.runtime.esm-bundler-C3q8JS9f.js";import{G as p,I as m,T as h,a as g}from"./common-ui.es-mrcutgtG.js";import"./dist-CrcJCHFk.js";import"./MarkdownRenderer-DoVbFpA6-BIbDxYC4.js";import{k as _}from"./dist-DkeESCP4.js";import{t as v}from"./index-jZbLHVSV.js";var y={};(function main(e,t,n,r){var i=!!(e.Worker&&e.Blob&&e.Promise&&e.OffscreenCanvas&&e.OffscreenCanvasRenderingContext2D&&e.HTMLCanvasElement&&e.HTMLCanvasElement.prototype.transferControlToOffscreen&&e.URL&&e.URL.createObjectURL),a=typeof Path2D==`function`&&typeof DOMMatrix==`function`,o=(function(){if(!e.OffscreenCanvas)return!1;var t=new OffscreenCanvas(1,1),n=t.getContext(`2d`);n.fillRect(0,0,1,1);var r=t.transferToImageBitmap();try{n.createPattern(r,`no-repeat`)}catch{return!1}return!0})();function noop(){}function promise(n){var r=t.exports.Promise,i=r===void 0?e.Promise:r;return typeof i==`function`?new i(n):(n(noop,noop),null)}var s=(function(e,t){return{transform:function(n){if(e)return n;if(t.has(n))return t.get(n);var r=new OffscreenCanvas(n.width,n.height);return r.getContext(`2d`).drawImage(n,0,0),t.set(n,r),r},clear:function(){t.clear()}}})(o,new Map),c=function(){var e=16,frame,cancel,t={},n=0;return typeof requestAnimationFrame==`function`&&typeof cancelAnimationFrame==`function`?(frame=function(r){var i=Math.random();return t[i]=requestAnimationFrame(function onFrame(a){n===a||n+e-1<a?(n=a,delete t[i],r()):t[i]=requestAnimationFrame(onFrame)}),i},cancel=function(e){t[e]&&cancelAnimationFrame(t[e])}):(frame=function(t){return setTimeout(t,e)},cancel=function(e){return clearTimeout(e)}),{frame,cancel}}(),l=(function(){var e,t,r={};function decorate(e){function execute(t,n){e.postMessage({options:t||{},callback:n})}e.init=function initWorker(t){var n=t.transferControlToOffscreen();e.postMessage({canvas:n},[n])},e.fire=function fireWorker(n,i,a){if(t)return execute(n,null),t;var o=Math.random().toString(36).slice(2);return t=promise(function(i){function workerDone(n){n.data.callback===o&&(delete r[o],e.removeEventListener(`message`,workerDone),t=null,s.clear(),a(),i())}e.addEventListener(`message`,workerDone),execute(n,o),r[o]=workerDone.bind(null,{data:{callback:o}})}),t},e.reset=function resetWorker(){for(var t in e.postMessage({reset:!0}),r)r[t](),delete r[t]}}return function(){if(e)return e;if(!n&&i){var t=[`var CONFETTI, SIZE = {}, module = {};`,`(`+main.toString()+`)(this, module, true, SIZE);`,`onmessage = function(msg) {`,` if (msg.data.options) {`,` CONFETTI(msg.data.options).then(function () {`,` if (msg.data.callback) {`,` postMessage({ callback: msg.data.callback });`,` }`,` });`,` } else if (msg.data.reset) {`,` CONFETTI && CONFETTI.reset();`,` } else if (msg.data.resize) {`,` SIZE.width = msg.data.resize.width;`,` SIZE.height = msg.data.resize.height;`,` } else if (msg.data.canvas) {`,` SIZE.width = msg.data.canvas.width;`,` SIZE.height = msg.data.canvas.height;`,` CONFETTI = module.exports.create(msg.data.canvas);`,` }`,`}`].join(`
|
|
2
|
+
`);try{e=new Worker(URL.createObjectURL(new Blob([t])))}catch(e){return typeof console.warn==`function`&&console.warn(`🎊 Could not load worker`,e),null}decorate(e)}return e}})(),u={particleCount:50,angle:90,spread:45,startVelocity:45,decay:.9,gravity:1,drift:0,ticks:200,x:.5,y:.5,shapes:[`square`,`circle`],zIndex:100,colors:[`#26ccff`,`#a25afd`,`#ff5e7e`,`#88ff5a`,`#fcff42`,`#ffa62d`,`#ff36ff`],disableForReducedMotion:!1,scalar:1};function convert(e,t){return t?t(e):e}function isOk(e){return e!=null}function prop(e,t,n){return convert(e&&isOk(e[t])?e[t]:u[t],n)}function onlyPositiveInt(e){return e<0?0:Math.floor(e)}function randomInt(e,t){return Math.floor(Math.random()*(t-e))+e}function toDecimal(e){return parseInt(e,16)}function colorsToRgb(e){return e.map(hexToRgb)}function hexToRgb(e){var t=String(e).replace(/[^0-9a-f]/gi,``);return t.length<6&&(t=t[0]+t[0]+t[1]+t[1]+t[2]+t[2]),{r:toDecimal(t.substring(0,2)),g:toDecimal(t.substring(2,4)),b:toDecimal(t.substring(4,6))}}function getOrigin(e){var t=prop(e,`origin`,Object);return t.x=prop(t,`x`,Number),t.y=prop(t,`y`,Number),t}function setCanvasWindowSize(e){e.width=document.documentElement.clientWidth,e.height=document.documentElement.clientHeight}function setCanvasRectSize(e){var t=e.getBoundingClientRect();e.width=t.width,e.height=t.height}function getCanvas(e){var t=document.createElement(`canvas`);return t.style.position=`fixed`,t.style.top=`0px`,t.style.left=`0px`,t.style.pointerEvents=`none`,t.style.zIndex=e,t}function ellipse(e,t,n,r,i,a,o,s,c){e.save(),e.translate(t,n),e.rotate(a),e.scale(r,i),e.arc(0,0,1,o,s,c),e.restore()}function randomPhysics(e){var t=e.angle*(Math.PI/180),n=e.spread*(Math.PI/180);return{x:e.x,y:e.y,wobble:Math.random()*10,wobbleSpeed:Math.min(.11,Math.random()*.1+.05),velocity:e.startVelocity*.5+Math.random()*e.startVelocity,angle2D:-t+(.5*n-Math.random()*n),tiltAngle:(Math.random()*.5+.25)*Math.PI,color:e.color,shape:e.shape,tick:0,totalTicks:e.ticks,decay:e.decay,drift:e.drift,random:Math.random()+2,tiltSin:0,tiltCos:0,wobbleX:0,wobbleY:0,gravity:e.gravity*3,ovalScalar:.6,scalar:e.scalar,flat:e.flat}}function updateFetti(e,t){t.x+=Math.cos(t.angle2D)*t.velocity+t.drift,t.y+=Math.sin(t.angle2D)*t.velocity+t.gravity,t.velocity*=t.decay,t.flat?(t.wobble=0,t.wobbleX=t.x+10*t.scalar,t.wobbleY=t.y+10*t.scalar,t.tiltSin=0,t.tiltCos=0,t.random=1):(t.wobble+=t.wobbleSpeed,t.wobbleX=t.x+10*t.scalar*Math.cos(t.wobble),t.wobbleY=t.y+10*t.scalar*Math.sin(t.wobble),t.tiltAngle+=.1,t.tiltSin=Math.sin(t.tiltAngle),t.tiltCos=Math.cos(t.tiltAngle),t.random=Math.random()+2);var n=t.tick++/t.totalTicks,r=t.x+t.random*t.tiltCos,i=t.y+t.random*t.tiltSin,o=t.wobbleX+t.random*t.tiltCos,c=t.wobbleY+t.random*t.tiltSin;if(e.fillStyle=`rgba(`+t.color.r+`, `+t.color.g+`, `+t.color.b+`, `+(1-n)+`)`,e.beginPath(),a&&t.shape.type===`path`&&typeof t.shape.path==`string`&&Array.isArray(t.shape.matrix))e.fill(transformPath2D(t.shape.path,t.shape.matrix,t.x,t.y,Math.abs(o-r)*.1,Math.abs(c-i)*.1,Math.PI/10*t.wobble));else if(t.shape.type===`bitmap`){var l=Math.PI/10*t.wobble,u=Math.abs(o-r)*.1,d=Math.abs(c-i)*.1,f=t.shape.bitmap.width*t.scalar,p=t.shape.bitmap.height*t.scalar,m=new DOMMatrix([Math.cos(l)*u,Math.sin(l)*u,-Math.sin(l)*d,Math.cos(l)*d,t.x,t.y]);m.multiplySelf(new DOMMatrix(t.shape.matrix));var h=e.createPattern(s.transform(t.shape.bitmap),`no-repeat`);h.setTransform(m),e.globalAlpha=1-n,e.fillStyle=h,e.fillRect(t.x-f/2,t.y-p/2,f,p),e.globalAlpha=1}else if(t.shape===`circle`)e.ellipse?e.ellipse(t.x,t.y,Math.abs(o-r)*t.ovalScalar,Math.abs(c-i)*t.ovalScalar,Math.PI/10*t.wobble,0,2*Math.PI):ellipse(e,t.x,t.y,Math.abs(o-r)*t.ovalScalar,Math.abs(c-i)*t.ovalScalar,Math.PI/10*t.wobble,0,2*Math.PI);else if(t.shape===`star`)for(var g=Math.PI/2*3,_=4*t.scalar,v=8*t.scalar,y=t.x,b=t.y,x=5,S=Math.PI/x;x--;)y=t.x+Math.cos(g)*v,b=t.y+Math.sin(g)*v,e.lineTo(y,b),g+=S,y=t.x+Math.cos(g)*_,b=t.y+Math.sin(g)*_,e.lineTo(y,b),g+=S;else e.moveTo(Math.floor(t.x),Math.floor(t.y)),e.lineTo(Math.floor(t.wobbleX),Math.floor(i)),e.lineTo(Math.floor(o),Math.floor(c)),e.lineTo(Math.floor(r),Math.floor(t.wobbleY));return e.closePath(),e.fill(),t.tick<t.totalTicks}function animate(e,t,i,a,o){var l=t.slice(),u=e.getContext(`2d`),d,f,p=promise(function(t){function onDone(){d=f=null,u.clearRect(0,0,a.width,a.height),s.clear(),o(),t()}function update(){n&&!(a.width===r.width&&a.height===r.height)&&(a.width=e.width=r.width,a.height=e.height=r.height),!a.width&&!a.height&&(i(e),a.width=e.width,a.height=e.height),u.clearRect(0,0,a.width,a.height),l=l.filter(function(e){return updateFetti(u,e)}),l.length?d=c.frame(update):onDone()}d=c.frame(update),f=onDone});return{addFettis:function(e){return l=l.concat(e),p},canvas:e,promise:p,reset:function(){d&&c.cancel(d),f&&f()}}}function confettiCannon(t,n){var r=!t,a=!!prop(n||{},`resize`),o=!1,s=prop(n,`disableForReducedMotion`,Boolean),c=i&&prop(n||{},`useWorker`)?l():null,u=r?setCanvasWindowSize:setCanvasRectSize,d=t&&c?!!t.__confetti_initialized:!1,f=typeof matchMedia==`function`&&matchMedia(`(prefers-reduced-motion)`).matches,p;function fireLocal(e,n,r){for(var i=prop(e,`particleCount`,onlyPositiveInt),a=prop(e,`angle`,Number),o=prop(e,`spread`,Number),s=prop(e,`startVelocity`,Number),c=prop(e,`decay`,Number),l=prop(e,`gravity`,Number),d=prop(e,`drift`,Number),f=prop(e,`colors`,colorsToRgb),m=prop(e,`ticks`,Number),h=prop(e,`shapes`),g=prop(e,`scalar`),_=!!prop(e,`flat`),v=getOrigin(e),y=i,b=[],x=t.width*v.x,S=t.height*v.y;y--;)b.push(randomPhysics({x,y:S,angle:a,spread:o,startVelocity:s,color:f[y%f.length],shape:h[randomInt(0,h.length)],ticks:m,decay:c,gravity:l,drift:d,scalar:g,flat:_}));return p?p.addFettis(b):(p=animate(t,b,u,n,r),p.promise)}function fire(n){var i=s||prop(n,`disableForReducedMotion`,Boolean),l=prop(n,`zIndex`,Number);if(i&&f)return promise(function(e){e()});r&&p?t=p.canvas:r&&!t&&(t=getCanvas(l),document.body.appendChild(t)),a&&!d&&u(t);var m={width:t.width,height:t.height};c&&!d&&c.init(t),d=!0,c&&(t.__confetti_initialized=!0);function onResize(){if(c){var e={getBoundingClientRect:function(){if(!r)return t.getBoundingClientRect()}};u(e),c.postMessage({resize:{width:e.width,height:e.height}});return}m.width=m.height=null}function done(){p=null,a&&(o=!1,e.removeEventListener(`resize`,onResize)),r&&t&&(document.body.contains(t)&&document.body.removeChild(t),t=null,d=!1)}return a&&!o&&(o=!0,e.addEventListener(`resize`,onResize,!1)),c?c.fire(n,m,done):fireLocal(n,m,done)}return fire.reset=function(){c&&c.reset(),p&&p.reset()},fire}var d;function getDefaultFire(){return d||(d=confettiCannon(null,{useWorker:!0,resize:!0})),d}function transformPath2D(e,t,n,r,i,a,o){var s=new Path2D(e),c=new Path2D;c.addPath(s,new DOMMatrix(t));var l=new Path2D;return l.addPath(c,new DOMMatrix([Math.cos(o)*i,Math.sin(o)*i,-Math.sin(o)*a,Math.cos(o)*a,n,r])),l}function shapeFromPath(e){if(!a)throw Error(`path confetti are not supported in this browser`);var t,n;typeof e==`string`?t=e:(t=e.path,n=e.matrix);var r=new Path2D(t),i=document.createElement(`canvas`).getContext(`2d`);if(!n){for(var o=1e3,s=o,c=o,l=0,u=0,d,f,p=0;p<o;p+=2)for(var m=0;m<o;m+=2)i.isPointInPath(r,p,m,`nonzero`)&&(s=Math.min(s,p),c=Math.min(c,m),l=Math.max(l,p),u=Math.max(u,m));d=l-s,f=u-c;var h=10,g=Math.min(h/d,h/f);n=[g,0,0,g,-Math.round(d/2+s)*g,-Math.round(f/2+c)*g]}return{type:`path`,path:t,matrix:n}}function shapeFromText(e){var t,n=1,r=`#000000`,i=`"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji", "EmojiOne Color", "Android Emoji", "Twemoji Mozilla", "system emoji", sans-serif`;typeof e==`string`?t=e:(t=e.text,n=`scalar`in e?e.scalar:n,i=`fontFamily`in e?e.fontFamily:i,r=`color`in e?e.color:r);var a=10*n,o=``+a+`px `+i,s=new OffscreenCanvas(a,a),c=s.getContext(`2d`);c.font=o;var l=c.measureText(t),u=Math.ceil(l.actualBoundingBoxRight+l.actualBoundingBoxLeft),d=Math.ceil(l.actualBoundingBoxAscent+l.actualBoundingBoxDescent),f=2,p=l.actualBoundingBoxLeft+f,m=l.actualBoundingBoxAscent+f;u+=f+f,d+=f+f,s=new OffscreenCanvas(u,d),c=s.getContext(`2d`),c.font=o,c.fillStyle=r,c.fillText(t,p,m);var h=1/n;return{type:`bitmap`,bitmap:s.transferToImageBitmap(),matrix:[h,0,0,h,-u*h/2,-d*h/2]}}t.exports=function(){return getDefaultFire().apply(this,arguments)},t.exports.reset=function(){getDefaultFire().reset()},t.exports.create=confettiCannon,t.exports.shapeFromPath=shapeFromPath,t.exports.shapeFromText=shapeFromText})((function(){return typeof window<`u`?window:typeof self<`u`?self:this||{}})(),y,!1);var b=y.exports;y.exports.create;var x=s({name:`UserSettings`,components:{UserTagPreferences:g},props:{username:{type:String,required:!0}},setup(){let e=m(),t=p();return{configStore:e,darkMode:e.config.darkMode,likesConfetti:e.config.likesConfetti,route:t,isNewUser:t.path.endsWith(`new`)}},data(){return{u:{},configLanguages:[{name:`English`,code:`en`},{name:`French`,code:`fr`}],selectedLanguages:[],registeredCourses:[],selectedCourseId:``}},async created(){this.u=await h(),this.configLanguages.forEach(e=>{console.log(`afweatifvwzeatfvwzeta`+e.name)}),await this.loadRegisteredCourses()},methods:{updateDark(){this.configStore.updateDarkMode(this.configStore.config.darkMode)},updateConfetti(){this.configStore.updateLikesConfetti(this.configStore.config.likesConfetti),this.configStore.config.likesConfetti&&b({origin:{x:.5,y:1}})},async loadRegisteredCourses(){try{let e=await this.u.getActiveCourses(),t=_();this.registeredCourses=(await Promise.all(e.map(async e=>{try{return await t.getCourseDB(e.courseID).getCourseConfig()}catch{return{courseID:e.courseID,name:e.courseID}}}))).filter(Boolean)}catch(e){console.error(`Failed to load registered courses:`,e)}},onPreferencesSaved(){console.log(`Preferences saved for course:`,this.selectedCourseId)}}}),S={class:`text-subtitle-1`};function _sfc_render(s,p,m,h,g,_){let v=i(`v-alert`),y=i(`v-checkbox`),b=i(`v-divider`),x=i(`v-list-item`),C=i(`v-select`),w=i(`user-tag-preferences`);return r(),u(`div`,S,[s.isNewUser?(r(),d(v,{key:0,type:`success`,class:`text-subtitle-1`,variant:`tonal`,"prepend-icon":`mdi-check`},{default:e(()=>[o(` Welcome, `+c(s.username)+`! Please take a moment to look through these settings: `,1)]),_:1})):f(``,!0),p[3]||(p[3]=l(`h1`,{class:`text-h3`},`Account Settings`,-1)),p[4]||(p[4]=l(`h2`,{class:`text-h4`},`General:`,-1)),t(y,{modelValue:s.configStore.config.likesConfetti,"onUpdate:modelValue":[p[0]||(p[0]=e=>s.configStore.config.likesConfetti=e),s.updateConfetti],label:`I like confetti`},null,8,[`modelValue`,`onUpdate:modelValue`]),t(y,{modelValue:s.configStore.config.darkMode,"onUpdate:modelValue":[p[1]||(p[1]=e=>s.configStore.config.darkMode=e),s.updateDark],label:`I like the dark`},null,8,[`modelValue`,`onUpdate:modelValue`]),t(b,{class:`my-6`}),p[5]||(p[5]=l(`h2`,{class:`text-h4 mb-4`},`Learning Preferences:`,-1)),p[6]||(p[6]=l(`p`,{class:`text-body-2 text-medium-emphasis mb-4`},`Customize how content is presented in your registered courses.`,-1)),t(C,{modelValue:s.selectedCourseId,"onUpdate:modelValue":p[2]||(p[2]=e=>s.selectedCourseId=e),items:s.registeredCourses,"item-title":`name`,"item-value":`courseID`,label:`Select a course`,variant:`outlined`,density:`comfortable`,class:`mb-4`,style:{"max-width":`400px`}},{item:e(({props:r,item:i})=>[t(x,a(n(r)),{subtitle:e(()=>[o(c(i.raw.courseID),1)]),_:2},1040)]),_:1},8,[`modelValue`,`items`]),s.selectedCourseId?(r(),d(w,{key:1,"course-id":s.selectedCourseId,onPreferencesSaved:s.onPreferencesSaved},null,8,[`course-id`,`onPreferencesSaved`])):f(``,!0)])}var C=v(x,[[`render`,_sfc_render]]);export{C as default};
|
|
3
|
+
//# sourceMappingURL=User-1-_6oW2W.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"User-1-_6oW2W.js","names":[],"sources":["../../../../node_modules/canvas-confetti/dist/confetti.module.mjs","../../src/views/User.vue","../../src/views/User.vue"],"sourcesContent":["// canvas-confetti v1.9.3 built on 2024-04-30T22:19:17.794Z\nvar module = {};\n\n// source content\n/* globals Map */\n\n(function main(global, module, isWorker, workerSize) {\n var canUseWorker = !!(\n global.Worker &&\n global.Blob &&\n global.Promise &&\n global.OffscreenCanvas &&\n global.OffscreenCanvasRenderingContext2D &&\n global.HTMLCanvasElement &&\n global.HTMLCanvasElement.prototype.transferControlToOffscreen &&\n global.URL &&\n global.URL.createObjectURL);\n\n var canUsePaths = typeof Path2D === 'function' && typeof DOMMatrix === 'function';\n var canDrawBitmap = (function () {\n // this mostly supports ssr\n if (!global.OffscreenCanvas) {\n return false;\n }\n\n var canvas = new OffscreenCanvas(1, 1);\n var ctx = canvas.getContext('2d');\n ctx.fillRect(0, 0, 1, 1);\n var bitmap = canvas.transferToImageBitmap();\n\n try {\n ctx.createPattern(bitmap, 'no-repeat');\n } catch (e) {\n return false;\n }\n\n return true;\n })();\n\n function noop() {}\n\n // create a promise if it exists, otherwise, just\n // call the function directly\n function promise(func) {\n var ModulePromise = module.exports.Promise;\n var Prom = ModulePromise !== void 0 ? ModulePromise : global.Promise;\n\n if (typeof Prom === 'function') {\n return new Prom(func);\n }\n\n func(noop, noop);\n\n return null;\n }\n\n var bitmapMapper = (function (skipTransform, map) {\n // see https://github.com/catdad/canvas-confetti/issues/209\n // creating canvases is actually pretty expensive, so we should create a\n // 1:1 map for bitmap:canvas, so that we can animate the confetti in\n // a performant manner, but also not store them forever so that we don't\n // have a memory leak\n return {\n transform: function(bitmap) {\n if (skipTransform) {\n return bitmap;\n }\n\n if (map.has(bitmap)) {\n return map.get(bitmap);\n }\n\n var canvas = new OffscreenCanvas(bitmap.width, bitmap.height);\n var ctx = canvas.getContext('2d');\n ctx.drawImage(bitmap, 0, 0);\n\n map.set(bitmap, canvas);\n\n return canvas;\n },\n clear: function () {\n map.clear();\n }\n };\n })(canDrawBitmap, new Map());\n\n var raf = (function () {\n var TIME = Math.floor(1000 / 60);\n var frame, cancel;\n var frames = {};\n var lastFrameTime = 0;\n\n if (typeof requestAnimationFrame === 'function' && typeof cancelAnimationFrame === 'function') {\n frame = function (cb) {\n var id = Math.random();\n\n frames[id] = requestAnimationFrame(function onFrame(time) {\n if (lastFrameTime === time || lastFrameTime + TIME - 1 < time) {\n lastFrameTime = time;\n delete frames[id];\n\n cb();\n } else {\n frames[id] = requestAnimationFrame(onFrame);\n }\n });\n\n return id;\n };\n cancel = function (id) {\n if (frames[id]) {\n cancelAnimationFrame(frames[id]);\n }\n };\n } else {\n frame = function (cb) {\n return setTimeout(cb, TIME);\n };\n cancel = function (timer) {\n return clearTimeout(timer);\n };\n }\n\n return { frame: frame, cancel: cancel };\n }());\n\n var getWorker = (function () {\n var worker;\n var prom;\n var resolves = {};\n\n function decorate(worker) {\n function execute(options, callback) {\n worker.postMessage({ options: options || {}, callback: callback });\n }\n worker.init = function initWorker(canvas) {\n var offscreen = canvas.transferControlToOffscreen();\n worker.postMessage({ canvas: offscreen }, [offscreen]);\n };\n\n worker.fire = function fireWorker(options, size, done) {\n if (prom) {\n execute(options, null);\n return prom;\n }\n\n var id = Math.random().toString(36).slice(2);\n\n prom = promise(function (resolve) {\n function workerDone(msg) {\n if (msg.data.callback !== id) {\n return;\n }\n\n delete resolves[id];\n worker.removeEventListener('message', workerDone);\n\n prom = null;\n\n bitmapMapper.clear();\n\n done();\n resolve();\n }\n\n worker.addEventListener('message', workerDone);\n execute(options, id);\n\n resolves[id] = workerDone.bind(null, { data: { callback: id }});\n });\n\n return prom;\n };\n\n worker.reset = function resetWorker() {\n worker.postMessage({ reset: true });\n\n for (var id in resolves) {\n resolves[id]();\n delete resolves[id];\n }\n };\n }\n\n return function () {\n if (worker) {\n return worker;\n }\n\n if (!isWorker && canUseWorker) {\n var code = [\n 'var CONFETTI, SIZE = {}, module = {};',\n '(' + main.toString() + ')(this, module, true, SIZE);',\n 'onmessage = function(msg) {',\n ' if (msg.data.options) {',\n ' CONFETTI(msg.data.options).then(function () {',\n ' if (msg.data.callback) {',\n ' postMessage({ callback: msg.data.callback });',\n ' }',\n ' });',\n ' } else if (msg.data.reset) {',\n ' CONFETTI && CONFETTI.reset();',\n ' } else if (msg.data.resize) {',\n ' SIZE.width = msg.data.resize.width;',\n ' SIZE.height = msg.data.resize.height;',\n ' } else if (msg.data.canvas) {',\n ' SIZE.width = msg.data.canvas.width;',\n ' SIZE.height = msg.data.canvas.height;',\n ' CONFETTI = module.exports.create(msg.data.canvas);',\n ' }',\n '}',\n ].join('\\n');\n try {\n worker = new Worker(URL.createObjectURL(new Blob([code])));\n } catch (e) {\n // eslint-disable-next-line no-console\n typeof console !== undefined && typeof console.warn === 'function' ? console.warn('🎊 Could not load worker', e) : null;\n\n return null;\n }\n\n decorate(worker);\n }\n\n return worker;\n };\n })();\n\n var defaults = {\n particleCount: 50,\n angle: 90,\n spread: 45,\n startVelocity: 45,\n decay: 0.9,\n gravity: 1,\n drift: 0,\n ticks: 200,\n x: 0.5,\n y: 0.5,\n shapes: ['square', 'circle'],\n zIndex: 100,\n colors: [\n '#26ccff',\n '#a25afd',\n '#ff5e7e',\n '#88ff5a',\n '#fcff42',\n '#ffa62d',\n '#ff36ff'\n ],\n // probably should be true, but back-compat\n disableForReducedMotion: false,\n scalar: 1\n };\n\n function convert(val, transform) {\n return transform ? transform(val) : val;\n }\n\n function isOk(val) {\n return !(val === null || val === undefined);\n }\n\n function prop(options, name, transform) {\n return convert(\n options && isOk(options[name]) ? options[name] : defaults[name],\n transform\n );\n }\n\n function onlyPositiveInt(number){\n return number < 0 ? 0 : Math.floor(number);\n }\n\n function randomInt(min, max) {\n // [min, max)\n return Math.floor(Math.random() * (max - min)) + min;\n }\n\n function toDecimal(str) {\n return parseInt(str, 16);\n }\n\n function colorsToRgb(colors) {\n return colors.map(hexToRgb);\n }\n\n function hexToRgb(str) {\n var val = String(str).replace(/[^0-9a-f]/gi, '');\n\n if (val.length < 6) {\n val = val[0]+val[0]+val[1]+val[1]+val[2]+val[2];\n }\n\n return {\n r: toDecimal(val.substring(0,2)),\n g: toDecimal(val.substring(2,4)),\n b: toDecimal(val.substring(4,6))\n };\n }\n\n function getOrigin(options) {\n var origin = prop(options, 'origin', Object);\n origin.x = prop(origin, 'x', Number);\n origin.y = prop(origin, 'y', Number);\n\n return origin;\n }\n\n function setCanvasWindowSize(canvas) {\n canvas.width = document.documentElement.clientWidth;\n canvas.height = document.documentElement.clientHeight;\n }\n\n function setCanvasRectSize(canvas) {\n var rect = canvas.getBoundingClientRect();\n canvas.width = rect.width;\n canvas.height = rect.height;\n }\n\n function getCanvas(zIndex) {\n var canvas = document.createElement('canvas');\n\n canvas.style.position = 'fixed';\n canvas.style.top = '0px';\n canvas.style.left = '0px';\n canvas.style.pointerEvents = 'none';\n canvas.style.zIndex = zIndex;\n\n return canvas;\n }\n\n function ellipse(context, x, y, radiusX, radiusY, rotation, startAngle, endAngle, antiClockwise) {\n context.save();\n context.translate(x, y);\n context.rotate(rotation);\n context.scale(radiusX, radiusY);\n context.arc(0, 0, 1, startAngle, endAngle, antiClockwise);\n context.restore();\n }\n\n function randomPhysics(opts) {\n var radAngle = opts.angle * (Math.PI / 180);\n var radSpread = opts.spread * (Math.PI / 180);\n\n return {\n x: opts.x,\n y: opts.y,\n wobble: Math.random() * 10,\n wobbleSpeed: Math.min(0.11, Math.random() * 0.1 + 0.05),\n velocity: (opts.startVelocity * 0.5) + (Math.random() * opts.startVelocity),\n angle2D: -radAngle + ((0.5 * radSpread) - (Math.random() * radSpread)),\n tiltAngle: (Math.random() * (0.75 - 0.25) + 0.25) * Math.PI,\n color: opts.color,\n shape: opts.shape,\n tick: 0,\n totalTicks: opts.ticks,\n decay: opts.decay,\n drift: opts.drift,\n random: Math.random() + 2,\n tiltSin: 0,\n tiltCos: 0,\n wobbleX: 0,\n wobbleY: 0,\n gravity: opts.gravity * 3,\n ovalScalar: 0.6,\n scalar: opts.scalar,\n flat: opts.flat\n };\n }\n\n function updateFetti(context, fetti) {\n fetti.x += Math.cos(fetti.angle2D) * fetti.velocity + fetti.drift;\n fetti.y += Math.sin(fetti.angle2D) * fetti.velocity + fetti.gravity;\n fetti.velocity *= fetti.decay;\n\n if (fetti.flat) {\n fetti.wobble = 0;\n fetti.wobbleX = fetti.x + (10 * fetti.scalar);\n fetti.wobbleY = fetti.y + (10 * fetti.scalar);\n\n fetti.tiltSin = 0;\n fetti.tiltCos = 0;\n fetti.random = 1;\n } else {\n fetti.wobble += fetti.wobbleSpeed;\n fetti.wobbleX = fetti.x + ((10 * fetti.scalar) * Math.cos(fetti.wobble));\n fetti.wobbleY = fetti.y + ((10 * fetti.scalar) * Math.sin(fetti.wobble));\n\n fetti.tiltAngle += 0.1;\n fetti.tiltSin = Math.sin(fetti.tiltAngle);\n fetti.tiltCos = Math.cos(fetti.tiltAngle);\n fetti.random = Math.random() + 2;\n }\n\n var progress = (fetti.tick++) / fetti.totalTicks;\n\n var x1 = fetti.x + (fetti.random * fetti.tiltCos);\n var y1 = fetti.y + (fetti.random * fetti.tiltSin);\n var x2 = fetti.wobbleX + (fetti.random * fetti.tiltCos);\n var y2 = fetti.wobbleY + (fetti.random * fetti.tiltSin);\n\n context.fillStyle = 'rgba(' + fetti.color.r + ', ' + fetti.color.g + ', ' + fetti.color.b + ', ' + (1 - progress) + ')';\n\n context.beginPath();\n\n if (canUsePaths && fetti.shape.type === 'path' && typeof fetti.shape.path === 'string' && Array.isArray(fetti.shape.matrix)) {\n context.fill(transformPath2D(\n fetti.shape.path,\n fetti.shape.matrix,\n fetti.x,\n fetti.y,\n Math.abs(x2 - x1) * 0.1,\n Math.abs(y2 - y1) * 0.1,\n Math.PI / 10 * fetti.wobble\n ));\n } else if (fetti.shape.type === 'bitmap') {\n var rotation = Math.PI / 10 * fetti.wobble;\n var scaleX = Math.abs(x2 - x1) * 0.1;\n var scaleY = Math.abs(y2 - y1) * 0.1;\n var width = fetti.shape.bitmap.width * fetti.scalar;\n var height = fetti.shape.bitmap.height * fetti.scalar;\n\n var matrix = new DOMMatrix([\n Math.cos(rotation) * scaleX,\n Math.sin(rotation) * scaleX,\n -Math.sin(rotation) * scaleY,\n Math.cos(rotation) * scaleY,\n fetti.x,\n fetti.y\n ]);\n\n // apply the transform matrix from the confetti shape\n matrix.multiplySelf(new DOMMatrix(fetti.shape.matrix));\n\n var pattern = context.createPattern(bitmapMapper.transform(fetti.shape.bitmap), 'no-repeat');\n pattern.setTransform(matrix);\n\n context.globalAlpha = (1 - progress);\n context.fillStyle = pattern;\n context.fillRect(\n fetti.x - (width / 2),\n fetti.y - (height / 2),\n width,\n height\n );\n context.globalAlpha = 1;\n } else if (fetti.shape === 'circle') {\n context.ellipse ?\n context.ellipse(fetti.x, fetti.y, Math.abs(x2 - x1) * fetti.ovalScalar, Math.abs(y2 - y1) * fetti.ovalScalar, Math.PI / 10 * fetti.wobble, 0, 2 * Math.PI) :\n ellipse(context, fetti.x, fetti.y, Math.abs(x2 - x1) * fetti.ovalScalar, Math.abs(y2 - y1) * fetti.ovalScalar, Math.PI / 10 * fetti.wobble, 0, 2 * Math.PI);\n } else if (fetti.shape === 'star') {\n var rot = Math.PI / 2 * 3;\n var innerRadius = 4 * fetti.scalar;\n var outerRadius = 8 * fetti.scalar;\n var x = fetti.x;\n var y = fetti.y;\n var spikes = 5;\n var step = Math.PI / spikes;\n\n while (spikes--) {\n x = fetti.x + Math.cos(rot) * outerRadius;\n y = fetti.y + Math.sin(rot) * outerRadius;\n context.lineTo(x, y);\n rot += step;\n\n x = fetti.x + Math.cos(rot) * innerRadius;\n y = fetti.y + Math.sin(rot) * innerRadius;\n context.lineTo(x, y);\n rot += step;\n }\n } else {\n context.moveTo(Math.floor(fetti.x), Math.floor(fetti.y));\n context.lineTo(Math.floor(fetti.wobbleX), Math.floor(y1));\n context.lineTo(Math.floor(x2), Math.floor(y2));\n context.lineTo(Math.floor(x1), Math.floor(fetti.wobbleY));\n }\n\n context.closePath();\n context.fill();\n\n return fetti.tick < fetti.totalTicks;\n }\n\n function animate(canvas, fettis, resizer, size, done) {\n var animatingFettis = fettis.slice();\n var context = canvas.getContext('2d');\n var animationFrame;\n var destroy;\n\n var prom = promise(function (resolve) {\n function onDone() {\n animationFrame = destroy = null;\n\n context.clearRect(0, 0, size.width, size.height);\n bitmapMapper.clear();\n\n done();\n resolve();\n }\n\n function update() {\n if (isWorker && !(size.width === workerSize.width && size.height === workerSize.height)) {\n size.width = canvas.width = workerSize.width;\n size.height = canvas.height = workerSize.height;\n }\n\n if (!size.width && !size.height) {\n resizer(canvas);\n size.width = canvas.width;\n size.height = canvas.height;\n }\n\n context.clearRect(0, 0, size.width, size.height);\n\n animatingFettis = animatingFettis.filter(function (fetti) {\n return updateFetti(context, fetti);\n });\n\n if (animatingFettis.length) {\n animationFrame = raf.frame(update);\n } else {\n onDone();\n }\n }\n\n animationFrame = raf.frame(update);\n destroy = onDone;\n });\n\n return {\n addFettis: function (fettis) {\n animatingFettis = animatingFettis.concat(fettis);\n\n return prom;\n },\n canvas: canvas,\n promise: prom,\n reset: function () {\n if (animationFrame) {\n raf.cancel(animationFrame);\n }\n\n if (destroy) {\n destroy();\n }\n }\n };\n }\n\n function confettiCannon(canvas, globalOpts) {\n var isLibCanvas = !canvas;\n var allowResize = !!prop(globalOpts || {}, 'resize');\n var hasResizeEventRegistered = false;\n var globalDisableForReducedMotion = prop(globalOpts, 'disableForReducedMotion', Boolean);\n var shouldUseWorker = canUseWorker && !!prop(globalOpts || {}, 'useWorker');\n var worker = shouldUseWorker ? getWorker() : null;\n var resizer = isLibCanvas ? setCanvasWindowSize : setCanvasRectSize;\n var initialized = (canvas && worker) ? !!canvas.__confetti_initialized : false;\n var preferLessMotion = typeof matchMedia === 'function' && matchMedia('(prefers-reduced-motion)').matches;\n var animationObj;\n\n function fireLocal(options, size, done) {\n var particleCount = prop(options, 'particleCount', onlyPositiveInt);\n var angle = prop(options, 'angle', Number);\n var spread = prop(options, 'spread', Number);\n var startVelocity = prop(options, 'startVelocity', Number);\n var decay = prop(options, 'decay', Number);\n var gravity = prop(options, 'gravity', Number);\n var drift = prop(options, 'drift', Number);\n var colors = prop(options, 'colors', colorsToRgb);\n var ticks = prop(options, 'ticks', Number);\n var shapes = prop(options, 'shapes');\n var scalar = prop(options, 'scalar');\n var flat = !!prop(options, 'flat');\n var origin = getOrigin(options);\n\n var temp = particleCount;\n var fettis = [];\n\n var startX = canvas.width * origin.x;\n var startY = canvas.height * origin.y;\n\n while (temp--) {\n fettis.push(\n randomPhysics({\n x: startX,\n y: startY,\n angle: angle,\n spread: spread,\n startVelocity: startVelocity,\n color: colors[temp % colors.length],\n shape: shapes[randomInt(0, shapes.length)],\n ticks: ticks,\n decay: decay,\n gravity: gravity,\n drift: drift,\n scalar: scalar,\n flat: flat\n })\n );\n }\n\n // if we have a previous canvas already animating,\n // add to it\n if (animationObj) {\n return animationObj.addFettis(fettis);\n }\n\n animationObj = animate(canvas, fettis, resizer, size , done);\n\n return animationObj.promise;\n }\n\n function fire(options) {\n var disableForReducedMotion = globalDisableForReducedMotion || prop(options, 'disableForReducedMotion', Boolean);\n var zIndex = prop(options, 'zIndex', Number);\n\n if (disableForReducedMotion && preferLessMotion) {\n return promise(function (resolve) {\n resolve();\n });\n }\n\n if (isLibCanvas && animationObj) {\n // use existing canvas from in-progress animation\n canvas = animationObj.canvas;\n } else if (isLibCanvas && !canvas) {\n // create and initialize a new canvas\n canvas = getCanvas(zIndex);\n document.body.appendChild(canvas);\n }\n\n if (allowResize && !initialized) {\n // initialize the size of a user-supplied canvas\n resizer(canvas);\n }\n\n var size = {\n width: canvas.width,\n height: canvas.height\n };\n\n if (worker && !initialized) {\n worker.init(canvas);\n }\n\n initialized = true;\n\n if (worker) {\n canvas.__confetti_initialized = true;\n }\n\n function onResize() {\n if (worker) {\n // TODO this really shouldn't be immediate, because it is expensive\n var obj = {\n getBoundingClientRect: function () {\n if (!isLibCanvas) {\n return canvas.getBoundingClientRect();\n }\n }\n };\n\n resizer(obj);\n\n worker.postMessage({\n resize: {\n width: obj.width,\n height: obj.height\n }\n });\n return;\n }\n\n // don't actually query the size here, since this\n // can execute frequently and rapidly\n size.width = size.height = null;\n }\n\n function done() {\n animationObj = null;\n\n if (allowResize) {\n hasResizeEventRegistered = false;\n global.removeEventListener('resize', onResize);\n }\n\n if (isLibCanvas && canvas) {\n if (document.body.contains(canvas)) {\n document.body.removeChild(canvas); \n }\n canvas = null;\n initialized = false;\n }\n }\n\n if (allowResize && !hasResizeEventRegistered) {\n hasResizeEventRegistered = true;\n global.addEventListener('resize', onResize, false);\n }\n\n if (worker) {\n return worker.fire(options, size, done);\n }\n\n return fireLocal(options, size, done);\n }\n\n fire.reset = function () {\n if (worker) {\n worker.reset();\n }\n\n if (animationObj) {\n animationObj.reset();\n }\n };\n\n return fire;\n }\n\n // Make default export lazy to defer worker creation until called.\n var defaultFire;\n function getDefaultFire() {\n if (!defaultFire) {\n defaultFire = confettiCannon(null, { useWorker: true, resize: true });\n }\n return defaultFire;\n }\n\n function transformPath2D(pathString, pathMatrix, x, y, scaleX, scaleY, rotation) {\n var path2d = new Path2D(pathString);\n\n var t1 = new Path2D();\n t1.addPath(path2d, new DOMMatrix(pathMatrix));\n\n var t2 = new Path2D();\n // see https://developer.mozilla.org/en-US/docs/Web/API/DOMMatrix/DOMMatrix\n t2.addPath(t1, new DOMMatrix([\n Math.cos(rotation) * scaleX,\n Math.sin(rotation) * scaleX,\n -Math.sin(rotation) * scaleY,\n Math.cos(rotation) * scaleY,\n x,\n y\n ]));\n\n return t2;\n }\n\n function shapeFromPath(pathData) {\n if (!canUsePaths) {\n throw new Error('path confetti are not supported in this browser');\n }\n\n var path, matrix;\n\n if (typeof pathData === 'string') {\n path = pathData;\n } else {\n path = pathData.path;\n matrix = pathData.matrix;\n }\n\n var path2d = new Path2D(path);\n var tempCanvas = document.createElement('canvas');\n var tempCtx = tempCanvas.getContext('2d');\n\n if (!matrix) {\n // attempt to figure out the width of the path, up to 1000x1000\n var maxSize = 1000;\n var minX = maxSize;\n var minY = maxSize;\n var maxX = 0;\n var maxY = 0;\n var width, height;\n\n // do some line skipping... this is faster than checking\n // every pixel and will be mostly still correct\n for (var x = 0; x < maxSize; x += 2) {\n for (var y = 0; y < maxSize; y += 2) {\n if (tempCtx.isPointInPath(path2d, x, y, 'nonzero')) {\n minX = Math.min(minX, x);\n minY = Math.min(minY, y);\n maxX = Math.max(maxX, x);\n maxY = Math.max(maxY, y);\n }\n }\n }\n\n width = maxX - minX;\n height = maxY - minY;\n\n var maxDesiredSize = 10;\n var scale = Math.min(maxDesiredSize/width, maxDesiredSize/height);\n\n matrix = [\n scale, 0, 0, scale,\n -Math.round((width/2) + minX) * scale,\n -Math.round((height/2) + minY) * scale\n ];\n }\n\n return {\n type: 'path',\n path: path,\n matrix: matrix\n };\n }\n\n function shapeFromText(textData) {\n var text,\n scalar = 1,\n color = '#000000',\n // see https://nolanlawson.com/2022/04/08/the-struggle-of-using-native-emoji-on-the-web/\n fontFamily = '\"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\", \"EmojiOne Color\", \"Android Emoji\", \"Twemoji Mozilla\", \"system emoji\", sans-serif';\n\n if (typeof textData === 'string') {\n text = textData;\n } else {\n text = textData.text;\n scalar = 'scalar' in textData ? textData.scalar : scalar;\n fontFamily = 'fontFamily' in textData ? textData.fontFamily : fontFamily;\n color = 'color' in textData ? textData.color : color;\n }\n\n // all other confetti are 10 pixels,\n // so this pixel size is the de-facto 100% scale confetti\n var fontSize = 10 * scalar;\n var font = '' + fontSize + 'px ' + fontFamily;\n\n var canvas = new OffscreenCanvas(fontSize, fontSize);\n var ctx = canvas.getContext('2d');\n\n ctx.font = font;\n var size = ctx.measureText(text);\n var width = Math.ceil(size.actualBoundingBoxRight + size.actualBoundingBoxLeft);\n var height = Math.ceil(size.actualBoundingBoxAscent + size.actualBoundingBoxDescent);\n\n var padding = 2;\n var x = size.actualBoundingBoxLeft + padding;\n var y = size.actualBoundingBoxAscent + padding;\n width += padding + padding;\n height += padding + padding;\n\n canvas = new OffscreenCanvas(width, height);\n ctx = canvas.getContext('2d');\n ctx.font = font;\n ctx.fillStyle = color;\n\n ctx.fillText(text, x, y);\n\n var scale = 1 / scalar;\n\n return {\n type: 'bitmap',\n // TODO these probably need to be transfered for workers\n bitmap: canvas.transferToImageBitmap(),\n matrix: [scale, 0, 0, scale, -width * scale / 2, -height * scale / 2]\n };\n }\n\n module.exports = function() {\n return getDefaultFire().apply(this, arguments);\n };\n module.exports.reset = function() {\n getDefaultFire().reset();\n };\n module.exports.create = confettiCannon;\n module.exports.shapeFromPath = shapeFromPath;\n module.exports.shapeFromText = shapeFromText;\n}((function () {\n if (typeof window !== 'undefined') {\n return window;\n }\n\n if (typeof self !== 'undefined') {\n return self;\n }\n\n return this || {};\n})(), module, false));\n\n// end source content\n\nexport default module.exports;\nexport var create = module.exports.create;\n","<template>\n <div class=\"text-subtitle-1\">\n <v-alert v-if=\"isNewUser\" type=\"success\" class=\"text-subtitle-1\" variant=\"tonal\" :prepend-icon=\"'mdi-check'\">\n Welcome, {{ username }}! Please take a moment to look through these settings:\n </v-alert>\n\n <h1 class=\"text-h3\">Account Settings</h1>\n <h2 class=\"text-h4\">General:</h2>\n\n <v-checkbox\n v-model=\"configStore.config.likesConfetti\"\n label=\"I like confetti\"\n @update:model-value=\"updateConfetti\"\n />\n <v-checkbox v-model=\"configStore.config.darkMode\" label=\"I like the dark\" @update:model-value=\"updateDark\" />\n <!-- <h2 class=\"text-h4\">Languages:</h2>\n I am near-fluent or better in the following languages:\n {{ selectedLanguages.toString() }}\n <v-checkbox\n v-for=\"(language, i) in configLanguages\"\n :key=\"i\"\n :label=\"language.name\"\n :value=\"language.code\"\n v-model=\"selectedLanguages\"\n @update:model-value=\"updateLanguage\"\n /> -->\n\n <v-divider class=\"my-6\" />\n\n <h2 class=\"text-h4 mb-4\">Learning Preferences:</h2>\n <p class=\"text-body-2 text-medium-emphasis mb-4\">Customize how content is presented in your registered courses.</p>\n\n <v-select\n v-model=\"selectedCourseId\"\n :items=\"registeredCourses\"\n item-title=\"name\"\n item-value=\"courseID\"\n label=\"Select a course\"\n variant=\"outlined\"\n density=\"comfortable\"\n class=\"mb-4\"\n style=\"max-width: 400px\"\n >\n <template #item=\"{ props, item }\">\n <v-list-item v-bind=\"props\">\n <template #subtitle>\n {{ item.raw.courseID }}\n </template>\n </v-list-item>\n </template>\n </v-select>\n\n <user-tag-preferences\n v-if=\"selectedCourseId\"\n :course-id=\"selectedCourseId\"\n @preferences-saved=\"onPreferencesSaved\"\n />\n </div>\n</template>\n\n<script lang=\"ts\">\nimport { getCurrentUser, useConfigStore, UserTagPreferences } from '@vue-skuilder/common-ui';\nimport { UserDBInterface, getDataLayer } from '@vue-skuilder/db';\nimport { CourseConfig } from '@vue-skuilder/common';\nimport confetti from 'canvas-confetti';\nimport { defineComponent, PropType } from 'vue';\nimport { useRoute } from 'vue-router';\n\ninterface Language {\n name: string;\n code: string;\n}\n\nexport default defineComponent({\n name: 'UserSettings',\n\n components: {\n UserTagPreferences,\n },\n\n props: {\n username: {\n type: String as PropType<string>,\n required: true,\n },\n },\n\n setup() {\n const configStore = useConfigStore();\n const route = useRoute();\n\n const darkMode = configStore.config.darkMode;\n const likesConfetti = configStore.config.likesConfetti;\n\n const isNewUser = route.path.endsWith('new');\n\n return { configStore, darkMode, likesConfetti, route, isNewUser };\n },\n\n data() {\n return {\n u: {} as UserDBInterface,\n configLanguages: [\n {\n name: 'English',\n code: 'en',\n },\n {\n name: 'French',\n code: 'fr',\n },\n ] as Language[],\n selectedLanguages: [] as string[],\n registeredCourses: [] as CourseConfig[],\n selectedCourseId: '' as string,\n };\n },\n\n async created() {\n this.u = await getCurrentUser();\n this.configLanguages.forEach((l) => {\n console.log(`afweatifvwzeatfvwzeta` + l.name);\n });\n\n // Load registered courses for preferences selector\n await this.loadRegisteredCourses();\n },\n\n methods: {\n updateDark(): void {\n this.configStore.updateDarkMode(this.configStore.config.darkMode);\n },\n\n updateConfetti(): void {\n this.configStore.updateLikesConfetti(this.configStore.config.likesConfetti);\n\n if (this.configStore.config.likesConfetti) {\n confetti({\n origin: {\n x: 0.5,\n y: 1,\n },\n });\n }\n },\n\n async loadRegisteredCourses(): Promise<void> {\n try {\n const activeCourses = await this.u.getActiveCourses();\n const dataLayer = getDataLayer();\n\n // Fetch course configs for display names\n const courseConfigs = await Promise.all(\n activeCourses.map(async (reg) => {\n try {\n const courseDB = dataLayer.getCourseDB(reg.courseID);\n return await courseDB.getCourseConfig();\n } catch {\n // Return minimal config if fetch fails\n return { courseID: reg.courseID, name: reg.courseID } as CourseConfig;\n }\n })\n );\n\n this.registeredCourses = courseConfigs.filter(Boolean);\n } catch (e) {\n console.error('Failed to load registered courses:', e);\n }\n },\n\n onPreferencesSaved(): void {\n // Could show a snackbar or other feedback here\n console.log('Preferences saved for course:', this.selectedCourseId);\n },\n },\n});\n</script>\n\n<style scoped></style>\n","<template>\n <div class=\"text-subtitle-1\">\n <v-alert v-if=\"isNewUser\" type=\"success\" class=\"text-subtitle-1\" variant=\"tonal\" :prepend-icon=\"'mdi-check'\">\n Welcome, {{ username }}! Please take a moment to look through these settings:\n </v-alert>\n\n <h1 class=\"text-h3\">Account Settings</h1>\n <h2 class=\"text-h4\">General:</h2>\n\n <v-checkbox\n v-model=\"configStore.config.likesConfetti\"\n label=\"I like confetti\"\n @update:model-value=\"updateConfetti\"\n />\n <v-checkbox v-model=\"configStore.config.darkMode\" label=\"I like the dark\" @update:model-value=\"updateDark\" />\n <!-- <h2 class=\"text-h4\">Languages:</h2>\n I am near-fluent or better in the following languages:\n {{ selectedLanguages.toString() }}\n <v-checkbox\n v-for=\"(language, i) in configLanguages\"\n :key=\"i\"\n :label=\"language.name\"\n :value=\"language.code\"\n v-model=\"selectedLanguages\"\n @update:model-value=\"updateLanguage\"\n /> -->\n\n <v-divider class=\"my-6\" />\n\n <h2 class=\"text-h4 mb-4\">Learning Preferences:</h2>\n <p class=\"text-body-2 text-medium-emphasis mb-4\">Customize how content is presented in your registered courses.</p>\n\n <v-select\n v-model=\"selectedCourseId\"\n :items=\"registeredCourses\"\n item-title=\"name\"\n item-value=\"courseID\"\n label=\"Select a course\"\n variant=\"outlined\"\n density=\"comfortable\"\n class=\"mb-4\"\n style=\"max-width: 400px\"\n >\n <template #item=\"{ props, item }\">\n <v-list-item v-bind=\"props\">\n <template #subtitle>\n {{ item.raw.courseID }}\n </template>\n </v-list-item>\n </template>\n </v-select>\n\n <user-tag-preferences\n v-if=\"selectedCourseId\"\n :course-id=\"selectedCourseId\"\n @preferences-saved=\"onPreferencesSaved\"\n />\n </div>\n</template>\n\n<script lang=\"ts\">\nimport { getCurrentUser, useConfigStore, UserTagPreferences } from '@vue-skuilder/common-ui';\nimport { UserDBInterface, getDataLayer } from '@vue-skuilder/db';\nimport { CourseConfig } from '@vue-skuilder/common';\nimport confetti from 'canvas-confetti';\nimport { defineComponent, PropType } from 'vue';\nimport { useRoute } from 'vue-router';\n\ninterface Language {\n name: string;\n code: string;\n}\n\nexport default defineComponent({\n name: 'UserSettings',\n\n components: {\n UserTagPreferences,\n },\n\n props: {\n username: {\n type: String as PropType<string>,\n required: true,\n },\n },\n\n setup() {\n const configStore = useConfigStore();\n const route = useRoute();\n\n const darkMode = configStore.config.darkMode;\n const likesConfetti = configStore.config.likesConfetti;\n\n const isNewUser = route.path.endsWith('new');\n\n return { configStore, darkMode, likesConfetti, route, isNewUser };\n },\n\n data() {\n return {\n u: {} as UserDBInterface,\n configLanguages: [\n {\n name: 'English',\n code: 'en',\n },\n {\n name: 'French',\n code: 'fr',\n },\n ] as Language[],\n selectedLanguages: [] as string[],\n registeredCourses: [] as CourseConfig[],\n selectedCourseId: '' as string,\n };\n },\n\n async created() {\n this.u = await getCurrentUser();\n this.configLanguages.forEach((l) => {\n console.log(`afweatifvwzeatfvwzeta` + l.name);\n });\n\n // Load registered courses for preferences selector\n await this.loadRegisteredCourses();\n },\n\n methods: {\n updateDark(): void {\n this.configStore.updateDarkMode(this.configStore.config.darkMode);\n },\n\n updateConfetti(): void {\n this.configStore.updateLikesConfetti(this.configStore.config.likesConfetti);\n\n if (this.configStore.config.likesConfetti) {\n confetti({\n origin: {\n x: 0.5,\n y: 1,\n },\n });\n }\n },\n\n async loadRegisteredCourses(): Promise<void> {\n try {\n const activeCourses = await this.u.getActiveCourses();\n const dataLayer = getDataLayer();\n\n // Fetch course configs for display names\n const courseConfigs = await Promise.all(\n activeCourses.map(async (reg) => {\n try {\n const courseDB = dataLayer.getCourseDB(reg.courseID);\n return await courseDB.getCourseConfig();\n } catch {\n // Return minimal config if fetch fails\n return { courseID: reg.courseID, name: reg.courseID } as CourseConfig;\n }\n })\n );\n\n this.registeredCourses = courseConfigs.filter(Boolean);\n } catch (e) {\n console.error('Failed to load registered courses:', e);\n }\n },\n\n onPreferencesSaved(): void {\n // Could show a snackbar or other feedback here\n console.log('Preferences saved for course:', this.selectedCourseId);\n },\n },\n});\n</script>\n\n<style scoped></style>\n"],"x_google_ignoreList":[0],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import"./chunk-D6hFPZc3.js";import{H as e,S as t,T as n,Vt as r,_ as i,b as a,lt as o,z as s}from"./vue.runtime.esm-bundler-C3q8JS9f.js";import{T as c}from"./common-ui.es-mrcutgtG.js";import"./dist-CrcJCHFk.js";import"./MarkdownRenderer-DoVbFpA6-BIbDxYC4.js";import"./dist-DkeESCP4.js";var l=n({__name:`UserStats`,setup(n){let l=o(null),u=o([]);return s(async()=>{l.value=await c(),u.value=(await Promise.all([1,7,30].map(e=>l.value.getReviewsForcast(e)))).map(e=>e.length)}),(n,o)=>(e(),a(`div`,null,[o[0]||(o[0]=i(`h2`,null,`Stats!`,-1)),t(` One day: `+r(u.value[0])+` `,1),o[1]||(o[1]=i(`br`,null,null,-1)),t(` Seven day: `+r(u.value[1])+` `,1),o[2]||(o[2]=i(`br`,null,null,-1)),t(` Thirty Day: `+r(u.value[2]),1)]))}});export{l as default};
|
|
2
|
+
//# sourceMappingURL=UserStats-BW2xTraR.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UserStats-BW2xTraR.js","names":[],"sources":["../../src/components/User/UserStats.vue","../../src/components/User/UserStats.vue"],"sourcesContent":["<template>\n <div>\n <h2>Stats!</h2>\n One day: {{ scheduledReviews[0] }}\n <br />\n Seven day: {{ scheduledReviews[1] }}\n <br />\n Thirty Day: {{ scheduledReviews[2] }}\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, onMounted } from 'vue';\nimport { User } from '@vue-skuilder/db';\nimport { getCurrentUser } from '@vue-skuilder/common-ui';\n\nconst user = ref<User | null>(null);\nconst scheduledReviews = ref<number[]>([]);\n\nonMounted(async () => {\n user.value = await getCurrentUser();\n\n // Using Promise.all to handle all forecasts concurrently\n const days = [1, 7, 30];\n const forecasts = await Promise.all(days.map((d) => user.value!.getReviewsForcast(d)));\n scheduledReviews.value = forecasts.map((f) => f.length);\n});\n</script>\n\n<style scoped></style>\n","<template>\n <div>\n <h2>Stats!</h2>\n One day: {{ scheduledReviews[0] }}\n <br />\n Seven day: {{ scheduledReviews[1] }}\n <br />\n Thirty Day: {{ scheduledReviews[2] }}\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, onMounted } from 'vue';\nimport { User } from '@vue-skuilder/db';\nimport { getCurrentUser } from '@vue-skuilder/common-ui';\n\nconst user = ref<User | null>(null);\nconst scheduledReviews = ref<number[]>([]);\n\nonMounted(async () => {\n user.value = await getCurrentUser();\n\n // Using Promise.all to handle all forecasts concurrently\n const days = [1, 7, 30];\n const forecasts = await Promise.all(days.map((d) => user.value!.getReviewsForcast(d)));\n scheduledReviews.value = forecasts.map((f) => f.length);\n});\n</script>\n\n<style scoped></style>\n"],"mappings":"mUCgBA,IAAM,EAAO,EAAiB,KAAK,CAC7B,EAAmB,EAAc,EAAE,CAAC,QAE1C,EAAU,SAAY,CACpB,EAAK,MAAQ,MAAM,GAAgB,CAKnC,EAAiB,OADC,MAAM,QAAQ,IADnB,CAAC,EAAG,EAAG,GAAG,CACkB,IAAK,GAAM,EAAK,MAAO,kBAAkB,EAAE,CAAC,CAAC,EACnD,IAAK,GAAM,EAAE,OAAO,EACvD"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import"./chunk-D6hFPZc3.js";import{$ as e,C as t,H as n,K as r,S as i,T as a,v as o}from"./vue.runtime.esm-bundler-C3q8JS9f.js";import{V as s}from"./common-ui.es-mrcutgtG.js";import"./dist-CrcJCHFk.js";import"./MarkdownRenderer-DoVbFpA6-BIbDxYC4.js";import"./dist-DkeESCP4.js";import{t as c}from"./index-jZbLHVSV.js";var l=a({name:`VerifyEmailView`,components:{VerifyEmail:s},methods:{handleVerified(e){console.log(`User ${e} verified successfully`)},handleError(e){console.error(`Verification error:`,e)},goToLogin(){this.$router.push({name:`login`})},goToHome(){this.$router.push({name:`home`})}}});function _sfc_render(a,s,c,l,u,d){let f=r(`v-btn`),p=r(`verify-email`,!0);return n(),o(p,{onVerified:a.handleVerified,onError:a.handleError},{"success-action":e(()=>[t(f,{color:`primary`,onClick:a.goToLogin},{default:e(()=>s[0]||(s[0]=[i(` Continue to Login `)])),_:1},8,[`onClick`])]),"error-action":e(()=>[t(f,{variant:`text`,onClick:a.goToHome},{default:e(()=>s[1]||(s[1]=[i(` Back to Home `)])),_:1},8,[`onClick`])]),_:1},8,[`onVerified`,`onError`])}var u=c(l,[[`render`,_sfc_render]]);export{u as default};
|
|
2
|
+
//# sourceMappingURL=VerifyEmail-B5pYw6N1.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VerifyEmail-B5pYw6N1.js","names":[],"sources":["../../src/views/VerifyEmail.vue","../../src/views/VerifyEmail.vue"],"sourcesContent":["<template>\n <verify-email @verified=\"handleVerified\" @error=\"handleError\">\n <template #success-action>\n <v-btn color=\"primary\" @click=\"goToLogin\">\n Continue to Login\n </v-btn>\n </template>\n <template #error-action>\n <v-btn variant=\"text\" @click=\"goToHome\">\n Back to Home\n </v-btn>\n </template>\n </verify-email>\n</template>\n\n<script lang=\"ts\">\nimport { defineComponent } from 'vue';\nimport { VerifyEmail } from '@vue-skuilder/common-ui';\n\nexport default defineComponent({\n name: 'VerifyEmailView',\n\n components: {\n VerifyEmail,\n },\n\n methods: {\n handleVerified(username: string) {\n console.log(`User ${username} verified successfully`);\n },\n\n handleError(error: string) {\n console.error('Verification error:', error);\n },\n\n goToLogin() {\n this.$router.push({ name: 'login' });\n },\n\n goToHome() {\n this.$router.push({ name: 'home' });\n },\n },\n});\n</script>\n","<template>\n <verify-email @verified=\"handleVerified\" @error=\"handleError\">\n <template #success-action>\n <v-btn color=\"primary\" @click=\"goToLogin\">\n Continue to Login\n </v-btn>\n </template>\n <template #error-action>\n <v-btn variant=\"text\" @click=\"goToHome\">\n Back to Home\n </v-btn>\n </template>\n </verify-email>\n</template>\n\n<script lang=\"ts\">\nimport { defineComponent } from 'vue';\nimport { VerifyEmail } from '@vue-skuilder/common-ui';\n\nexport default defineComponent({\n name: 'VerifyEmailView',\n\n components: {\n VerifyEmail,\n },\n\n methods: {\n handleVerified(username: string) {\n console.log(`User ${username} verified successfully`);\n },\n\n handleError(error: string) {\n console.error('Verification error:', error);\n },\n\n goToLogin() {\n this.$router.push({ name: 'login' });\n },\n\n goToHome() {\n this.$router.push({ name: 'home' });\n },\n },\n});\n</script>\n"],"mappings":"6TCmBA,IAAA,EAAe,EAAgB,CAC7B,KAAM,kBAEN,WAAY,CACV,YAAA,EACD,CAED,QAAS,CACP,eAAe,EAAkB,CAC/B,QAAQ,IAAI,QAAQ,EAAQ,wBAAyB,EAGvD,YAAY,EAAe,CACzB,QAAQ,MAAM,sBAAuB,EAAM,EAG7C,WAAY,CACV,KAAK,QAAQ,KAAK,CAAE,KAAM,QAAS,CAAC,EAGtC,UAAW,CACT,KAAK,QAAQ,KAAK,CAAE,KAAM,OAAQ,CAAC,EAEtC,CACF,CAAC,sFA1CA,EAWe,EAAA,CAXA,WAAU,EAAA,eAAiB,QAAO,EAAA,cACpC,iBAAc,MAGf,CAFR,EAEQ,EAAA,CAFD,MAAM,UAAW,QAAO,EAAA,YAHrC,QAAA,MAKM,EAAA,KAAA,EAAA,GAAA,CALN,EAGgD,sBAE1C,CAAA,EAAA,CALN,EAAA,oBAOe,eAAY,MAGb,CAFR,EAEQ,EAAA,CAFD,QAAQ,OAAQ,QAAO,EAAA,WARpC,QAAA,MAUM,EAAA,KAAA,EAAA,GAAA,CAVN,EAQ8C,iBAExC,CAAA,EAAA,CAVN,EAAA,oBAAA,EAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e=Object.create,t=Object.defineProperty,__name=(e,n)=>t(e,`name`,{value:n,configurable:!0}),n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,__esmMin=(e,t)=>()=>(e&&(t=e(e=0)),t),__commonJSMin=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),__exportAll=(e,n)=>{let r={};for(var i in e)t(r,i,{get:e[i],enumerable:!0});return n||t(r,Symbol.toStringTag,{value:`Module`}),r},__copyProps=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},__toESM=(n,r,a)=>(a=n==null?{}:e(i(n)),__copyProps(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n)),__toCommonJS=e=>a.call(e,`module.exports`)?e[`module.exports`]:__copyProps(t({},`__esModule`,{value:!0}),e),o=(e=>typeof require<`u`?require:typeof Proxy<`u`?new Proxy(e,{get:(e,t)=>(typeof require<`u`?require:e)[t]}):e)(function(e){if(typeof require<`u`)return require.apply(this,arguments);throw Error('Calling `require` for "'+e+"\" in an environment that doesn't expose the `require` function. See https://rolldown.rs/in-depth/bundling-cjs#require-external-modules for more details.")});export{o as a,__name as i,__esmMin as n,__toCommonJS as o,__exportAll as r,__toESM as s,__commonJSMin as t};
|