@@ -2,7 +2,7 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" href="/favicon.png" />
|
<link rel="icon" href="{{ favicon or '/assets/lms/frontend/favicon.png' }}" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Frappe Learning</title>
|
<title>Frappe Learning</title>
|
||||||
<meta name="title" content="{{ meta.title }}" />
|
<meta name="title" content="{{ meta.title }}" />
|
||||||
|
|||||||
@@ -17,12 +17,6 @@
|
|||||||
>
|
>
|
||||||
<template #body-content>
|
<template #body-content>
|
||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
<FormControl
|
|
||||||
type="select"
|
|
||||||
v-model="details.course"
|
|
||||||
:label="__('Course')"
|
|
||||||
:options="getCourses()"
|
|
||||||
/>
|
|
||||||
<Link
|
<Link
|
||||||
v-model="details.evaluator"
|
v-model="details.evaluator"
|
||||||
:label="__('Evaluator')"
|
:label="__('Evaluator')"
|
||||||
@@ -38,6 +32,12 @@
|
|||||||
v-model="details.expiry_date"
|
v-model="details.expiry_date"
|
||||||
:label="__('Expiry Date')"
|
:label="__('Expiry Date')"
|
||||||
/>
|
/>
|
||||||
|
<FormControl
|
||||||
|
type="select"
|
||||||
|
v-model="details.course"
|
||||||
|
:label="__('Course')"
|
||||||
|
:options="getCourses()"
|
||||||
|
/>
|
||||||
<Link
|
<Link
|
||||||
v-model="details.template"
|
v-model="details.template"
|
||||||
:label="__('Template')"
|
:label="__('Template')"
|
||||||
@@ -94,7 +94,7 @@ const createCertificate = createResource({
|
|||||||
template: details.template,
|
template: details.template,
|
||||||
published: details.published,
|
published: details.published,
|
||||||
course: values.course,
|
course: values.course,
|
||||||
batch: values.batch,
|
batch_name: values.batch,
|
||||||
member: values.member,
|
member: values.member,
|
||||||
evaluator: details.evaluator,
|
evaluator: details.evaluator,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -38,10 +38,10 @@
|
|||||||
v-model="title"
|
v-model="title"
|
||||||
:placeholder="__('Search by Title')"
|
:placeholder="__('Search by Title')"
|
||||||
type="text"
|
type="text"
|
||||||
class="min-w-40 lg:min-w-0 lg:w-32"
|
class="min-w-40 lg:min-w-0 lg:w-32 xl:w-40"
|
||||||
@input="updateBatches()"
|
@input="updateBatches()"
|
||||||
/>
|
/>
|
||||||
<div class="min-w-40 lg:min-w-0 lg:w-32">
|
<div class="min-w-40 lg:min-w-0 lg:w-32 xl:w-40">
|
||||||
<Select
|
<Select
|
||||||
v-if="categories.length"
|
v-if="categories.length"
|
||||||
v-model="currentCategory"
|
v-model="currentCategory"
|
||||||
@@ -101,6 +101,7 @@ import {
|
|||||||
} from 'frappe-ui'
|
} from 'frappe-ui'
|
||||||
import { computed, inject, onMounted, ref, watch } from 'vue'
|
import { computed, inject, onMounted, ref, watch } from 'vue'
|
||||||
import { BookOpen, Plus } from 'lucide-vue-next'
|
import { BookOpen, Plus } from 'lucide-vue-next'
|
||||||
|
import { updateDocumentTitle } from '@/utils'
|
||||||
import BatchCard from '@/components/BatchCard.vue'
|
import BatchCard from '@/components/BatchCard.vue'
|
||||||
|
|
||||||
const user = inject('$user')
|
const user = inject('$user')
|
||||||
@@ -188,6 +189,8 @@ const updateTabFilter = () => {
|
|||||||
}
|
}
|
||||||
if (currentTab.value == 'Enrolled' && user.data?.is_student) {
|
if (currentTab.value == 'Enrolled' && user.data?.is_student) {
|
||||||
filters.value['enrolled'] = 1
|
filters.value['enrolled'] = 1
|
||||||
|
delete filters.value['start_date']
|
||||||
|
delete filters.value['published']
|
||||||
orderBy.value = 'start_date desc'
|
orderBy.value = 'start_date desc'
|
||||||
} else if (user.data?.is_student) {
|
} else if (user.data?.is_student) {
|
||||||
delete filters.value['enrolled']
|
delete filters.value['enrolled']
|
||||||
@@ -200,7 +203,7 @@ const updateTabFilter = () => {
|
|||||||
filters.value['published'] = 1
|
filters.value['published'] = 1
|
||||||
orderBy.value = 'start_date'
|
orderBy.value = 'start_date'
|
||||||
} else if (currentTab.value == 'Archived') {
|
} else if (currentTab.value == 'Archived') {
|
||||||
filters.value['start_date'] = ['<', dayjs().format('YYYY-MM-DD')]
|
filters.value['start_date'] = ['<=', dayjs().format('YYYY-MM-DD')]
|
||||||
} else if (currentTab.value == 'Unpublished') {
|
} else if (currentTab.value == 'Unpublished') {
|
||||||
filters.value['published'] = 0
|
filters.value['published'] = 0
|
||||||
}
|
}
|
||||||
@@ -208,7 +211,7 @@ const updateTabFilter = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const updateStudentFilter = () => {
|
const updateStudentFilter = () => {
|
||||||
if (!user.data || user.data?.is_student) {
|
if (!user.data || (user.data?.is_student && currentTab.value != 'Enrolled')) {
|
||||||
filters.value['start_date'] = ['>=', dayjs().format('YYYY-MM-DD')]
|
filters.value['start_date'] = ['>=', dayjs().format('YYYY-MM-DD')]
|
||||||
filters.value['published'] = 1
|
filters.value['published'] = 1
|
||||||
}
|
}
|
||||||
@@ -283,4 +286,13 @@ const breadcrumbs = computed(() => [
|
|||||||
route: { name: 'Batches' },
|
route: { name: 'Batches' },
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
|
const pageMeta = computed(() => {
|
||||||
|
return {
|
||||||
|
title: 'Batches',
|
||||||
|
description: 'All upcoming batches.',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
updateDocumentTitle(pageMeta)
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -7,14 +7,14 @@
|
|||||||
<div
|
<div
|
||||||
v-for="certificate in certificates.data"
|
v-for="certificate in certificates.data"
|
||||||
:key="certificate.name"
|
:key="certificate.name"
|
||||||
class="bg-white shadow rounded-lg p-3 cursor-pointer"
|
class="flex flex-col bg-white shadow rounded-lg p-3 cursor-pointer hover:bg-gray-50"
|
||||||
@click="openCertificate(certificate)"
|
@click="openCertificate(certificate)"
|
||||||
>
|
>
|
||||||
<div class="font-medium leading-5">
|
<div class="font-medium leading-5 mb-2">
|
||||||
{{ certificate.course_title }}
|
{{ certificate.course_title || certificate.batch_title }}
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-2">
|
<div class="text-sm text-gray-700 font-medium mt-auto">
|
||||||
<span class="text-xs text-gray-700"> {{ __('issued on') }}: </span>
|
<span> {{ __('Issued on') }}: </span>
|
||||||
{{ dayjs(certificate.issue_date).format('DD MMM YYYY') }}
|
{{ dayjs(certificate.issue_date).format('DD MMM YYYY') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -22,8 +22,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { createResource } from 'frappe-ui'
|
import { createListResource } from 'frappe-ui'
|
||||||
import { inject } from 'vue'
|
import { inject, onMounted } from 'vue'
|
||||||
|
|
||||||
const dayjs = inject('$dayjs')
|
const dayjs = inject('$dayjs')
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -33,12 +33,19 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const certificates = createResource({
|
onMounted(() => {
|
||||||
url: 'lms.lms.api.get_certificates',
|
if (props.profile.data?.name) {
|
||||||
params: {
|
certificates.reload()
|
||||||
member: props.profile.data.name,
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const certificates = createListResource({
|
||||||
|
doctype: 'LMS Certificate',
|
||||||
|
filters: {
|
||||||
|
member: props.profile.data?.name,
|
||||||
},
|
},
|
||||||
auto: true,
|
fields: ['name', 'course_title', 'batch_title', 'issue_date'],
|
||||||
|
cache: ['certificates', props.profile.data?.name],
|
||||||
})
|
})
|
||||||
|
|
||||||
const openCertificate = (certificate) => {
|
const openCertificate = (certificate) => {
|
||||||
|
|||||||
@@ -407,17 +407,6 @@ def get_assigned_badges(member):
|
|||||||
return assigned_badges
|
return assigned_badges
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
|
||||||
def get_certificates(member):
|
|
||||||
"""Get certificates for a member."""
|
|
||||||
return frappe.get_all(
|
|
||||||
"LMS Certificate",
|
|
||||||
filters={"member": member},
|
|
||||||
fields=["name", "course", "course_title", "issue_date", "template"],
|
|
||||||
order_by="creation desc",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_all_users():
|
def get_all_users():
|
||||||
users = frappe.get_all(
|
users = frappe.get_all(
|
||||||
|
|||||||
@@ -1,25 +1,27 @@
|
|||||||
{
|
{
|
||||||
"actions": [],
|
"actions": [],
|
||||||
"allow_import": 1,
|
"allow_import": 1,
|
||||||
|
"autoname": "hash",
|
||||||
"creation": "2021-08-16 15:47:19.494055",
|
"creation": "2021-08-16 15:47:19.494055",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
"field_order": [
|
"field_order": [
|
||||||
"course",
|
|
||||||
"course_title",
|
|
||||||
"member",
|
"member",
|
||||||
"member_name",
|
"member_name",
|
||||||
"column_break_vwbn",
|
|
||||||
"issue_date",
|
|
||||||
"template",
|
|
||||||
"published",
|
|
||||||
"section_break_scyf",
|
|
||||||
"evaluator",
|
"evaluator",
|
||||||
"evaluator_name",
|
"evaluator_name",
|
||||||
"column_break_slaw",
|
"column_break_vwbn",
|
||||||
|
"issue_date",
|
||||||
"expiry_date",
|
"expiry_date",
|
||||||
"batch_name"
|
"template",
|
||||||
|
"published",
|
||||||
|
"section_break_unwn",
|
||||||
|
"course",
|
||||||
|
"course_title",
|
||||||
|
"column_break_ywee",
|
||||||
|
"batch_name",
|
||||||
|
"batch_title"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@@ -32,11 +34,9 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "course",
|
"fieldname": "course",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"in_list_view": 1,
|
|
||||||
"in_standard_filter": 1,
|
"in_standard_filter": 1,
|
||||||
"label": "Course",
|
"label": "Course",
|
||||||
"options": "LMS Course",
|
"options": "LMS Course"
|
||||||
"reqd": 1
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "expiry_date",
|
"fieldname": "expiry_date",
|
||||||
@@ -46,7 +46,6 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "member",
|
"fieldname": "member",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"in_list_view": 1,
|
|
||||||
"in_standard_filter": 1,
|
"in_standard_filter": 1,
|
||||||
"label": "Member",
|
"label": "Member",
|
||||||
"options": "User",
|
"options": "User",
|
||||||
@@ -56,6 +55,8 @@
|
|||||||
"fetch_from": "member.full_name",
|
"fetch_from": "member.full_name",
|
||||||
"fieldname": "member_name",
|
"fieldname": "member_name",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"in_standard_filter": 1,
|
||||||
"label": "Member Name",
|
"label": "Member Name",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
@@ -90,14 +91,6 @@
|
|||||||
"fieldname": "column_break_vwbn",
|
"fieldname": "column_break_vwbn",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"fieldname": "section_break_scyf",
|
|
||||||
"fieldtype": "Section Break"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "column_break_slaw",
|
|
||||||
"fieldtype": "Column Break"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "evaluator",
|
"fieldname": "evaluator",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
@@ -108,16 +101,33 @@
|
|||||||
"fetch_from": "evaluator.full_name",
|
"fetch_from": "evaluator.full_name",
|
||||||
"fieldname": "evaluator_name",
|
"fieldname": "evaluator_name",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
|
"in_list_view": 1,
|
||||||
"label": "Evaluator Name",
|
"label": "Evaluator Name",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_unwn",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_ywee",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fetch_from": "batch_name.title",
|
||||||
|
"fieldname": "batch_title",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"label": "Batch Title",
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-09-11 11:37:20.419956",
|
"modified": "2025-01-16 12:12:49.998114",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "LMS",
|
"module": "LMS",
|
||||||
"name": "LMS Certificate",
|
"name": "LMS Certificate",
|
||||||
|
"naming_rule": "Random",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1861,8 +1861,6 @@ def get_batches(filters=None, start=0, page_length=20, order_by="start_date"):
|
|||||||
)
|
)
|
||||||
filters.update({"name": ["in", enrolled_batches]})
|
filters.update({"name": ["in", enrolled_batches]})
|
||||||
del filters["enrolled"]
|
del filters["enrolled"]
|
||||||
del filters["published"]
|
|
||||||
del filters["start_date"]
|
|
||||||
|
|
||||||
batches = frappe.get_all(
|
batches = frappe.get_all(
|
||||||
"LMS Batch",
|
"LMS Batch",
|
||||||
@@ -1889,6 +1887,46 @@ def get_batches(filters=None, start=0, page_length=20, order_by="start_date"):
|
|||||||
page_length=page_length,
|
page_length=page_length,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
batches = filter_batches_based_on_start_time(batches, filters)
|
||||||
|
batches = get_batch_card_details(batches)
|
||||||
|
return batches
|
||||||
|
|
||||||
|
|
||||||
|
def filter_batches_based_on_start_time(batches, filters):
|
||||||
|
batchType = get_batch_type(filters)
|
||||||
|
if batchType == "upcoming":
|
||||||
|
batches_to_remove = [
|
||||||
|
batch
|
||||||
|
for batch in batches
|
||||||
|
if getdate(batch.start_date) == getdate()
|
||||||
|
and get_time_str(batch.start_time) < nowtime()
|
||||||
|
]
|
||||||
|
batches = [batch for batch in batches if batch not in batches_to_remove]
|
||||||
|
elif batchType == "archived":
|
||||||
|
batches_to_remove = [
|
||||||
|
batch
|
||||||
|
for batch in batches
|
||||||
|
if getdate(batch.start_date) == getdate()
|
||||||
|
and get_time_str(batch.start_time) >= nowtime()
|
||||||
|
]
|
||||||
|
batches = [batch for batch in batches if batch not in batches_to_remove]
|
||||||
|
return batches
|
||||||
|
|
||||||
|
|
||||||
|
def get_batch_type(filters):
|
||||||
|
start_date_filter = filters.get("start_date")
|
||||||
|
batchType = None
|
||||||
|
if start_date_filter:
|
||||||
|
sign = start_date_filter[0]
|
||||||
|
if ">" in sign:
|
||||||
|
batchType = "upcoming"
|
||||||
|
elif "<" in sign:
|
||||||
|
batchType = "archived"
|
||||||
|
|
||||||
|
return batchType
|
||||||
|
|
||||||
|
|
||||||
|
def get_batch_card_details(batches):
|
||||||
for batch in batches:
|
for batch in batches:
|
||||||
batch.instructors = get_instructors(batch.name)
|
batch.instructors = get_instructors(batch.name)
|
||||||
students_count = frappe.db.count("Batch Student", {"parent": batch.name})
|
students_count = frappe.db.count("Batch Student", {"parent": batch.name})
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ def get_context():
|
|||||||
frappe.db.commit() # nosemgrep
|
frappe.db.commit() # nosemgrep
|
||||||
context.csrf_token = csrf_token
|
context.csrf_token = csrf_token
|
||||||
capture("active_site", "lms")
|
capture("active_site", "lms")
|
||||||
|
context.favicon = frappe.db.get_single_value("Website Settings", "favicon")
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user