Merge pull request #1334 from pateljannat/paid-certificate-on-courses

feat: paid certifications on courses
This commit is contained in:
Jannat Patel
2025-02-25 14:47:07 +05:30
committed by GitHub
23 changed files with 510 additions and 279 deletions

View File

@@ -0,0 +1,67 @@
<template>
<div
v-if="
certification.data &&
certification.data.membership &&
certification.data.paid_certificate &&
user.data?.is_student
"
>
<router-link
v-if="!certification.data.membership.purchased_certificate"
:to="{
name: 'Billing',
params: {
type: 'certificate',
name: courseName,
},
}"
>
<Button class="w-full">
<template #prefix>
<GraduationCap class="size-4 stroke-1.5" />
</template>
{{ __('Get Certified') }}
</Button>
</router-link>
<router-link
v-else-if="!certification.data.membership.certficate"
:to="{
name: 'CourseCertification',
params: {
courseName: courseName,
},
}"
>
<Button class="w-full">
<template #prefix>
<GraduationCap class="size-4 stroke-1.5" />
</template>
{{ __('Get Certified') }}
</Button>
</router-link>
</div>
</template>
<script setup>
import { Button, createResource } from 'frappe-ui'
import { inject } from 'vue'
import { GraduationCap } from 'lucide-vue-next'
const user = inject('$user')
const props = defineProps({
courseName: {
type: String,
required: true,
},
})
const certification = createResource({
url: 'lms.lms.api.get_certification_details',
params: {
course: props.courseName,
},
auto: true,
cache: ['certificationData', user.data?.name],
})
</script>

View File

@@ -100,9 +100,15 @@
<CourseInstructors :instructors="course.instructors" />
</div>
<div class="font-semibold">
<div v-if="course.paid_course" class="font-semibold">
{{ course.price }}
</div>
<div
v-if="course.paid_certificate || course.enable_certification"
class="text-xs text-ink-blue-3 bg-surface-blue-1 py-0.5 px-1 rounded-md"
>
{{ __('Certification') }}
</div>
</div>
</div>
</div>

View File

@@ -6,30 +6,32 @@
class="rounded-t-md min-h-56 w-full"
/>
<div class="p-5">
<div v-if="course.data.price" class="text-2xl font-semibold mb-3">
<div v-if="course.data.paid_course" class="text-2xl font-semibold mb-3">
{{ course.data.price }}
</div>
<router-link
v-if="course.data.membership"
:to="{
name: 'Lesson',
params: {
courseName: course.name,
chapterNumber: course.data.current_lesson
? course.data.current_lesson.split('-')[0]
: 1,
lessonNumber: course.data.current_lesson
? course.data.current_lesson.split('-')[1]
: 1,
},
}"
>
<Button variant="solid" size="md" class="w-full">
<span>
{{ __('Continue Learning') }}
</span>
</Button>
</router-link>
<div v-if="course.data.membership" class="space-y-2">
<router-link
:to="{
name: 'Lesson',
params: {
courseName: course.name,
chapterNumber: course.data.current_lesson
? course.data.current_lesson.split('-')[0]
: 1,
lessonNumber: course.data.current_lesson
? course.data.current_lesson.split('-')[1]
: 1,
},
}"
>
<Button variant="solid" size="md" class="w-full">
<span>
{{ __('Continue Learning') }}
</span>
</Button>
</router-link>
<CertificationLinks :courseName="course.data.name" />
</div>
<router-link
v-else-if="course.data.paid_course"
:to="{
@@ -113,17 +115,36 @@
{{ course.data.rating }} {{ __('Rating') }}
</span>
</div>
<div
v-if="course.data.enable_certification"
class="flex items-center font-semibold text-ink-gray-9"
>
<GraduationCap class="h-4 w-4 stroke-2" />
<span class="ml-2">
{{ __('Certificate of Completion') }}
</span>
</div>
<div
v-if="course.data.paid_certificate"
class="flex items-center font-semibold text-ink-gray-9"
>
<GraduationCap class="h-4 w-4 stroke-2" />
<span class="ml-2">
{{ __('Paid Certificate after Evaluation') }}
</span>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { BookOpen, Users, Star } from 'lucide-vue-next'
import { BookOpen, Users, Star, GraduationCap } from 'lucide-vue-next'
import { computed, inject } from 'vue'
import { Button, createResource } from 'frappe-ui'
import { Button, createResource, Tooltip } from 'frappe-ui'
import { showToast, formatAmount } from '@/utils/'
import { capture } from '@/telemetry'
import { useRouter } from 'vue-router'
import CertificationLinks from '@/components/CertificationLinks.vue'
const router = useRouter()
const user = inject('$user')

View File

@@ -38,7 +38,7 @@
<div class="flex mt-2">
<Star
v-for="index in 5"
class="h-5 w-5 text-ink-gray-2 rounded-sm mr-2"
class="h-5 w-5 text-ink-gray-1 rounded-sm mr-2"
:class="
index <= Math.ceil(review.rating)
? 'fill-orange-500'

View File

@@ -169,6 +169,11 @@ const getCourses = () => {
})
}
}
if (courses.length == 1) {
evaluation.course = courses[0].value
}
return courses
}

View File

@@ -4,7 +4,13 @@
<div class="text-lg font-semibold">
{{ __('Upcoming Evaluations') }}
</div>
<Button @click="openEvalModal">
<Button
v-if="
!upcoming_evals.data?.length ||
upcoming_evals.length == courses.length
"
@click="openEvalModal"
>
{{ __('Schedule Evaluation') }}
</Button>
</div>
@@ -60,7 +66,7 @@
</div>
</div>
<div v-else class="text-sm italic text-ink-gray-5">
{{ __('No upcoming evaluations.') }}
{{ __('Please schedule an evaluation to get certified.') }}
</div>
</div>
<EvaluationModal
@@ -107,10 +113,10 @@ const props = defineProps({
const upcoming_evals = createResource({
url: 'lms.lms.utils.get_upcoming_evals',
cache: ['upcoming_evals', user.data.name],
params: {
student: user.data.name,
courses: props.courses.map((course) => course.course),
batch: props.batch,
},
auto: true,
})