fix: polished the course certification flow

This commit is contained in:
Jannat Patel
2025-02-25 12:46:35 +05:30
parent 4f1dcbfb78
commit 63bf6a5574
9 changed files with 44 additions and 25 deletions

View File

@@ -104,10 +104,10 @@
{{ course.price }}
</div>
<div
v-if="course.paid_certificate"
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"
>
{{ __('Paid Certificate') }}
{{ __('Certification') }}
</div>
</div>
</div>

View File

@@ -115,13 +115,22 @@
{{ 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') }}
{{ __('Paid Certificate after Evaluation') }}
</span>
</div>
</div>
@@ -131,7 +140,7 @@
<script setup>
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'

View File

@@ -113,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,
})

View File

@@ -12,16 +12,11 @@
v-if="access.data?.access && orderSummary.data"
class="pt-5 pb-10 mx-5"
>
<!-- <div class="mb-5">
<div class="text-lg font-semibold">
{{ __('Address') }}
</div>
</div> -->
<div class="flex flex-col lg:flex-row justify-between">
<div
class="h-fit bg-surface-gray-2 rounded-md p-5 space-y-4 lg:order-last mb-10 lg:mt-10 text-sm font-medium lg:w-1/4"
class="h-fit bg-surface-gray-2 rounded-md p-5 space-y-4 lg:order-last mb-10 lg:mt-10 font-medium lg:w-1/3"
>
<div class="flex flex-col space-y-2">
<div class="flex items-baseline justify-between space-y-2">
<div class="text-ink-gray-5">
{{ __('Payment for ') }} {{ type }}:
</div>
@@ -243,17 +238,19 @@ const paymentLink = createResource({
total_amount: orderSummary.data.amount,
currency: orderSummary.data.currency,
address: billingDetails,
redirect_to: redirectTo,
redirect_to: redirectTo.value,
payment_for_certificate: props.type == 'certificate',
}
},
})
const generatePaymentLink = () => {
console.log('called')
paymentLink.submit(
{},
{
validate() {
console.log('validation start')
if (!billingDetails.source) {
return __('Please let us know where you heard about us from.')
}
@@ -332,6 +329,8 @@ const validateAddress = () => {
!states.includes(billingDetails.state)
)
return 'Please enter a valid state with correct spelling and the first letter capitalized.'
console.log('validation address')
}
const showError = (err) => {

View File

@@ -5,7 +5,7 @@
<Breadcrumbs class="h-7" :items="breadcrumbs" />
</header>
<div class="p-5">
<div v-if="certificate.data">
<div v-if="certificate.data && Object.keys(certificate.data).length">
<div class="text-lg text-ink-gray-9 font-semibold mb-1">
{{ __('Certification') }}
</div>
@@ -17,7 +17,7 @@
}}
</div>
<div
class="border p-2 w-fit rounded-md space-y-2 hover:bg-surface-gray-1 cursor-pointer mt-5"
class="border p-3 w-fit min-w-60 rounded-md space-y-2 hover:bg-surface-gray-1 cursor-pointer mt-5"
@click="openCertificate(certificate.data)"
>
<div class="text-ink-gray-9 font-semibold">
@@ -67,6 +67,7 @@ const certificate = createResource({
fieldname: ['name', 'template', 'issue_date'],
},
auto: true,
cache: [user.data?.name, props.courseName],
})
const fetchCourseDetails = () => {

View File

@@ -304,6 +304,7 @@ const course = reactive({
paid_certificate: false,
course_price: '',
currency: '',
evaluator: '',
})
onMounted(() => {

View File

@@ -59,7 +59,6 @@ class CourseEvaluator(Document):
@frappe.whitelist()
def get_schedule(course, date, batch=None):
print(batch)
evaluator = get_evaluator(course, batch)
day = datetime.strptime(date, "%Y-%m-%d").strftime("%A")

View File

@@ -18,7 +18,7 @@ class LMSCourse(Document):
self.validate_video_link()
self.validate_status()
self.validate_payments_app()
self.validate_evaluator()
self.validate_certification()
self.validate_amount_and_currency()
self.image = validate_image(self.image)
@@ -52,7 +52,12 @@ class LMSCourse(Document):
if "payments" not in installed_apps:
frappe.throw(_("Please install the Payments app to create a paid courses."))
def validate_evaluator(self):
def validate_certification(self):
if self.enable_certification and self.paid_certificate:
frappe.throw(
_("A course cannot have both paid certificate and certificate of completion.")
)
if self.paid_certificate and not self.evaluator:
frappe.throw(_("Evaluator is required for paid certificates."))

View File

@@ -869,15 +869,20 @@ def get_evaluator(course, batch=None):
@frappe.whitelist()
def get_upcoming_evals(student, courses):
def get_upcoming_evals(student, courses, batch=None):
filters = {
"member": student,
"course": ["in", courses],
"date": [">=", frappe.utils.nowdate()],
"status": "Upcoming",
}
if batch:
filters["batch_name"] = batch
upcoming_evals = frappe.get_all(
"LMS Certificate Request",
{
"member": student,
"course": ["in", courses],
"date": [">=", frappe.utils.nowdate()],
"status": "Upcoming",
},
filters,
[
"name",
"date",