fix: evaluator info and other styles
This commit is contained in:
@@ -4,10 +4,14 @@
|
|||||||
{{ props.label }}
|
{{ props.label }}
|
||||||
</label>
|
</label>
|
||||||
<div class="flex text-center">
|
<div class="flex text-center">
|
||||||
<div v-for="index in 5" @mouseover="hoveredRating = index" @mouseleave="hoveredRating = 0">
|
<div
|
||||||
|
v-for="index in 5"
|
||||||
|
@mouseover="hoveredRating = index"
|
||||||
|
@mouseleave="hoveredRating = 0"
|
||||||
|
>
|
||||||
<Star
|
<Star
|
||||||
class="h-6 w-6 fill-gray-400 text-gray-50 stroke-1 mr-1 cursor-pointer"
|
class="fill-gray-400 text-gray-50 stroke-1 mr-1 cursor-pointer"
|
||||||
:class="{ 'fill-yellow-200': (index <= hoveredRating && index > rating), 'fill-yellow-500': index <= rating }"
|
:class="iconClasses(index)"
|
||||||
@click="markRating(index)"
|
@click="markRating(index)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -17,7 +21,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { Star } from 'lucide-vue-next'
|
import { Star } from 'lucide-vue-next'
|
||||||
import { ref, watch } from 'vue'
|
import { computed, ref, watch } from 'vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
id: {
|
id: {
|
||||||
@@ -32,8 +36,29 @@ const props = defineProps({
|
|||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
|
size: {
|
||||||
|
type: String,
|
||||||
|
default: 'md',
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const iconClasses = (index) => {
|
||||||
|
let classes = [
|
||||||
|
{
|
||||||
|
sm: 'size-4',
|
||||||
|
md: 'size-5',
|
||||||
|
lg: 'size-6',
|
||||||
|
xl: 'size-7',
|
||||||
|
}[props.size],
|
||||||
|
]
|
||||||
|
if (index <= hoveredRating.value && index > rating.value) {
|
||||||
|
classes.push('fill-yellow-200')
|
||||||
|
} else if (index <= rating.value) {
|
||||||
|
classes.push('fill-yellow-500')
|
||||||
|
}
|
||||||
|
return classes.join(' ')
|
||||||
|
}
|
||||||
|
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
const rating = ref(props.modelValue)
|
const rating = ref(props.modelValue)
|
||||||
const hoveredRating = ref(0)
|
const hoveredRating = ref(0)
|
||||||
@@ -47,7 +72,10 @@ function markRating(index) {
|
|||||||
rating.value = index
|
rating.value = index
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(() => props.modelValue, (newVal) => {
|
watch(
|
||||||
rating.value = newVal
|
() => props.modelValue,
|
||||||
})
|
(newVal) => {
|
||||||
|
rating.value = newVal
|
||||||
|
}
|
||||||
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,309 +1,378 @@
|
|||||||
<template>
|
<template>
|
||||||
<Dialog
|
<Dialog
|
||||||
v-model="show"
|
v-model="show"
|
||||||
:options="{
|
:options="{
|
||||||
size: '2xl',
|
size: '2xl',
|
||||||
}">
|
}"
|
||||||
<template #body>
|
>
|
||||||
<div class="flex text-base">
|
<template #body>
|
||||||
<div class="flex flex-col w-1/2 p-5">
|
<div class="flex text-base">
|
||||||
<div class="text-lg font-semibold mb-4">
|
<div class="flex flex-col w-1/2 p-5">
|
||||||
{{ event.title }}
|
<div class="text-lg font-semibold mb-4">
|
||||||
</div>
|
{{ event.title }}
|
||||||
|
</div>
|
||||||
<div class="flex flex-col space-y-4 text-sm text-gray-800">
|
|
||||||
<Tooltip :text="__('Email ID')">
|
<div class="flex flex-col space-y-4 text-sm text-gray-800">
|
||||||
<div class="flex items-center space-x-2 w-fit">
|
<Tooltip :text="__('Email ID')">
|
||||||
<User class="h-4 w-4 stroke-1.5" />
|
<div class="flex items-center space-x-2 w-fit">
|
||||||
<span>
|
<User class="h-4 w-4 stroke-1.5" />
|
||||||
{{ event.member }}
|
<span>
|
||||||
</span>
|
{{ event.member }}
|
||||||
</div>
|
</span>
|
||||||
</Tooltip>
|
</div>
|
||||||
<Tooltip :text="__('Course')">
|
</Tooltip>
|
||||||
<div class="flex items-center space-x-2 w-fit">
|
<Tooltip :text="__('Course')">
|
||||||
<BookOpen class="h-4 w-4 stroke-1.5" />
|
<div class="flex items-center space-x-2 w-fit">
|
||||||
<span>
|
<BookOpen class="h-4 w-4 stroke-1.5" />
|
||||||
{{ event.course_title }}
|
<span>
|
||||||
</span>
|
{{ event.course_title }}
|
||||||
</div>
|
</span>
|
||||||
</Tooltip>
|
</div>
|
||||||
<Tooltip v-if="event.batch_title" :text="__('Batch')">
|
</Tooltip>
|
||||||
<div class="flex items-center space-x-2 w-fit">
|
<Tooltip v-if="event.batch_title" :text="__('Batch')">
|
||||||
<Users class="h-4 w-4 stroke-1.5" />
|
<div class="flex items-center space-x-2 w-fit">
|
||||||
<span>
|
<Users class="h-4 w-4 stroke-1.5" />
|
||||||
{{ event.batch_title }}
|
<span>
|
||||||
</span>
|
{{ event.batch_title }}
|
||||||
</div>
|
</span>
|
||||||
</Tooltip>
|
</div>
|
||||||
<Tooltip :text="__('Date')">
|
</Tooltip>
|
||||||
<div class="flex items-center space-x-2 w-fit">
|
<Tooltip :text="__('Date')">
|
||||||
<Calendar class="h-4 w-4 stroke-1.5" />
|
<div class="flex items-center space-x-2 w-fit">
|
||||||
<span>
|
<Calendar class="h-4 w-4 stroke-1.5" />
|
||||||
{{ dayjs(event.date).format("DD MMM YYYY") }}
|
<span>
|
||||||
</span>
|
{{ dayjs(event.date).format('DD MMM YYYY') }}
|
||||||
</div>
|
</span>
|
||||||
</Tooltip>
|
</div>
|
||||||
<Tooltip :text="__('Time')">
|
</Tooltip>
|
||||||
<div class="flex items-center space-x-2 w-fit">
|
<Tooltip :text="__('Time')">
|
||||||
<Clock class="h-4 w-4 stroke-1.5" />
|
<div class="flex items-center space-x-2 w-fit">
|
||||||
<span>
|
<Clock class="h-4 w-4 stroke-1.5" />
|
||||||
{{ formatTime(event.start_time) }} - {{ formatTime(event.end_time) }}
|
<span>
|
||||||
</span>
|
{{ formatTime(event.start_time) }} -
|
||||||
</div>
|
{{ formatTime(event.end_time) }}
|
||||||
</Tooltip>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center space-x-2 mt-auto">
|
</Tooltip>
|
||||||
<Button v-if="certificate.name" @click="openCertificate(certificate)" class="w-full">
|
</div>
|
||||||
<template #prefix>
|
<div class="flex items-center space-x-2 mt-auto">
|
||||||
<FileText class="h-4 w-4 stroke-1.5" />
|
<Button
|
||||||
</template>
|
v-if="certificate.name"
|
||||||
{{ __("View Certificate") }}
|
@click="openCertificate(certificate)"
|
||||||
</Button>
|
class="w-full"
|
||||||
<Button v-else @click="openCallLink(event.venue)" class="w-full">
|
>
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<Video class="h-4 w-4 stroke-1.5" />
|
<FileText class="h-4 w-4 stroke-1.5" />
|
||||||
</template>
|
</template>
|
||||||
<span>
|
{{ __('View Certificate') }}
|
||||||
{{ __("Join Meeting") }}
|
</Button>
|
||||||
</span>
|
<Button v-else @click="openCallLink(event.venue)" class="w-full">
|
||||||
</Button>
|
<template #prefix>
|
||||||
</div>
|
<Video class="h-4 w-4 stroke-1.5" />
|
||||||
</div>
|
</template>
|
||||||
<Tabs :tabs='tabs' v-model="tabIndex" class="border-l w-1/2">
|
<span>
|
||||||
<template #default="{ tab }">
|
{{ __('Join Meeting') }}
|
||||||
<div v-if="tab.label == 'Evaluation'" class="flex flex-col space-y-4 p-5">
|
</span>
|
||||||
<Rating v-model="evaluation.rating" :label="__('Rating')"/>
|
</Button>
|
||||||
<FormControl type="select" :options='[{
|
</div>
|
||||||
value: "Pending",
|
</div>
|
||||||
label: __("Pending")
|
<Tabs :tabs="tabs" v-model="tabIndex" class="border-l w-1/2">
|
||||||
}, {
|
<template #default="{ tab }">
|
||||||
value: "In Progress",
|
<div
|
||||||
label: __("In Progress")
|
v-if="tab.label == 'Evaluation'"
|
||||||
}, {
|
class="flex flex-col space-y-4 p-5"
|
||||||
value: "Pass",
|
>
|
||||||
label: __("Pass")
|
<div class="flex items-center justify-between">
|
||||||
}, {
|
<Rating v-model="evaluation.rating" :label="__('Rating')" />
|
||||||
value: "Fail",
|
<FormControl
|
||||||
label: __("Fail")
|
type="select"
|
||||||
}]'
|
:options="statusOptions"
|
||||||
v-model="evaluation.status" :label="__('Status')" />
|
v-model="evaluation.status"
|
||||||
<FormControl type="textarea" v-model="evaluation.summary" :label="__('Summary')" />
|
:label="__('Status')"
|
||||||
<Button variant="solid" @click="saveEvaluation()">
|
class="w-1/2"
|
||||||
{{ __("Save") }}
|
/>
|
||||||
</Button>
|
</div>
|
||||||
</div>
|
<Textarea
|
||||||
<div v-else class="flex flex-col space-y-4 p-5">
|
v-model="evaluation.summary"
|
||||||
|
:label="__('Summary')"
|
||||||
<FormControl type="checkbox" v-model="certificate.published" :label="__('Published')" />
|
:rows="7"
|
||||||
<Link v-model="certificate.template" :label="__('Template')" doctype="Print Format" :filters='{
|
/>
|
||||||
"doc_type": "LMS Certificate"
|
<Button variant="solid" @click="saveEvaluation()">
|
||||||
}' />
|
{{ __('Save') }}
|
||||||
<FormControl type="date" v-model="certificate.issue_date" :label="__('Issue Date')" />
|
</Button>
|
||||||
<FormControl type="date" v-model="certificate.expiry_date" :label="__('Expiry Date')" />
|
</div>
|
||||||
<Button variant="solid" @click="saveCertificate()">
|
<div v-else class="flex flex-col space-y-4 p-5">
|
||||||
{{ __("Save") }}
|
<FormControl
|
||||||
</Button>
|
type="checkbox"
|
||||||
</div>
|
v-model="certificate.published"
|
||||||
</template>
|
:label="__('Published')"
|
||||||
</Tabs>
|
/>
|
||||||
</div>
|
<Link
|
||||||
</template>
|
v-model="certificate.template"
|
||||||
</Dialog>
|
:label="__('Template')"
|
||||||
|
doctype="Print Format"
|
||||||
|
:filters="{
|
||||||
|
doc_type: 'LMS Certificate',
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
<FormControl
|
||||||
|
type="date"
|
||||||
|
v-model="certificate.issue_date"
|
||||||
|
:label="__('Issue Date')"
|
||||||
|
/>
|
||||||
|
<FormControl
|
||||||
|
type="date"
|
||||||
|
v-model="certificate.expiry_date"
|
||||||
|
:label="__('Expiry Date')"
|
||||||
|
/>
|
||||||
|
<Button variant="solid" @click="saveCertificate()">
|
||||||
|
{{ __('Save') }}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Tabs>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Dialog>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { Dialog, Button, FormControl, createResource, Tabs, Tooltip } from 'frappe-ui';
|
import {
|
||||||
import { User, Calendar, Clock, Video, BookOpen, FileText, GraduationCap, Users, ClipboardList } from "lucide-vue-next"
|
Dialog,
|
||||||
import { inject, reactive, watch, ref, computed } from "vue"
|
Button,
|
||||||
import { formatTime, showToast } from "@/utils"
|
FormControl,
|
||||||
import Rating from "@/components/Controls/Rating.vue"
|
createResource,
|
||||||
import Link from "@/components/Controls/Link.vue"
|
Tabs,
|
||||||
|
Tooltip,
|
||||||
|
Textarea,
|
||||||
|
} from 'frappe-ui'
|
||||||
|
import {
|
||||||
|
User,
|
||||||
|
Calendar,
|
||||||
|
Clock,
|
||||||
|
Video,
|
||||||
|
BookOpen,
|
||||||
|
FileText,
|
||||||
|
GraduationCap,
|
||||||
|
Users,
|
||||||
|
ClipboardList,
|
||||||
|
} from 'lucide-vue-next'
|
||||||
|
import { inject, reactive, watch, ref, computed } from 'vue'
|
||||||
|
import { formatTime, showToast } from '@/utils'
|
||||||
|
import Rating from '@/components/Controls/Rating.vue'
|
||||||
|
import Link from '@/components/Controls/Link.vue'
|
||||||
|
|
||||||
const show = defineModel()
|
const show = defineModel()
|
||||||
const dayjs = inject("$dayjs")
|
const dayjs = inject('$dayjs')
|
||||||
const tabIndex = ref(0)
|
const tabIndex = ref(0)
|
||||||
const showCertification = ref(false)
|
const showCertification = ref(false)
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
event: {
|
event: {
|
||||||
type: [Object, null],
|
type: [Object, null],
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
||||||
const evaluation = reactive({})
|
const evaluation = reactive({})
|
||||||
|
|
||||||
const certificate = reactive({})
|
const certificate = reactive({})
|
||||||
|
|
||||||
const defaultTemplate = createResource({
|
const defaultTemplate = createResource({
|
||||||
url: "frappe.client.get_value",
|
url: 'frappe.client.get_value',
|
||||||
makeParams(values) {
|
makeParams(values) {
|
||||||
return {
|
return {
|
||||||
doctype: "Property Setter",
|
doctype: 'Property Setter',
|
||||||
fieldname: "value",
|
fieldname: 'value',
|
||||||
filters: {
|
filters: {
|
||||||
doc_type: "LMS Certificate",
|
doc_type: 'LMS Certificate',
|
||||||
property: "default_print_format"
|
property: 'default_print_format',
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
auto: true,
|
auto: true,
|
||||||
onSuccess(data) {
|
onSuccess(data) {
|
||||||
certificate.template = data.value
|
certificate.template = data.value
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const openCallLink = (link) => {
|
const openCallLink = (link) => {
|
||||||
window.open(link, "_blank")
|
window.open(link, '_blank')
|
||||||
}
|
}
|
||||||
|
|
||||||
const evaluationResource = createResource({
|
const evaluationResource = createResource({
|
||||||
url: "lms.lms.api.save_evaluation_details",
|
url: 'lms.lms.api.save_evaluation_details',
|
||||||
makeParams(values) {
|
makeParams(values) {
|
||||||
return {
|
return {
|
||||||
member: props.event.member,
|
member: props.event.member,
|
||||||
course: props.event.course,
|
course: props.event.course,
|
||||||
batch_name: props.event.batch_name,
|
batch_name: props.event.batch_name,
|
||||||
date: props.event.date,
|
date: props.event.date,
|
||||||
start_time: props.event.start_time,
|
start_time: props.event.start_time,
|
||||||
end_time: props.event.end_time,
|
end_time: props.event.end_time,
|
||||||
status: evaluation.status,
|
status: evaluation.status,
|
||||||
rating: evaluation.rating,
|
rating: evaluation.rating,
|
||||||
summary: evaluation.summary,
|
summary: evaluation.summary,
|
||||||
}
|
evaluator: props.event.evaluator,
|
||||||
},
|
}
|
||||||
auto: false,
|
},
|
||||||
onSuccess(data) {
|
auto: false,
|
||||||
evaluation.name = data.name
|
onSuccess(data) {
|
||||||
}
|
evaluation.name = data.name
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const evaluationDetails = createResource({
|
const evaluationDetails = createResource({
|
||||||
url: "frappe.client.get",
|
url: 'frappe.client.get',
|
||||||
makeParams(values) {
|
makeParams(values) {
|
||||||
return {
|
return {
|
||||||
doctype: "LMS Certificate Evaluation",
|
doctype: 'LMS Certificate Evaluation',
|
||||||
filters: {
|
filters: {
|
||||||
member: props.event.member,
|
member: props.event.member,
|
||||||
course: props.event.course,
|
course: props.event.course,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onSuccess(data) {
|
onSuccess(data) {
|
||||||
for (const key in data) {
|
for (const key in data) {
|
||||||
if (key in evaluation)
|
if (key in evaluation) evaluation[key] = data[key]
|
||||||
evaluation[key] = data[key]
|
if (key == 'rating') evaluation.rating = data.rating * 5
|
||||||
if (key == "rating")
|
if (evaluation.status == 'Pass') showCertification.value = true
|
||||||
evaluation.rating = data.rating * 5
|
}
|
||||||
if (evaluation.status == "Pass")
|
},
|
||||||
showCertification.value = true
|
auto: false,
|
||||||
}
|
|
||||||
},
|
|
||||||
auto: false
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const saveEvaluation = () => {
|
const saveEvaluation = () => {
|
||||||
evaluationResource.submit({}, {
|
evaluationResource.submit(
|
||||||
onSuccess: () => {
|
{},
|
||||||
if (evaluation.status == "Pass") {
|
{
|
||||||
showCertification.value = true
|
onSuccess: () => {
|
||||||
} else {
|
if (evaluation.status == 'Pass') {
|
||||||
show.value = false
|
showCertification.value = true
|
||||||
}
|
} else {
|
||||||
showToast( __("Success"), __("Evaluation saved successfully"), "check")
|
show.value = false
|
||||||
}
|
}
|
||||||
})
|
showToast(__('Success'), __('Evaluation saved successfully'), 'check')
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const certificateResource = createResource({
|
const certificateResource = createResource({
|
||||||
url: "lms.lms.api.save_certificate_details",
|
url: 'lms.lms.api.save_certificate_details',
|
||||||
makeParams(values) {
|
makeParams(values) {
|
||||||
return {
|
return {
|
||||||
member: props.event.member,
|
member: props.event.member,
|
||||||
course: props.event.course,
|
course: props.event.course,
|
||||||
batch_name: props.event.batch_name,
|
batch_name: props.event.batch_name,
|
||||||
published: certificate.published,
|
published: certificate.published,
|
||||||
issue_date: certificate.issue_date,
|
issue_date: certificate.issue_date,
|
||||||
expiry_date: certificate.expiry_date,
|
expiry_date: certificate.expiry_date,
|
||||||
template: certificate.template,
|
template: certificate.template,
|
||||||
}
|
evaluator: props.event.evaluator,
|
||||||
},
|
}
|
||||||
auto: false,
|
},
|
||||||
onSuccess(data) {
|
auto: false,
|
||||||
certificate.name = data
|
onSuccess(data) {
|
||||||
}
|
certificate.name = data
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const certificateDetails = createResource({
|
const certificateDetails = createResource({
|
||||||
url: "frappe.client.get",
|
url: 'frappe.client.get',
|
||||||
makeParams(values) {
|
makeParams(values) {
|
||||||
return {
|
return {
|
||||||
doctype: "LMS Certificate",
|
doctype: 'LMS Certificate',
|
||||||
filters: {
|
filters: {
|
||||||
member: props.event.member,
|
member: props.event.member,
|
||||||
course: props.event.course,
|
course: props.event.course,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onSuccess(data) {
|
onSuccess(data) {
|
||||||
for (const key in data) {
|
for (const key in data) {
|
||||||
if (key in certificate)
|
if (key in certificate) certificate[key] = data[key]
|
||||||
certificate[key] = data[key]
|
certificate.name = data.name
|
||||||
certificate.name = data.name
|
showCertification.value = true
|
||||||
showCertification.value = true
|
}
|
||||||
}
|
},
|
||||||
},
|
onError(err) {
|
||||||
onError(err) {
|
certificate.template = defaultTemplate.data.value
|
||||||
certificate.template = defaultTemplate.data.value
|
},
|
||||||
},
|
auto: false,
|
||||||
auto: false
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const saveCertificate = () => {
|
const saveCertificate = () => {
|
||||||
certificateResource.submit({}, {
|
certificateResource.submit(
|
||||||
onSuccess: () => {
|
{},
|
||||||
showToast( __("Success"), __("Certificate saved successfully"), "check")
|
{
|
||||||
}
|
onSuccess: () => {
|
||||||
})
|
showToast(__('Success'), __('Certificate saved successfully'), 'check')
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(show, () => {
|
watch(show, () => {
|
||||||
if (show.value) {
|
if (show.value) {
|
||||||
evaluation.rating = 0
|
evaluation.rating = 0
|
||||||
evaluation.status = "Pending"
|
evaluation.status = 'Pending'
|
||||||
evaluation.summary = ""
|
evaluation.summary = ''
|
||||||
evaluationDetails.reload()
|
evaluationDetails.reload()
|
||||||
|
|
||||||
certificate.published = true
|
certificate.published = true
|
||||||
certificate.issue_date = dayjs().format("YYYY-MM-DD")
|
certificate.issue_date = dayjs().format('YYYY-MM-DD')
|
||||||
certificate.expiry_date = null
|
certificate.expiry_date = null
|
||||||
certificate.template = null
|
certificate.template = null
|
||||||
certificate.name = null
|
certificate.name = null
|
||||||
certificateDetails.reload()
|
certificateDetails.reload()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const openCertificate = (certificate) => {
|
const openCertificate = (certificate) => {
|
||||||
window.open(
|
window.open(
|
||||||
`/api/method/frappe.utils.print_format.download_pdf?doctype=LMS+Certificate&name=${certificate.name}&format=${encodeURIComponent(certificate.template)}`
|
`/api/method/frappe.utils.print_format.download_pdf?doctype=LMS+Certificate&name=${
|
||||||
)
|
certificate.name
|
||||||
|
}&format=${encodeURIComponent(certificate.template)}`
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const tabs = computed(() => {
|
const statusOptions = computed(() => {
|
||||||
const tabsArray = [
|
return [
|
||||||
{
|
{
|
||||||
label: __("Evaluation"),
|
value: 'Pending',
|
||||||
icon: ClipboardList,
|
label: __('Pending'),
|
||||||
},
|
},
|
||||||
]
|
{
|
||||||
|
value: 'In Progress',
|
||||||
if (showCertification.value) {
|
label: __('In Progress'),
|
||||||
tabsArray.push({
|
},
|
||||||
label: __("Certificate"),
|
{
|
||||||
icon: GraduationCap,
|
value: 'Pass',
|
||||||
})
|
label: __('Pass'),
|
||||||
}
|
},
|
||||||
|
{
|
||||||
return tabsArray
|
value: 'Fail',
|
||||||
|
label: __('Fail'),
|
||||||
|
},
|
||||||
|
]
|
||||||
})
|
})
|
||||||
</script>
|
|
||||||
|
const tabs = computed(() => {
|
||||||
|
const tabsArray = [
|
||||||
|
{
|
||||||
|
label: __('Evaluation'),
|
||||||
|
icon: ClipboardList,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
if (showCertification.value) {
|
||||||
|
tabsArray.push({
|
||||||
|
label: __('Certificate'),
|
||||||
|
icon: GraduationCap,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return tabsArray
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|||||||
@@ -158,11 +158,11 @@ const setActiveTab = () => {
|
|||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
if (activeTab.value) {
|
if (activeTab.value) {
|
||||||
let route = {
|
let route = {
|
||||||
"About": { name: 'ProfileAbout' },
|
About: { name: 'ProfileAbout' },
|
||||||
"Certificates": { name: 'ProfileCertificates' },
|
Certificates: { name: 'ProfileCertificates' },
|
||||||
"Roles": { name: 'ProfileRoles' },
|
Roles: { name: 'ProfileRoles' },
|
||||||
"Slots": { name: 'ProfileEvaluator' },
|
Slots: { name: 'ProfileEvaluator' },
|
||||||
"Schedule": { name: 'ProfileEvaluationSchedule' },
|
Schedule: { name: 'ProfileEvaluationSchedule' },
|
||||||
}[activeTab.value]
|
}[activeTab.value]
|
||||||
router.push(route)
|
router.push(route)
|
||||||
}
|
}
|
||||||
@@ -186,7 +186,10 @@ const isSessionUser = () => {
|
|||||||
const getTabButtons = () => {
|
const getTabButtons = () => {
|
||||||
let buttons = [{ label: 'About' }, { label: 'Certificates' }]
|
let buttons = [{ label: 'About' }, { label: 'Certificates' }]
|
||||||
if ($user.data?.is_moderator) buttons.push({ label: 'Roles' })
|
if ($user.data?.is_moderator) buttons.push({ label: 'Roles' })
|
||||||
if (isSessionUser() && ($user.data?.is_evaluator || $user.data?.is_moderator)) {
|
if (
|
||||||
|
isSessionUser() &&
|
||||||
|
($user.data?.is_evaluator || $user.data?.is_moderator)
|
||||||
|
) {
|
||||||
buttons.push({ label: 'Slots' })
|
buttons.push({ label: 'Slots' })
|
||||||
buttons.push({ label: 'Schedule' })
|
buttons.push({ label: 'Schedule' })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,52 +1,49 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="mt-7 mb-20">
|
<div class="mt-7 mb-20">
|
||||||
<div class="flex h-screen flex-col overflow-hidden">
|
<div class="flex h-screen flex-col overflow-hidden">
|
||||||
<Calendar
|
<Calendar
|
||||||
v-if="evaluations.data?.length"
|
v-if="evaluations.data?.length"
|
||||||
:config="{
|
:config="{
|
||||||
defaultMode: 'Month',
|
defaultMode: 'Month',
|
||||||
disableModes: ['Day', 'Week'],
|
disableModes: ['Day', 'Week'],
|
||||||
redundantCellHeight: 100,
|
redundantCellHeight: 100,
|
||||||
enableShortcuts: false,
|
enableShortcuts: false,
|
||||||
}"
|
}"
|
||||||
:events="evaluations.data"
|
:events="evaluations.data"
|
||||||
@click="(event) => openEvent(event)"
|
@click="(event) => openEvent(event)"
|
||||||
>
|
>
|
||||||
<template #header="{currentMonthYear,
|
<template #header="{ currentMonthYear, decrement, increment }">
|
||||||
decrement,
|
<div class="mb-2 flex justify-between">
|
||||||
increment,
|
<span class="text-lg font-semibold">
|
||||||
}">
|
{{ currentMonthYear }}
|
||||||
<div class="mb-2 flex justify-between">
|
</span>
|
||||||
<span class="text-lg font-semibold">
|
<div class="flex gap-x-1">
|
||||||
{{ currentMonthYear }}
|
<Button
|
||||||
</span>
|
@click="decrement()"
|
||||||
<div class="flex gap-x-1">
|
variant="ghost"
|
||||||
<Button
|
class="h-4 w-4"
|
||||||
@click="decrement()"
|
icon="chevron-left"
|
||||||
variant="ghost"
|
/>
|
||||||
class="h-4 w-4"
|
<Button
|
||||||
icon="chevron-left"
|
@click="increment()"
|
||||||
/>
|
variant="ghost"
|
||||||
<Button
|
class="h-4 w-4"
|
||||||
@click="increment()"
|
icon="chevron-right"
|
||||||
variant="ghost"
|
/>
|
||||||
class="h-4 w-4"
|
</div>
|
||||||
icon="chevron-right"
|
</div>
|
||||||
/>
|
</template>
|
||||||
</div>
|
</Calendar>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</div>
|
||||||
</Calendar>
|
<Event v-model="showEvent" :event="currentEvent" />
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Event v-model="showEvent" :event="currentEvent"/>
|
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { Calendar, createListResource, Button } from "frappe-ui"
|
import { Calendar, createListResource, Button } from 'frappe-ui'
|
||||||
import { inject, ref } from "vue"
|
import { inject, ref } from 'vue'
|
||||||
import Event from "@/components/Modals/Event.vue"
|
import Event from '@/components/Modals/Event.vue'
|
||||||
|
|
||||||
const user = inject("$user")
|
const user = inject('$user')
|
||||||
const currentEvent = ref(null)
|
const currentEvent = ref(null)
|
||||||
const showEvent = ref(false)
|
const showEvent = ref(false)
|
||||||
|
|
||||||
@@ -58,37 +55,48 @@ const props = defineProps({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const evaluations = createListResource({
|
const evaluations = createListResource({
|
||||||
doctype: "LMS Certificate Request",
|
doctype: 'LMS Certificate Request',
|
||||||
filters: {
|
filters: {
|
||||||
"evaluator": user.data?.name
|
evaluator: user.data?.name,
|
||||||
},
|
},
|
||||||
fields: ["name", "member_name", "member", "course", "course_title", "batch_name", "batch_title", "date", "start_time", "end_time", "google_meet_link"],
|
fields: [
|
||||||
auto: true,
|
'name',
|
||||||
cache: ["schedule", user.data?.name],
|
'member_name',
|
||||||
transform(data) {
|
'member',
|
||||||
return data.map((d) => {
|
'course',
|
||||||
return {
|
'course_title',
|
||||||
title: `${d.member_name}'s Evaluation`,
|
'batch_name',
|
||||||
participant: d.member_name,
|
'batch_title',
|
||||||
id: d.name,
|
'evaluator',
|
||||||
venue: d.google_meet_link,
|
'evaluator_name',
|
||||||
fromDate: `${d.date} ${d.start_time}`,
|
'date',
|
||||||
toDate: `${d.date} ${d.end_time}`,
|
'start_time',
|
||||||
color: "green",
|
'end_time',
|
||||||
start_time: d.start_time,
|
'google_meet_link',
|
||||||
end_time: d.end_time,
|
],
|
||||||
course: d.course,
|
auto: true,
|
||||||
course_title: d.course_title,
|
orderBy: 'creation desc',
|
||||||
member: d.member,
|
limit: 100,
|
||||||
batch_name: d.batch_name,
|
cache: ['schedule', user.data?.name],
|
||||||
batch_title: d.batch_title
|
transform(data) {
|
||||||
}
|
return data.map((d) => {
|
||||||
})
|
let mappedData = Object.assign({}, d)
|
||||||
},
|
|
||||||
|
mappedData.title = `${d.member_name}'s Evaluation`
|
||||||
|
mappedData.participant = d.member_name
|
||||||
|
mappedData.id = d.name
|
||||||
|
mappedData.venue = d.google_meet_link
|
||||||
|
mappedData.fromDate = `${d.date} ${d.start_time}`
|
||||||
|
mappedData.toDate = `${d.date} ${d.end_time}`
|
||||||
|
mappedData.color = 'green'
|
||||||
|
|
||||||
|
return mappedData
|
||||||
|
})
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const openEvent = (event) => {
|
const openEvent = (event) => {
|
||||||
currentEvent.value = event.calendarEvent
|
currentEvent.value = event.calendarEvent
|
||||||
showEvent.value = true
|
showEvent.value = true
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -85,8 +85,9 @@ const routes = [
|
|||||||
{
|
{
|
||||||
name: 'ProfileEvaluationSchedule',
|
name: 'ProfileEvaluationSchedule',
|
||||||
path: 'schedule',
|
path: 'schedule',
|
||||||
component: () => import('@/pages/ProfileEvaluationSchedule.vue'),
|
component: () =>
|
||||||
}
|
import('@/pages/ProfileEvaluationSchedule.vue'),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ from frappe.query_builder.functions import Count
|
|||||||
from frappe.utils import time_diff, now_datetime, get_datetime
|
from frappe.utils import time_diff, now_datetime, get_datetime
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def autosave_section(section, code):
|
def autosave_section(section, code):
|
||||||
"""Saves the code edited in one of the sections."""
|
"""Saves the code edited in one of the sections."""
|
||||||
@@ -614,22 +615,23 @@ def check_app_permission():
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def save_evaluation_details(
|
def save_evaluation_details(
|
||||||
member: str,
|
member,
|
||||||
course: str,
|
course,
|
||||||
batch_name: str,
|
batch_name,
|
||||||
date: str,
|
evaluator,
|
||||||
start_time: str,
|
date,
|
||||||
end_time: str,
|
start_time,
|
||||||
status: str,
|
end_time,
|
||||||
rating: int,
|
status,
|
||||||
summary: str) -> None:
|
rating,
|
||||||
|
summary,
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Save evaluation details for a member against a course.
|
Save evaluation details for a member against a course.
|
||||||
"""
|
"""
|
||||||
evaluation = frappe.db.exists("LMS Certificate Evaluation", {
|
evaluation = frappe.db.exists(
|
||||||
"member": member,
|
"LMS Certificate Evaluation", {"member": member, "course": course}
|
||||||
"course": course
|
)
|
||||||
})
|
|
||||||
|
|
||||||
details = {
|
details = {
|
||||||
"date": date,
|
"date": date,
|
||||||
@@ -638,7 +640,7 @@ def save_evaluation_details(
|
|||||||
"status": status,
|
"status": status,
|
||||||
"rating": rating / 5,
|
"rating": rating / 5,
|
||||||
"summary": summary,
|
"summary": summary,
|
||||||
"batch_name": batch_name
|
"batch_name": batch_name,
|
||||||
}
|
}
|
||||||
|
|
||||||
if evaluation:
|
if evaluation:
|
||||||
@@ -646,10 +648,13 @@ def save_evaluation_details(
|
|||||||
return evaluation
|
return evaluation
|
||||||
else:
|
else:
|
||||||
doc = frappe.new_doc("LMS Certificate Evaluation")
|
doc = frappe.new_doc("LMS Certificate Evaluation")
|
||||||
details.update({
|
details.update(
|
||||||
"member": member,
|
{
|
||||||
"course": course,
|
"member": member,
|
||||||
})
|
"course": course,
|
||||||
|
"evaluator": evaluator,
|
||||||
|
}
|
||||||
|
)
|
||||||
doc.update(details)
|
doc.update(details)
|
||||||
doc.insert()
|
doc.insert()
|
||||||
return doc.name
|
return doc.name
|
||||||
@@ -657,39 +662,40 @@ def save_evaluation_details(
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def save_certificate_details(
|
def save_certificate_details(
|
||||||
member: str,
|
member,
|
||||||
course: str,
|
course,
|
||||||
batch_name: str,
|
batch_name,
|
||||||
|
evaluator,
|
||||||
issue_date,
|
issue_date,
|
||||||
expiry_date,
|
expiry_date,
|
||||||
template,
|
template,
|
||||||
published=True,
|
published=True,
|
||||||
) -> None:
|
):
|
||||||
"""
|
"""
|
||||||
Save certificate details for a member against a course.
|
Save certificate details for a member against a course.
|
||||||
"""
|
"""
|
||||||
certificate = frappe.db.exists("LMS Certificate", {
|
certificate = frappe.db.exists("LMS Certificate", {"member": member, "course": course})
|
||||||
"member": member,
|
|
||||||
"course": course
|
|
||||||
})
|
|
||||||
|
|
||||||
details = {
|
details = {
|
||||||
"published": published,
|
"published": published,
|
||||||
"issue_date": issue_date,
|
"issue_date": issue_date,
|
||||||
"expiry_date": expiry_date,
|
"expiry_date": expiry_date,
|
||||||
"template": template,
|
"template": template,
|
||||||
"batch_name": batch_name
|
"batch_name": batch_name,
|
||||||
}
|
}
|
||||||
|
|
||||||
if certificate:
|
if certificate:
|
||||||
frappe.db.set_value("LMS Certificate", certificate, details)
|
frappe.db.set_value("LMS Certificate", certificate, details)
|
||||||
return certificate
|
return certificate
|
||||||
else:
|
else:
|
||||||
doc = frappe.new_doc("LMS Certificate")
|
doc = frappe.new_doc("LMS Certificate")
|
||||||
details.update({
|
details.update(
|
||||||
"member": member,
|
{
|
||||||
"course": course,
|
"member": member,
|
||||||
})
|
"course": course,
|
||||||
|
"evaluator": evaluator,
|
||||||
|
}
|
||||||
|
)
|
||||||
doc.update(details)
|
doc.update(details)
|
||||||
doc.insert()
|
doc.insert()
|
||||||
return doc.name
|
return doc.name
|
||||||
|
|||||||
@@ -15,8 +15,10 @@
|
|||||||
"template",
|
"template",
|
||||||
"published",
|
"published",
|
||||||
"section_break_scyf",
|
"section_break_scyf",
|
||||||
"expiry_date",
|
"evaluator",
|
||||||
|
"evaluator_name",
|
||||||
"column_break_slaw",
|
"column_break_slaw",
|
||||||
|
"expiry_date",
|
||||||
"batch_name"
|
"batch_name"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
@@ -95,11 +97,24 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "column_break_slaw",
|
"fieldname": "column_break_slaw",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "evaluator",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Evaluator",
|
||||||
|
"options": "User"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fetch_from": "evaluator.full_name",
|
||||||
|
"fieldname": "evaluator_name",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"label": "Evaluator Name",
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-07-16 15:29:19.708888",
|
"modified": "2024-09-11 11:37:20.419955",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "LMS",
|
"module": "LMS",
|
||||||
"name": "LMS Certificate",
|
"name": "LMS Certificate",
|
||||||
|
|||||||
@@ -119,9 +119,9 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "evaluator",
|
"fieldname": "evaluator",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"in_standard_filter": 1,
|
||||||
"label": "Evaluator",
|
"label": "Evaluator",
|
||||||
"options": "User",
|
"options": "User"
|
||||||
"reqd": 1
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fetch_from": "evaluator.full_name",
|
"fetch_from": "evaluator.full_name",
|
||||||
@@ -133,7 +133,7 @@
|
|||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-09-10 20:17:49.908093",
|
"modified": "2024-09-11 11:20:06.233491",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "LMS",
|
"module": "LMS",
|
||||||
"name": "LMS Certificate Evaluation",
|
"name": "LMS Certificate Evaluation",
|
||||||
|
|||||||
@@ -48,8 +48,7 @@
|
|||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"in_standard_filter": 1,
|
"in_standard_filter": 1,
|
||||||
"label": "Evaluator",
|
"label": "Evaluator",
|
||||||
"options": "User",
|
"options": "User"
|
||||||
"read_only": 1
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "date",
|
"fieldname": "date",
|
||||||
@@ -149,7 +148,7 @@
|
|||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-09-10 13:13:48.282623",
|
"modified": "2024-09-11 11:19:44.669132",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "LMS",
|
"module": "LMS",
|
||||||
"name": "LMS Certificate Request",
|
"name": "LMS Certificate Request",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"name": "frappe_lms",
|
"name": "frappe_lms",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "Easy to use, open-source, Learning Management System",
|
"description": "Easy to use, open-source, Learning Management System",
|
||||||
"workspaces": [
|
"workspaces1": [
|
||||||
"frappe-ui",
|
"frappe-ui",
|
||||||
"frontend"
|
"frontend"
|
||||||
],
|
],
|
||||||
|
|||||||
Reference in New Issue
Block a user