feat: generate certificate from course page
This commit is contained in:
@@ -63,7 +63,13 @@
|
||||
{{ __('Start Learning') }}
|
||||
</span>
|
||||
</Button>
|
||||
<Button v-if="canGetCertificate">
|
||||
<Button
|
||||
v-if="canGetCertificate"
|
||||
@click="fetchCertificate()"
|
||||
variant="subtle"
|
||||
class="w-full mt-2"
|
||||
size="md"
|
||||
>
|
||||
{{ __('Get Certificate') }}
|
||||
</Button>
|
||||
<router-link
|
||||
@@ -139,7 +145,7 @@ function enrollStudent() {
|
||||
})
|
||||
setTimeout(() => {
|
||||
window.location.href = `/login?redirect-to=${window.location.pathname}`
|
||||
}, 3000)
|
||||
}, 2000)
|
||||
} else {
|
||||
const enrollStudentResource = createResource({
|
||||
url: 'lms.lms.doctype.lms_enrollment.lms_enrollment.create_membership',
|
||||
@@ -179,6 +185,37 @@ const is_instructor = () => {
|
||||
}
|
||||
|
||||
const canGetCertificate = computed(() => {
|
||||
console.log(props.course)
|
||||
if (
|
||||
props.course.data?.enable_certification &&
|
||||
props.course.data?.membership?.progress == 100
|
||||
) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
const certificate = createResource({
|
||||
url: 'lms.lms.doctype.lms_certificate.lms_certificate.create_certificate',
|
||||
makeParams(values) {
|
||||
return {
|
||||
course: values.course,
|
||||
}
|
||||
},
|
||||
onSuccess(data) {
|
||||
console.log(data)
|
||||
window.open(
|
||||
`/api/method/frappe.utils.print_format.download_pdf?doctype=LMS+Certificate&name=${
|
||||
data.name
|
||||
}&format=${encodeURIComponent(data.template)}`,
|
||||
'_blank'
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
const fetchCertificate = () => {
|
||||
certificate.submit({
|
||||
course: props.course.data?.name,
|
||||
member: user.data?.name,
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
<template>
|
||||
<div ref="videoContainer" class="video-block group relative">
|
||||
<video @timeupdate="updateTime" @ended="videoEnded" class="rounded-lg">
|
||||
<video
|
||||
@timeupdate="updateTime"
|
||||
@ended="videoEnded"
|
||||
class="rounded-lg border border-gray-100"
|
||||
>
|
||||
<source :src="fileURL" :type="type" />
|
||||
</video>
|
||||
<div
|
||||
class="flex items-center space-x-2 bg-gray-200 rounded-lg p-0.5 absolute bottom-3 w-[98%] left-0 right-0 mx-auto"
|
||||
class="flex items-center space-x-2 bg-gray-200 rounded-md p-0.5 absolute bottom-3 w-[98%] left-0 right-0 mx-auto"
|
||||
>
|
||||
<Button variant="ghost">
|
||||
<template #icon>
|
||||
|
||||
@@ -171,7 +171,7 @@
|
||||
{{ lesson.data.course_title }}
|
||||
</div>
|
||||
<div v-if="user && lesson.data.membership" class="text-sm mt-3">
|
||||
{{ Math.ceil(lessonProgress) }}% completed
|
||||
{{ Math.ceil(lessonProgress) }}% {{ __('completed') }}
|
||||
</div>
|
||||
|
||||
<ProgressBar
|
||||
@@ -190,7 +190,7 @@
|
||||
</template>
|
||||
<script setup>
|
||||
import { createResource, Breadcrumbs, Button } from 'frappe-ui'
|
||||
import { computed, watch, inject, ref } from 'vue'
|
||||
import { computed, watch, inject, ref, onMounted, onBeforeUnmount } from 'vue'
|
||||
import CourseOutline from '@/components/CourseOutline.vue'
|
||||
import UserAvatar from '@/components/UserAvatar.vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
@@ -208,6 +208,8 @@ const allowDiscussions = ref(false)
|
||||
const editor = ref(null)
|
||||
const instructorEditor = ref(null)
|
||||
const lessonProgress = ref(0)
|
||||
const timer = ref(0)
|
||||
let timerInterval
|
||||
|
||||
const props = defineProps({
|
||||
courseName: {
|
||||
@@ -224,6 +226,10 @@ const props = defineProps({
|
||||
},
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
startTimer()
|
||||
})
|
||||
|
||||
const lesson = createResource({
|
||||
url: 'lms.lms.utils.get_lesson',
|
||||
cache: ['lesson', props.courseName, props.chapterNumber, props.lessonNumber],
|
||||
@@ -237,7 +243,6 @@ const lesson = createResource({
|
||||
auto: true,
|
||||
onSuccess(data) {
|
||||
lessonProgress.value = data.membership?.progress
|
||||
markProgress(data)
|
||||
if (data.content) editor.value = renderEditor('editor', data.content)
|
||||
if (data.instructor_content?.blocks?.length)
|
||||
instructorEditor.value = renderEditor(
|
||||
@@ -269,11 +274,9 @@ const renderEditor = (holder, content) => {
|
||||
})
|
||||
}
|
||||
|
||||
const markProgress = (data) => {
|
||||
if (user.data && !data.progress) {
|
||||
setTimeout(() => {
|
||||
progress.submit()
|
||||
}, 30000)
|
||||
const markProgress = () => {
|
||||
if (user.data && !lesson.data?.progress) {
|
||||
progress.submit()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -325,10 +328,32 @@ watch(
|
||||
chapter: newChapterNumber,
|
||||
lesson: newLessonNumber,
|
||||
})
|
||||
clearInterval(timerInterval)
|
||||
timer.value = 0
|
||||
startTimer()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
const startTimer = () => {
|
||||
console.log('starting timer')
|
||||
timerInterval = setInterval(() => {
|
||||
timer.value++
|
||||
console.log(timer.value)
|
||||
if (timer.value == 30) {
|
||||
console.log('30 seconds passed')
|
||||
console.log(lesson.data?.title)
|
||||
clearInterval(timerInterval)
|
||||
markProgress()
|
||||
}
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
console.log('clearing interval')
|
||||
clearInterval(timerInterval)
|
||||
})
|
||||
|
||||
const checkIfDiscussionsAllowed = () => {
|
||||
let quizPresent = false
|
||||
JSON.parse(lesson.data?.content)?.blocks?.forEach((block) => {
|
||||
|
||||
Reference in New Issue
Block a user