refactor: student batch dashboard
This commit is contained in:
@@ -55,6 +55,9 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="!evaluation.course" class="text-ink-gray-7">
|
||||
{{ __('Please select a course to view available slots.') }}
|
||||
</div>
|
||||
<div v-else class="text-ink-red-3">
|
||||
{{ __('No slots available for the selected course.') }}
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
{{ __('Upcoming Evaluations') }}
|
||||
</div>
|
||||
<Button v-if="canScheduleEvals" @click="openEvalModal">
|
||||
{{ __('Schedule Evaluation') }}
|
||||
{{ __('Schedule') }}
|
||||
</Button>
|
||||
</div>
|
||||
<div
|
||||
@@ -31,7 +31,7 @@
|
||||
<div v-if="upcoming_evals.data?.length">
|
||||
<div
|
||||
class="grid gap-4"
|
||||
:class="forHome ? 'grid-cols-1 md:grid-cols-2' : 'grid-cols-3'"
|
||||
:class="forHome ? 'grid-cols-1 md:grid-cols-2' : 'grid-cols-1'"
|
||||
>
|
||||
<div v-for="evl in upcoming_evals.data">
|
||||
<div class="border text-ink-gray-7 rounded-md p-3">
|
||||
|
||||
@@ -75,6 +75,7 @@
|
||||
</template>
|
||||
<script setup>
|
||||
import {
|
||||
ClipboardPen,
|
||||
Laptop,
|
||||
List,
|
||||
Mail,
|
||||
@@ -166,7 +167,7 @@ const updateTabs = () => {
|
||||
if (isAdmin.value) {
|
||||
addToTabs('Dashboard', markRaw(AdminBatchDashboard), TrendingUp)
|
||||
} else if (isStudent.value) {
|
||||
addToTabs('Dashboard', markRaw(StudentBatchDashboard), null)
|
||||
addToTabs('Dashboard', markRaw(StudentBatchDashboard), ClipboardPen)
|
||||
}
|
||||
addToTabs('Classes', markRaw(LiveClass), Laptop)
|
||||
addToTabs('Announcements', markRaw(Announcements), Mail)
|
||||
|
||||
@@ -2,26 +2,6 @@
|
||||
<div class="">
|
||||
<div class="grid grid-cols-[3fr,2fr]">
|
||||
<div v-if="batchDetail.doc" class="py-5 h-[88vh] overflow-y-auto">
|
||||
<div class="px-5 pb-5 space-y-5 border-b mb-5">
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-5">
|
||||
<FormControl
|
||||
v-model="batchDetail.doc.published"
|
||||
type="checkbox"
|
||||
:label="__('Published')"
|
||||
/>
|
||||
<FormControl
|
||||
v-model="batchDetail.doc.allow_self_enrollment"
|
||||
type="checkbox"
|
||||
:label="__('Allow Self Enrollment')"
|
||||
/>
|
||||
<FormControl
|
||||
v-model="batchDetail.doc.certification"
|
||||
type="checkbox"
|
||||
:label="__('Certification')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="px-5 pb-5 space-y-5 border-b mb-5">
|
||||
<div class="text-lg text-ink-gray-9 font-semibold mb-4">
|
||||
{{ __('Details') }}
|
||||
@@ -29,6 +9,11 @@
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-5">
|
||||
<div class="space-y-5">
|
||||
<FormControl
|
||||
v-model="batchDetail.doc.published"
|
||||
type="checkbox"
|
||||
:label="__('Published')"
|
||||
/>
|
||||
<FormControl
|
||||
v-model="batchDetail.doc.title"
|
||||
:label="__('Title')"
|
||||
@@ -58,6 +43,11 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="space-y-5">
|
||||
<FormControl
|
||||
v-model="batchDetail.doc.allow_self_enrollment"
|
||||
type="checkbox"
|
||||
:label="__('Allow Self Enrollment')"
|
||||
/>
|
||||
<FormControl
|
||||
v-model="batchDetail.doc.start_time"
|
||||
:label="__('Session Start Time')"
|
||||
@@ -91,6 +81,35 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="px-5 pb-5 space-y-5 border-b mb-5">
|
||||
<div class="text-lg text-ink-gray-9 font-semibold mb-4">
|
||||
{{ __('Certification') }}
|
||||
</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-5 items-start">
|
||||
<div class="flex flex-col space-y-5">
|
||||
<FormControl
|
||||
v-model="batchDetail.doc.evaluation"
|
||||
type="checkbox"
|
||||
:label="__('Evaluation')"
|
||||
/>
|
||||
<FormControl
|
||||
v-if="batchDetail.doc.evaluation"
|
||||
v-model="batchDetail.doc.evaluation_end_date"
|
||||
:label="__('Evaluation End Date')"
|
||||
type="date"
|
||||
class="mb-4"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<FormControl
|
||||
v-model="batchDetail.doc.certification"
|
||||
type="checkbox"
|
||||
:label="__('Certification')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="px-5 pb-5 space-y-5 border-b mb-5">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-5">
|
||||
<div class="space-y-5">
|
||||
@@ -110,9 +129,15 @@
|
||||
:onCreate="(close) => openSettings('Evaluators', close)"
|
||||
:filters="{ ignore_user_type: 1 }"
|
||||
/>
|
||||
<Uploader
|
||||
v-model="batchDetail.doc.video_link"
|
||||
:label="__('Preview Video')"
|
||||
type="video"
|
||||
:required="false"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm text-ink-gray-5 mb-1">
|
||||
<label class="block text-sm text-ink-gray-5 mb-2">
|
||||
{{ __('Batch Details') }}
|
||||
<span class="text-ink-red-3">*</span>
|
||||
</label>
|
||||
@@ -139,7 +164,7 @@
|
||||
/>
|
||||
<Link
|
||||
doctype="Email Template"
|
||||
:label="__('Email Template')"
|
||||
:label="__('Enrollment Confirmation Email Template')"
|
||||
v-model="batchDetail.doc.confirmation_email_template"
|
||||
:onCreate="
|
||||
(value, close) => {
|
||||
@@ -147,6 +172,8 @@
|
||||
}
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<div class="space-y-5">
|
||||
<Link
|
||||
doctype="LMS Zoom Settings"
|
||||
:label="__('Zoom Account')"
|
||||
@@ -158,20 +185,6 @@
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<div class="space-y-5">
|
||||
<FormControl
|
||||
v-model="batchDetail.doc.evaluation_end_date"
|
||||
:label="__('Evaluation End Date')"
|
||||
type="date"
|
||||
class="mb-4"
|
||||
/>
|
||||
<Uploader
|
||||
v-model="batchDetail.doc.video_link"
|
||||
:label="__('Preview Video')"
|
||||
type="video"
|
||||
:required="false"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -350,6 +363,7 @@ const updateBatchData = () => {
|
||||
'paid_batch',
|
||||
'allow_self_enrollment',
|
||||
'certification',
|
||||
'evaluation',
|
||||
]
|
||||
for (let idx in checkboxes) {
|
||||
let key = checkboxes[idx]
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
<BatchOverlay :batch="batch" />
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="batch.data.courses.length">
|
||||
<div v-if="courses.data?.length">
|
||||
<div class="flex items-center mt-10">
|
||||
<div class="text-2xl font-semibold text-ink-gray-9">
|
||||
{{ __('Courses') }}
|
||||
@@ -40,7 +40,7 @@
|
||||
</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 mt-5">
|
||||
<div
|
||||
v-if="batch.data.courses"
|
||||
v-if="courses.data?.length"
|
||||
v-for="course in courses.data"
|
||||
:key="course.course"
|
||||
>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<NumberChartGraph
|
||||
class="border rounded-md"
|
||||
:title="__('Assessments')"
|
||||
:value="assessmentCount.data || 0"
|
||||
:value="batch?.data?.assessments?.length || 0"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -198,15 +198,6 @@ const props = defineProps<{
|
||||
batch: { [key: string]: any } | null
|
||||
}>()
|
||||
|
||||
const assessmentCount = createResource({
|
||||
url: 'lms.lms.utils.get_batch_assessment_count',
|
||||
cache: ['batch_assessment_count', props.batch?.data?.name],
|
||||
params: {
|
||||
batch: props.batch?.data?.name,
|
||||
},
|
||||
auto: true,
|
||||
})
|
||||
|
||||
const chartData = createResource({
|
||||
url: 'lms.lms.utils.get_batch_chart_data',
|
||||
cache: ['batch_chart_data', props.batch?.data?.name],
|
||||
@@ -276,6 +267,7 @@ const studentColumns = computed(() => {
|
||||
const showProgressChart = computed(
|
||||
() =>
|
||||
students.data?.length &&
|
||||
(props.batch?.data?.courses?.length || assessmentCount.data)
|
||||
(props.batch?.data?.courses?.length ||
|
||||
props.batch?.data?.assessments?.length)
|
||||
)
|
||||
</script>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<div class="text-lg text-ink-gray-9 font-semibold">
|
||||
<div class="text-ink-gray-9 font-semibold">
|
||||
{{ __('Assessments') }}
|
||||
</div>
|
||||
<Button v-if="canAddAssessments()" @click="showModal = true">
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<div class="text-lg text-ink-gray-9 font-semibold">
|
||||
<div class="text-ink-gray-9 font-semibold">
|
||||
{{ __('Courses') }}
|
||||
</div>
|
||||
<Button v-if="isAdmin()" @click="openCourseModal()">
|
||||
|
||||
@@ -1,19 +1,101 @@
|
||||
<template>
|
||||
<div class="space-y-10 mt-5 w-[75%] mx-auto">
|
||||
<UpcomingEvaluations
|
||||
:batch="batch.data.name"
|
||||
:endDate="batch.data.evaluation_end_date"
|
||||
:courses="batch.data.courses"
|
||||
/>
|
||||
<BatchCourses :batch="batch" />
|
||||
<Assessments :batch="batch.data.name" />
|
||||
<div class="grid grid-cols-2 gap-10"></div>
|
||||
<div class="h-[88vh]">
|
||||
<div class="grid grid-cols-[2fr,1fr] gap-5">
|
||||
<div class="p-5">
|
||||
<div class="mb-8 space-y-2">
|
||||
<div class="text-lg font-semibold">
|
||||
{{ __('Curriculum') }}
|
||||
</div>
|
||||
<div class="text-ink-gray-7">
|
||||
{{
|
||||
__(
|
||||
"As a part of this batch's curriculum you will have to complete the following courses and assessments."
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-10">
|
||||
<div>
|
||||
<div class="font-semibold mb-4">
|
||||
{{ __('Courses') }}
|
||||
</div>
|
||||
<ListView
|
||||
v-if="batch.data?.courses?.length"
|
||||
:columns="courseColumns"
|
||||
:rows="batch.data?.courses"
|
||||
row-key="name"
|
||||
class="border rounded-lg"
|
||||
:options="{
|
||||
showTooltip: false,
|
||||
selectable: user.data?.is_student ? false : true,
|
||||
getRowRoute: (row) => ({
|
||||
name: 'CourseDetail',
|
||||
params: { courseName: row.course },
|
||||
}),
|
||||
}"
|
||||
>
|
||||
<ListHeader
|
||||
class="mb-2 grid items-center space-x-4 rounded-none rounded-t bg-surface-gray-2 p-2"
|
||||
>
|
||||
</ListHeader>
|
||||
<ListRows>
|
||||
<ListRow
|
||||
:row="row"
|
||||
v-for="row in batch.data?.courses"
|
||||
class="!rounded-none text-sm"
|
||||
>
|
||||
<template #default="{ column, item }">
|
||||
<ListRowItem :item="row[column.key]" :align="column.align">
|
||||
<div v-if="column.key === 'progress'">
|
||||
{{ getProgress(row.course) }}%
|
||||
</div>
|
||||
<div v-else>
|
||||
{{ row[column.key] }}
|
||||
</div>
|
||||
</ListRowItem>
|
||||
</template>
|
||||
</ListRow>
|
||||
</ListRows>
|
||||
</ListView>
|
||||
<div v-else class="text-ink-gray-7">
|
||||
{{ __('No courses added to this batch') }}
|
||||
</div>
|
||||
</div>
|
||||
<!-- <BatchCourses :batch="batch" /> -->
|
||||
<Assessments :batch="batch.data.name" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="border-l h-[88vh] divide-y">
|
||||
<div v-if="batch.data?.evaluation" class="p-4 mb-5">
|
||||
<UpcomingEvaluations
|
||||
:batch="batch.data.name"
|
||||
:endDate="batch.data.evaluation_end_date"
|
||||
:courses="batch.data.courses"
|
||||
/>
|
||||
</div>
|
||||
<div class="p-5">
|
||||
<BatchFeedback :batch="batch.data?.name" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import UpcomingEvaluations from '@/components/UpcomingEvaluations.vue'
|
||||
import { inject } from 'vue'
|
||||
import {
|
||||
createListResource,
|
||||
ListView,
|
||||
ListHeader,
|
||||
ListRows,
|
||||
ListRow,
|
||||
ListRowItem,
|
||||
} from 'frappe-ui'
|
||||
import Assessments from '@/pages/Batches/components/Assessments.vue'
|
||||
import BatchCourses from '@/pages/Batches/components/BatchCourses.vue'
|
||||
import BatchFeedback from '@/pages/Batches/components/BatchFeedback.vue'
|
||||
import UpcomingEvaluations from '@/components/UpcomingEvaluations.vue'
|
||||
|
||||
const user = inject('$user')
|
||||
|
||||
const props = defineProps({
|
||||
batch: {
|
||||
@@ -25,4 +107,31 @@ const props = defineProps({
|
||||
default: false,
|
||||
},
|
||||
})
|
||||
|
||||
const progressList = createListResource({
|
||||
doctype: 'LMS Enrollment',
|
||||
filters: {
|
||||
member: user.data?.name,
|
||||
course: ['in', props.batch.data?.courses?.map((c) => c.course)],
|
||||
},
|
||||
fields: ['course', 'progress', 'name'],
|
||||
auto: true,
|
||||
})
|
||||
|
||||
const getProgress = (course) => {
|
||||
const progress = progressList.data?.find((p) => p.course === course)
|
||||
return progress ? Math.round(progress.progress) : 0
|
||||
}
|
||||
|
||||
const courseColumns = [
|
||||
{
|
||||
key: 'title',
|
||||
label: __('Course'),
|
||||
},
|
||||
{
|
||||
key: 'progress',
|
||||
label: __('Progress'),
|
||||
align: 'right',
|
||||
},
|
||||
]
|
||||
</script>
|
||||
|
||||
@@ -1,7 +1,24 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="text-lg text-ink-gray-9 font-semibold mb-5">
|
||||
{{ __('Feedback') }}
|
||||
<div class="flex justify-between mb-5">
|
||||
<div class="space-y-1">
|
||||
<div class="text-lg text-ink-gray-9 font-semibold">
|
||||
{{ __('Feedback') }}
|
||||
</div>
|
||||
<div
|
||||
v-if="feedbackList.data?.length && isAdmin"
|
||||
class="leading-5 text-ink-gray-7 text-sm mb-2 mt-5"
|
||||
>
|
||||
{{ __('Average Feedback Received') }}
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
v-if="feedbackList.data?.length && isAdmin"
|
||||
variant="outline"
|
||||
@click="showAllFeedback = true"
|
||||
>
|
||||
{{ __('View all feedback') }}
|
||||
</Button>
|
||||
</div>
|
||||
<div v-if="user.data?.is_student">
|
||||
<div>
|
||||
@@ -43,10 +60,6 @@
|
||||
</div>
|
||||
|
||||
<div v-else-if="feedbackList.data?.length">
|
||||
<div class="leading-5 text-sm mb-2 mt-5">
|
||||
{{ __('Average Feedback Received') }}
|
||||
</div>
|
||||
|
||||
<div class="space-y-4">
|
||||
<Rating
|
||||
v-for="key in ratingKeys"
|
||||
@@ -55,10 +68,6 @@
|
||||
:readonly="true"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Button variant="outline" class="mt-5" @click="showAllFeedback = true">
|
||||
{{ __('View all feedback') }}
|
||||
</Button>
|
||||
</div>
|
||||
<div v-else class="text-ink-gray-7 leading-5">
|
||||
{{ __('No feedback received yet.') }}
|
||||
@@ -71,7 +80,7 @@
|
||||
/>
|
||||
</template>
|
||||
<script setup>
|
||||
import { inject, onMounted, reactive, ref, watch } from 'vue'
|
||||
import { computed, inject, onMounted, reactive, ref, watch } from 'vue'
|
||||
import { convertToTitleCase } from '@/utils'
|
||||
import { Button, createListResource, FormControl, Rating } from 'frappe-ui'
|
||||
import FeedbackModal from '@/components/Modals/FeedbackModal.vue'
|
||||
@@ -164,10 +173,15 @@ const submitFeedback = () => {
|
||||
onSuccess: () => {
|
||||
feedbackList.reload()
|
||||
showFeedbackForm.value = false
|
||||
readOnly.value = true
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const isAdmin = computed(() => {
|
||||
return user.data?.is_moderator || user.data?.is_evaluator
|
||||
})
|
||||
</script>
|
||||
<style>
|
||||
.feedback-list > button > div {
|
||||
|
||||
Reference in New Issue
Block a user