fix: polished the course certification flow
This commit is contained in:
@@ -104,10 +104,10 @@
|
|||||||
{{ course.price }}
|
{{ course.price }}
|
||||||
</div>
|
</div>
|
||||||
<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"
|
class="text-xs text-ink-blue-3 bg-surface-blue-1 py-0.5 px-1 rounded-md"
|
||||||
>
|
>
|
||||||
{{ __('Paid Certificate') }}
|
{{ __('Certification') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -115,13 +115,22 @@
|
|||||||
{{ course.data.rating }} {{ __('Rating') }}
|
{{ course.data.rating }} {{ __('Rating') }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</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
|
<div
|
||||||
v-if="course.data.paid_certificate"
|
v-if="course.data.paid_certificate"
|
||||||
class="flex items-center font-semibold text-ink-gray-9"
|
class="flex items-center font-semibold text-ink-gray-9"
|
||||||
>
|
>
|
||||||
<GraduationCap class="h-4 w-4 stroke-2" />
|
<GraduationCap class="h-4 w-4 stroke-2" />
|
||||||
<span class="ml-2">
|
<span class="ml-2">
|
||||||
{{ __('Paid Certificate') }}
|
{{ __('Paid Certificate after Evaluation') }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -131,7 +140,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { BookOpen, Users, Star, GraduationCap } from 'lucide-vue-next'
|
import { BookOpen, Users, Star, GraduationCap } from 'lucide-vue-next'
|
||||||
import { computed, inject } from 'vue'
|
import { computed, inject } from 'vue'
|
||||||
import { Button, createResource } from 'frappe-ui'
|
import { Button, createResource, Tooltip } from 'frappe-ui'
|
||||||
import { showToast, formatAmount } from '@/utils/'
|
import { showToast, formatAmount } from '@/utils/'
|
||||||
import { capture } from '@/telemetry'
|
import { capture } from '@/telemetry'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
|
|||||||
@@ -113,10 +113,10 @@ const props = defineProps({
|
|||||||
|
|
||||||
const upcoming_evals = createResource({
|
const upcoming_evals = createResource({
|
||||||
url: 'lms.lms.utils.get_upcoming_evals',
|
url: 'lms.lms.utils.get_upcoming_evals',
|
||||||
cache: ['upcoming_evals', user.data.name],
|
|
||||||
params: {
|
params: {
|
||||||
student: user.data.name,
|
student: user.data.name,
|
||||||
courses: props.courses.map((course) => course.course),
|
courses: props.courses.map((course) => course.course),
|
||||||
|
batch: props.batch,
|
||||||
},
|
},
|
||||||
auto: true,
|
auto: true,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -12,16 +12,11 @@
|
|||||||
v-if="access.data?.access && orderSummary.data"
|
v-if="access.data?.access && orderSummary.data"
|
||||||
class="pt-5 pb-10 mx-5"
|
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="flex flex-col lg:flex-row justify-between">
|
||||||
<div
|
<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">
|
<div class="text-ink-gray-5">
|
||||||
{{ __('Payment for ') }} {{ type }}:
|
{{ __('Payment for ') }} {{ type }}:
|
||||||
</div>
|
</div>
|
||||||
@@ -243,17 +238,19 @@ const paymentLink = createResource({
|
|||||||
total_amount: orderSummary.data.amount,
|
total_amount: orderSummary.data.amount,
|
||||||
currency: orderSummary.data.currency,
|
currency: orderSummary.data.currency,
|
||||||
address: billingDetails,
|
address: billingDetails,
|
||||||
redirect_to: redirectTo,
|
redirect_to: redirectTo.value,
|
||||||
payment_for_certificate: props.type == 'certificate',
|
payment_for_certificate: props.type == 'certificate',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const generatePaymentLink = () => {
|
const generatePaymentLink = () => {
|
||||||
|
console.log('called')
|
||||||
paymentLink.submit(
|
paymentLink.submit(
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
validate() {
|
validate() {
|
||||||
|
console.log('validation start')
|
||||||
if (!billingDetails.source) {
|
if (!billingDetails.source) {
|
||||||
return __('Please let us know where you heard about us from.')
|
return __('Please let us know where you heard about us from.')
|
||||||
}
|
}
|
||||||
@@ -332,6 +329,8 @@ const validateAddress = () => {
|
|||||||
!states.includes(billingDetails.state)
|
!states.includes(billingDetails.state)
|
||||||
)
|
)
|
||||||
return 'Please enter a valid state with correct spelling and the first letter capitalized.'
|
return 'Please enter a valid state with correct spelling and the first letter capitalized.'
|
||||||
|
|
||||||
|
console.log('validation address')
|
||||||
}
|
}
|
||||||
|
|
||||||
const showError = (err) => {
|
const showError = (err) => {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<Breadcrumbs class="h-7" :items="breadcrumbs" />
|
<Breadcrumbs class="h-7" :items="breadcrumbs" />
|
||||||
</header>
|
</header>
|
||||||
<div class="p-5">
|
<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">
|
<div class="text-lg text-ink-gray-9 font-semibold mb-1">
|
||||||
{{ __('Certification') }}
|
{{ __('Certification') }}
|
||||||
</div>
|
</div>
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
}}
|
}}
|
||||||
</div>
|
</div>
|
||||||
<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)"
|
@click="openCertificate(certificate.data)"
|
||||||
>
|
>
|
||||||
<div class="text-ink-gray-9 font-semibold">
|
<div class="text-ink-gray-9 font-semibold">
|
||||||
@@ -67,6 +67,7 @@ const certificate = createResource({
|
|||||||
fieldname: ['name', 'template', 'issue_date'],
|
fieldname: ['name', 'template', 'issue_date'],
|
||||||
},
|
},
|
||||||
auto: true,
|
auto: true,
|
||||||
|
cache: [user.data?.name, props.courseName],
|
||||||
})
|
})
|
||||||
|
|
||||||
const fetchCourseDetails = () => {
|
const fetchCourseDetails = () => {
|
||||||
|
|||||||
@@ -304,6 +304,7 @@ const course = reactive({
|
|||||||
paid_certificate: false,
|
paid_certificate: false,
|
||||||
course_price: '',
|
course_price: '',
|
||||||
currency: '',
|
currency: '',
|
||||||
|
evaluator: '',
|
||||||
})
|
})
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|||||||
@@ -59,7 +59,6 @@ class CourseEvaluator(Document):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_schedule(course, date, batch=None):
|
def get_schedule(course, date, batch=None):
|
||||||
print(batch)
|
|
||||||
evaluator = get_evaluator(course, batch)
|
evaluator = get_evaluator(course, batch)
|
||||||
day = datetime.strptime(date, "%Y-%m-%d").strftime("%A")
|
day = datetime.strptime(date, "%Y-%m-%d").strftime("%A")
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ class LMSCourse(Document):
|
|||||||
self.validate_video_link()
|
self.validate_video_link()
|
||||||
self.validate_status()
|
self.validate_status()
|
||||||
self.validate_payments_app()
|
self.validate_payments_app()
|
||||||
self.validate_evaluator()
|
self.validate_certification()
|
||||||
self.validate_amount_and_currency()
|
self.validate_amount_and_currency()
|
||||||
self.image = validate_image(self.image)
|
self.image = validate_image(self.image)
|
||||||
|
|
||||||
@@ -52,7 +52,12 @@ class LMSCourse(Document):
|
|||||||
if "payments" not in installed_apps:
|
if "payments" not in installed_apps:
|
||||||
frappe.throw(_("Please install the Payments app to create a paid courses."))
|
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:
|
if self.paid_certificate and not self.evaluator:
|
||||||
frappe.throw(_("Evaluator is required for paid certificates."))
|
frappe.throw(_("Evaluator is required for paid certificates."))
|
||||||
|
|
||||||
|
|||||||
@@ -869,15 +869,20 @@ def get_evaluator(course, batch=None):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@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(
|
upcoming_evals = frappe.get_all(
|
||||||
"LMS Certificate Request",
|
"LMS Certificate Request",
|
||||||
{
|
filters,
|
||||||
"member": student,
|
|
||||||
"course": ["in", courses],
|
|
||||||
"date": [">=", frappe.utils.nowdate()],
|
|
||||||
"status": "Upcoming",
|
|
||||||
},
|
|
||||||
[
|
[
|
||||||
"name",
|
"name",
|
||||||
"date",
|
"date",
|
||||||
|
|||||||
Reference in New Issue
Block a user