Improved Client implementation
This commit is contained in:
parent
c1b1a5ec37
commit
cc166c01eb
@ -7,4 +7,8 @@ import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class CourseEnrollment(Document):
|
||||
pass
|
||||
|
||||
def get_all_activity(self):
|
||||
course_activity_list = frappe.get_all("Course Activity", filters={'enrollment':self.name}, fields=['content', 'content_type', 'modified'], order_by='modified')
|
||||
quiz_activity_list = frappe.get_all("Quiz Activity", filters={'enrollment':self.name}, fields=['quiz', 'status', 'modified'], order_by='modified')
|
||||
return course_activity_list, quiz_activity_list
|
@ -55,7 +55,7 @@
|
||||
"stock/dashboard/item_dashboard_list.html",
|
||||
"stock/dashboard/item_dashboard.js"
|
||||
],
|
||||
"js/web-academy.min.js": [
|
||||
"public/js/education/web-academy.js"
|
||||
"js/academy.min.js": [
|
||||
"public/js/education/academy/academy.js"
|
||||
]
|
||||
}
|
||||
|
@ -1,21 +1,20 @@
|
||||
import Vue from 'vue/dist/vue.js';
|
||||
import VueRouter from 'vue-router/dist/vue-router.js'
|
||||
|
||||
import AcademyRoot from "./web-academy/AcademyRoot.vue";
|
||||
import AcademyHome from "./web-academy/pages/AcademyHome.vue";
|
||||
import AcademyProgramPage from "./web-academy/pages/AcademyProgramPage.vue";
|
||||
import AcademyCoursePage from "./web-academy/pages/AcademyCoursePage.vue";
|
||||
import AcademyRoot from "./AcademyRoot.vue";
|
||||
import routes from './routes';
|
||||
import './call';
|
||||
|
||||
Vue.use(VueRouter)
|
||||
|
||||
|
||||
const routes = [
|
||||
{name: 'home', path: '', component: AcademyHome},
|
||||
{name: 'program', path: '/Program/:code', component: AcademyProgramPage, props: true},
|
||||
{name: 'content', path: '/Program/:code/:course/:type/:content', component: AcademyCoursePage, props: true},
|
||||
];
|
||||
|
||||
var store = {
|
||||
|
||||
frappe.provide('academy')
|
||||
|
||||
frappe.utils.make_event_emitter(academy);
|
||||
|
||||
academy.store = {
|
||||
debug: true,
|
||||
isLogin: false,
|
||||
completedCourses: new Set(),
|
||||
@ -104,19 +103,13 @@ var store = {
|
||||
this.updateEnrolledPrograms()
|
||||
this.updateEnrolledCourses()
|
||||
this.checkLogin()
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
const router = new VueRouter({
|
||||
routes: routes,
|
||||
});
|
||||
|
||||
frappe.ready(() => {
|
||||
window.v = new Vue({
|
||||
el: "#academy",
|
||||
router: router,
|
||||
data: store,
|
||||
router: new VueRouter({ routes }),
|
||||
template: "<academy-root/>",
|
||||
components: { AcademyRoot },
|
||||
created: function() {
|
||||
@ -125,4 +118,20 @@ frappe.ready(() => {
|
||||
}
|
||||
}
|
||||
});
|
||||
academy.store = new Vue({
|
||||
data: store,
|
||||
methods: {
|
||||
checkLogin (){
|
||||
if(frappe.session.user === "Guest"){
|
||||
if (this.debug) console.log('No Session')
|
||||
this.isLogin = false
|
||||
}
|
||||
else {
|
||||
if (this.debug) console.log('Current User: ', frappe.session.user)
|
||||
this.isLogin = true
|
||||
}
|
||||
return this.isLogin
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
13
erpnext/public/js/education/academy/call.js
Normal file
13
erpnext/public/js/education/academy/call.js
Normal file
@ -0,0 +1,13 @@
|
||||
frappe.provide('academy');
|
||||
|
||||
academy.call = (method, args) => {
|
||||
const method_path = 'erpnext.www.academy.' + method;
|
||||
return new Promise((resolve, reject) => {
|
||||
return frappe.call({
|
||||
method: method_path,
|
||||
args,
|
||||
})
|
||||
.then(r => resolve(r.message))
|
||||
.fail(reject)
|
||||
});
|
||||
}
|
@ -12,7 +12,29 @@
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="$root.$data.isLogin" class='course-buttons text-center col-xs-4 col-sm-3 col-md-2'>
|
||||
<AcademyCourseCardButton v-if="this.$root.$data.checkProgramEnrollment(this.$route.params.code)" :course="course.name" :nextContent="nextContent" :nextContentType="nextContentType"/>
|
||||
<!-- <AcademyCourseCardButton
|
||||
v-if="this.$root.$data.checkProgramEnrollment(this.$route.params.code)"
|
||||
:course="course.name"
|
||||
:nextContent="nextContent"
|
||||
:nextContentType="nextContentType"
|
||||
/> -->
|
||||
|
||||
<a-button
|
||||
v-if="showCompleted"
|
||||
type="success"
|
||||
size="sm"
|
||||
:route="firstContentRoute"
|
||||
>
|
||||
Completed
|
||||
</a-button>
|
||||
<a-button
|
||||
v-if="showStart"
|
||||
type="primary"
|
||||
size="sm"
|
||||
:route="firstContentRoute"
|
||||
>
|
||||
Start
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -20,6 +42,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AButton from './Button';
|
||||
import AcademyCourseCardButton from './AcademyCourseCardButton.vue'
|
||||
|
||||
export default {
|
||||
@ -32,7 +55,7 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if(this.$root.$data.checkLogin()){
|
||||
if(this.$root.$data.checkLogin()) {
|
||||
frappe.call({
|
||||
method: "erpnext.www.academy.get_starting_content",
|
||||
args: {
|
||||
@ -45,7 +68,19 @@ export default {
|
||||
}
|
||||
},
|
||||
components: {
|
||||
AcademyCourseCardButton
|
||||
AcademyCourseCardButton,
|
||||
AButton
|
||||
},
|
||||
computed: {
|
||||
showStart() {
|
||||
return academy.loggedIn && !this.course.completed;
|
||||
},
|
||||
showCompleted() {
|
||||
return academy.loggedIn && this.course.completed;
|
||||
},
|
||||
firstContentRoute() {
|
||||
return `${course.name}/${course.content_type}/${data.content}`
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
@ -38,14 +38,14 @@ export default {
|
||||
methods: {
|
||||
primaryAction(){
|
||||
if(this.$root.$data.isLogin){
|
||||
if(this.$root.$data.checkProgramEnrollment(program_code)){
|
||||
if(this.$root.$data.checkProgramEnrollment(this.program_code)){
|
||||
this.$router.push('/Program/' + program.name)
|
||||
}
|
||||
else {
|
||||
this.enroll()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
enroll() {
|
||||
frappe.call({
|
||||
method: "erpnext.www.academy.enroll_in_program",
|
||||
@ -57,11 +57,11 @@ export default {
|
||||
this.$root.$data.enrolledPrograms.add(this.program_code)
|
||||
this.$root.$data.updateEnrolledPrograms()
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
buttonName() {
|
||||
if(this.$root.$data.isLogin){
|
||||
if(this.$root.$data.checkProgramEnrollment(program_code)){
|
||||
if(this.$root.$data.checkProgramEnrollment(this.program_code)){
|
||||
return "Start Course"
|
||||
}
|
||||
else {
|
25
erpnext/public/js/education/academy/components/Button.vue
Normal file
25
erpnext/public/js/education/academy/components/Button.vue
Normal file
@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<button :class="classList" v-on="$listeners" v-bind="$attrs" @click="goToRoute">
|
||||
<slot></slot>
|
||||
</button>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'AButton',
|
||||
props: ['type', 'size', 'route'],
|
||||
computed: {
|
||||
classList() {
|
||||
return [
|
||||
'btn',
|
||||
'btn-' + this.type,
|
||||
'btn-' + this.size
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
goToRoute() {
|
||||
this.$router.push(this.route);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
@ -41,9 +41,12 @@ export default {
|
||||
enrollment: this.$root.$data.enrolledCourses[this.$route.params.course]
|
||||
}
|
||||
})
|
||||
this.$root.$data.addCompletedCourses(this.$route.params.course)
|
||||
// this.$root.$data.addCompletedCourses(this.$route.params.course)
|
||||
this.$root.$data.updateCompletedCourses()
|
||||
this.$router.push({ name: 'program', params: { code: this.$route.params.code}})
|
||||
|
||||
//
|
||||
academy.trigger('course-completed', course_name);
|
||||
}
|
||||
}
|
||||
};
|
@ -0,0 +1,73 @@
|
||||
<template>
|
||||
<div>
|
||||
<AcademyTopSection v-bind:title="program.program_name" v-bind:description="program.description">
|
||||
<!-- <AcademyTopSectionButton/> -->
|
||||
<a-button @click="startCourse">Start Course</a-button>
|
||||
<a-button @click="continueCourse">Continue Course</a-button>
|
||||
</AcademyTopSection>
|
||||
<AcademyList :title="'Courses'" :description="''">
|
||||
<AcademyCourseCard v-for="course in course_list" :course="course" :key="course.name"/>
|
||||
</AcademyList>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Button from '../components/Button.vue';
|
||||
import AcademyTopSection from "../components/AcademyTopSection.vue"
|
||||
import AcademyList from "../components/AcademyList.vue"
|
||||
import AcademyCourseCard from "../components/AcademyCourseCard.vue"
|
||||
import AcademyTopSectionButton from "../components/AcademyTopSectionButton.vue";
|
||||
|
||||
|
||||
export default {
|
||||
props: ['program_name'],
|
||||
name: "AcademyProgramPage",
|
||||
components: {
|
||||
AButton: Button,
|
||||
AcademyTopSection,
|
||||
AcademyList,
|
||||
AcademyCourseCard,
|
||||
AcademyTopSectionButton
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
program: {},
|
||||
course_list: []
|
||||
}
|
||||
},
|
||||
beforeMount() {
|
||||
if(this.$root.$data.isLogin) this.$root.$data.updateCompletedCourses()
|
||||
},
|
||||
mounted() {
|
||||
this.getProgramDetails().then(data => this.program = data);
|
||||
this.getCourses().then(data => this.course_list = data);
|
||||
|
||||
academy.on(`course-completed`, (course_name) => {
|
||||
const course = this.course_list.findIndex(c => c.name === course_name);
|
||||
this.course_list[course].completed = true;
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
startCourse() {
|
||||
this.getContentForNextCourse()
|
||||
.then((data) =>
|
||||
this.$router.push(`/Program/${this.program_name}/${data.course}/${data.content_type}/${data.content}`)
|
||||
)
|
||||
},
|
||||
getContentForNextCourse() {
|
||||
return academy.call('get_continue_data', {
|
||||
program_name: this.program_name
|
||||
});
|
||||
},
|
||||
getProgramDetails() {
|
||||
return academy.call('get_program_details', {
|
||||
program_name: this.program_name
|
||||
});
|
||||
},
|
||||
getCourses() {
|
||||
return academy.call('get_courses', {
|
||||
program_name: this.program_name
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
11
erpnext/public/js/education/academy/routes.js
Normal file
11
erpnext/public/js/education/academy/routes.js
Normal file
@ -0,0 +1,11 @@
|
||||
import AcademyHome from "./academy/pages/AcademyHome.vue";
|
||||
import AcademyProgramPage from "./academy/pages/AcademyProgramPage.vue";
|
||||
import AcademyCoursePage from "./academy/pages/AcademyCoursePage.vue";
|
||||
|
||||
const routes = [
|
||||
{name: 'home', path: '', component: AcademyHome},
|
||||
{name: 'program', path: '/Program/:program_name', component: AcademyProgramPage, props: true},
|
||||
{name: 'content', path: '/Program/:code/:course/:type/:content', component: AcademyCoursePage, props: true},
|
||||
];
|
||||
|
||||
export default routes;
|
@ -1,54 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<AcademyTopSection v-bind:title="program.program_name" v-bind:description="program.description">
|
||||
<AcademyTopSectionButton/>
|
||||
</AcademyTopSection>
|
||||
<AcademyList :title="'Courses'" :description="''">
|
||||
<AcademyCourseCard v-for="course in course_list" :course="course" :key="course.name"/>
|
||||
</AcademyList>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import AcademyTopSection from "../components/AcademyTopSection.vue"
|
||||
import AcademyList from "../components/AcademyList.vue"
|
||||
import AcademyCourseCard from "../components/AcademyCourseCard.vue"
|
||||
import AcademyTopSectionButton from "../components/AcademyTopSectionButton.vue"
|
||||
|
||||
export default {
|
||||
props: ['code'],
|
||||
name: "AcademyProgramPage",
|
||||
components: {
|
||||
AcademyTopSection,
|
||||
AcademyList,
|
||||
AcademyCourseCard,
|
||||
AcademyTopSectionButton
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
program: '',
|
||||
course_list: []
|
||||
}
|
||||
},
|
||||
beforeMount(){
|
||||
if(this.$root.$data.isLogin) this.$root.$data.updateCompletedCourses()
|
||||
},
|
||||
mounted() {
|
||||
frappe.call({
|
||||
method: "erpnext.www.academy.get_program_details",
|
||||
args: {
|
||||
program_name: this.code
|
||||
}
|
||||
}).then(r => {
|
||||
this.program = r.message
|
||||
});
|
||||
frappe.call({
|
||||
method: "erpnext.www.academy.get_courses",
|
||||
args: {
|
||||
program_name: this.code
|
||||
}
|
||||
}).then(r => {
|
||||
this.course_list = r.message
|
||||
})
|
||||
},
|
||||
};
|
||||
</script>
|
@ -4,5 +4,5 @@
|
||||
|
||||
{% block page_content %}
|
||||
<div id="academy"></div>
|
||||
<script type="text/javascript" src="/assets/js/web-academy.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/js/academy.min.js"></script>
|
||||
{% endblock %}
|
Loading…
x
Reference in New Issue
Block a user