Improved Client implementation

This commit is contained in:
scmmishra 2018-11-09 11:47:09 +05:30 committed by Aditya Hase
parent c1b1a5ec37
commit cc166c01eb
24 changed files with 202 additions and 83 deletions

View File

@ -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

View File

@ -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"
]
}

View File

@ -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
}
}
});
})

View 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)
});
}

View File

@ -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>

View File

@ -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 {

View 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>

View File

@ -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);
}
}
};

View File

@ -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>

View 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;

View File

@ -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>

View File

@ -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 %}