Files
enlight-lms/frontend/src/pages/Courses/StudentCourseProgress.vue
2026-02-05 12:10:24 +05:30

221 lines
5.5 KiB
Vue

<template>
<Dialog
v-model="show"
:options="{
title: __('Student Progress'),
size: hasAssessmentData ? '3xl' : 'xl',
}"
>
<template #body-content>
<div class="text-base text-ink-gray-9 max-h-[70vh] overflow-y-auto">
<div class="flex justify-between mb-5 px-2">
<div class="flex items-center space-x-2">
<Avatar
:image="student?.member_image"
:label="student?.member_name"
size="xl"
/>
<div>
<div class="font-semibold">
{{ student?.member_name }}
</div>
<div class="text-ink-gray-5">
{{ student.member }}
</div>
</div>
</div>
<div class="w-25 space-y-2">
<div class="text-ink-gray-5 text-sm">
{{ Math.round(student.progress) }}% {{ __('completed') }}
</div>
<ProgressBar
:label="__('Course Progress')"
:progress="student.progress"
/>
</div>
</div>
<div class="grid gap-5" :class="hasAssessmentData ? 'grid-cols-2' : ''">
<div
v-if="lessons.data"
class="border border-outline-gray-modals rounded-lg px-3 pt-3 max-h-[60vh] overflow-y-auto"
>
<div>
<div class="text-ink-gray-5 mb-5">
{{ __('Lesson Progress') }}
</div>
</div>
<div
v-for="progress in lessons.data"
class="flex justify-between text-sm py-2 my-1"
>
<div class="">
<span class="mr-3 text-xs">
{{ progress.chapter_idx }}.{{ progress.idx }}
</span>
<span>
{{ progress.title }}
</span>
</div>
<Tooltip
v-if="getLessonStatus(progress) == 'Complete'"
:text="__('Complete')"
>
<Check class="text-ink-green-3 size-4" />
</Tooltip>
<Tooltip v-else :text="__('Pending')">
<Minus class="text-ink-amber-2 size-4" />
</Tooltip>
<!-- <Badge :theme="getLessonStatusTheme(progress)">
{{ getLessonStatus(progress) }}
</Badge> -->
</div>
</div>
<div class="space-y-3">
<div
v-if="assessmentProgress.data?.quizzes?.length"
class="border border-outline-gray-modals rounded-lg px-3 pt-3 h-fit"
>
<div>
<div class="text-ink-gray-5 mb-5">
{{ __('Quiz Progress') }}
</div>
</div>
<div
v-for="quiz in assessmentProgress.data.quizzes"
class="flex justify-between text-sm py-2 my-1"
>
<div>
{{ quiz.quiz_title }}
</div>
<div>
{{ quiz.score }}
</div>
<div>{{ quiz.percentage }}%</div>
</div>
</div>
<div
v-if="assessmentProgress.data?.assignments?.length"
class="border border-outline-gray-modals rounded-lg px-3 pt-3 h-fit"
>
<div>
<div class="text-ink-gray-5 mb-5">
{{ __('Assignment Progress') }}
</div>
</div>
<div
v-for="assignment in assessmentProgress.data.assignments"
class="flex justify-between text-sm py-2 my-1"
>
<div>
{{ assignment.assignment_title }}
</div>
<Badge :theme="getAssessmentStatusTheme(assignment.status)">
{{ assignment.status }}
</Badge>
</div>
</div>
<div
v-if="assessmentProgress.data?.exercises?.length"
class="border border-outline-gray-modals rounded-lg px-3 pt-3 h-fit"
>
<div>
<div class="text-ink-gray-5 mb-5">
{{ __('Programming Exercise Progress') }}
</div>
</div>
<div
v-for="exercise in assessmentProgress.data.exercises"
class="flex justify-between text-sm py-2 my-1"
>
<div>
{{ exercise.exercise_title }}
</div>
<Badge :theme="getAssessmentStatusTheme(exercise.status)">
{{ exercise.status }}
</Badge>
</div>
</div>
</div>
</div>
</div>
</template>
</Dialog>
</template>
<script setup lang="ts">
import {
Avatar,
Badge,
createListResource,
createResource,
Dialog,
Tooltip,
} from 'frappe-ui'
import ProgressBar from '@/components/ProgressBar.vue'
import { computed } from 'vue'
import { Check, Minus } from 'lucide-vue-next'
const show = defineModel<boolean>({ required: true, default: false })
const props = defineProps<{
course: any
student: any
lessons: any
}>()
const lessonProgress = createListResource({
doctype: 'LMS Course Progress',
filters: {
course: ['=', props.course.data?.name],
member: ['=', props.student?.member],
},
fields: ['name', 'lesson', 'status'],
auto: true,
})
const assessmentProgress = createResource({
url: 'lms.lms.api.get_course_assessment_progress',
params: {
course: props.course.data?.name,
member: props.student?.member,
},
auto: true,
})
const getLessonStatus = (lesson: any) => {
return (
lessonProgress.data?.find((lp: any) => lp.lesson === lesson.lesson)
?.status || __('Pending')
)
}
const getLessonStatusTheme = (lesson: any) => {
const status = getLessonStatus(lesson)
if (status === 'Complete') {
return 'green'
} else {
return 'orange'
}
}
const getAssessmentStatusTheme = (status: string) => {
if (status.includes('Pass')) return 'green'
else if (status.includes('Fail')) return 'red'
else return 'orange'
}
const hasAssessmentData = computed(() => {
return (
(assessmentProgress.data?.quizzes &&
assessmentProgress.data.quizzes.length > 0) ||
(assessmentProgress.data?.assignments &&
assessmentProgress.data.assignments.length > 0) ||
(assessmentProgress.data?.exercises &&
assessmentProgress.data.exercises.length > 0)
)
})
</script>