refactor: batch student progress
This commit is contained in:
@@ -17,22 +17,20 @@
|
||||
{{ __('Save') }}
|
||||
</Button>
|
||||
</div>
|
||||
<div v-else-if="isAdmin" class="space-x-2">
|
||||
<Button
|
||||
v-if="batch.data?.certification"
|
||||
@click="openCertificateDialog = true"
|
||||
>
|
||||
{{ __('Generate Certificates') }}
|
||||
</Button>
|
||||
<Button v-if="canMakeAnnouncement()" @click="openAnnouncementModal()">
|
||||
<span>
|
||||
{{ __('Make an Announcement') }}
|
||||
</span>
|
||||
<template #suffix>
|
||||
<SendIcon class="h-4 stroke-1.5" />
|
||||
</template>
|
||||
</Button>
|
||||
</div>
|
||||
<Dropdown
|
||||
v-else-if="isAdmin"
|
||||
:options="batchMenu"
|
||||
placement="left"
|
||||
side="left"
|
||||
>
|
||||
<template v-slot="{ open }">
|
||||
<Button variant="ghost">
|
||||
<template #icon>
|
||||
<EllipsisVertical class="w-4 h-4 stroke-1.5" />
|
||||
</template>
|
||||
</Button>
|
||||
</template>
|
||||
</Dropdown>
|
||||
</header>
|
||||
<div>
|
||||
<BatchOverview v-if="!isAdmin && !isStudent" :batch="batch" />
|
||||
@@ -76,6 +74,7 @@
|
||||
<script setup>
|
||||
import {
|
||||
ClipboardPen,
|
||||
EllipsisVertical,
|
||||
Laptop,
|
||||
List,
|
||||
Mail,
|
||||
@@ -92,6 +91,7 @@ import {
|
||||
Breadcrumbs,
|
||||
Button,
|
||||
createResource,
|
||||
Dropdown,
|
||||
Tabs,
|
||||
usePageMeta,
|
||||
} from 'frappe-ui'
|
||||
@@ -205,6 +205,26 @@ const canMakeAnnouncement = () => {
|
||||
return user.data?.is_moderator || user.data?.is_evaluator
|
||||
}
|
||||
|
||||
const batchMenu = computed(() => {
|
||||
let options = [
|
||||
{
|
||||
label: __('Generate Certificates'),
|
||||
onClick() {
|
||||
openCertificateDialog.value = true
|
||||
},
|
||||
condition: () => batch.data?.certification,
|
||||
},
|
||||
{
|
||||
label: __('Make an Announcement'),
|
||||
onClick() {
|
||||
openAnnouncementModal()
|
||||
},
|
||||
condition: () => canMakeAnnouncement(),
|
||||
},
|
||||
]
|
||||
return options
|
||||
})
|
||||
|
||||
const breadcrumbs = computed(() => {
|
||||
let crumbs = [{ label: __('Batches'), route: { name: 'Batches' } }]
|
||||
crumbs.push({
|
||||
|
||||
@@ -55,6 +55,10 @@
|
||||
:options="{
|
||||
selectable: false,
|
||||
showTooltip: false,
|
||||
onRowClick: (row: any) => {
|
||||
currentStudent = row.member
|
||||
showProgressModal = true
|
||||
},
|
||||
}"
|
||||
>
|
||||
<ListHeader
|
||||
@@ -68,16 +72,7 @@
|
||||
</ListHeaderItem>
|
||||
</ListHeader>
|
||||
<ListRows v-for="row in students.data" class="max-h-[500px]">
|
||||
<ListRow
|
||||
:row="row"
|
||||
@click="
|
||||
() => {
|
||||
/* showProgressModal = true
|
||||
currentStudent = row */
|
||||
}
|
||||
"
|
||||
class="cursor-pointer"
|
||||
>
|
||||
<ListRow :row="row">
|
||||
<template #default="{ column, item }">
|
||||
<ListRowItem
|
||||
:item="row[column.key]"
|
||||
@@ -167,6 +162,12 @@
|
||||
:batch="batch"
|
||||
:students="students"
|
||||
/>
|
||||
<BatchStudentProgress
|
||||
v-if="showProgressModal"
|
||||
v-model="showProgressModal"
|
||||
:student="currentStudent"
|
||||
:batch="batch?.data?.name"
|
||||
/>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
@@ -188,11 +189,14 @@ import { computed, ref, watch } from 'vue'
|
||||
import { formatAmount } from '@/utils'
|
||||
import { Plus } from 'lucide-vue-next'
|
||||
import BatchFeedback from '@/pages/Batches/components/BatchFeedback.vue'
|
||||
import BatchStudentProgress from '@/pages/Batches/components/BatchStudentProgress.vue'
|
||||
import NumberChartGraph from '@/components/NumberChartGraph.vue'
|
||||
import StudentModal from '@/components/Modals/StudentModal.vue'
|
||||
|
||||
const searchFilter = ref<string | null>(null)
|
||||
const showEnrollmentModal = ref<boolean>(false)
|
||||
const showProgressModal = ref<boolean>(false)
|
||||
const currentStudent = ref<any>(null)
|
||||
|
||||
const props = defineProps<{
|
||||
batch: { [key: string]: any } | null
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="w-[75%] mx-auto mt-5">
|
||||
<div class="font-semibold text-lg mb-5">
|
||||
<div class="text-ink-gray-9 font-semibold text-lg mb-5">
|
||||
{{ __('Announcements') }}
|
||||
</div>
|
||||
<div v-if="communications.data?.length">
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<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">
|
||||
<div class="text-lg text-ink-gray-9 font-semibold">
|
||||
{{ __('Curriculum') }}
|
||||
</div>
|
||||
<div class="text-ink-gray-7">
|
||||
@@ -16,7 +16,7 @@
|
||||
</div>
|
||||
<div class="space-y-10">
|
||||
<div>
|
||||
<div class="font-semibold mb-4">
|
||||
<div class="text-ink-gray-9 font-semibold mb-4">
|
||||
{{ __('Courses') }}
|
||||
</div>
|
||||
<ListView
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
<template>
|
||||
<Dialog
|
||||
v-model="show"
|
||||
:options="{
|
||||
size: 'xl',
|
||||
}"
|
||||
>
|
||||
<template #body>
|
||||
<div v-if="studentDetails.data" class="p-5 space-y-10 text-sm">
|
||||
<div class="flex items-center space-x-2">
|
||||
<Avatar :image="studentDetails.data.user_image" size="3xl" />
|
||||
<div class="space-y-1">
|
||||
<div class="flex items-center space-x-2">
|
||||
<div class="text-xl font-semibold text-ink-gray-9">
|
||||
{{ studentDetails.data.full_name }}
|
||||
</div>
|
||||
<Badge
|
||||
v-if="
|
||||
Object.keys(studentDetails.data.assessments).length ||
|
||||
Object.keys(studentDetails.data.courses).length
|
||||
"
|
||||
:theme="studentDetails.data.progress === 100 ? 'green' : 'red'"
|
||||
>
|
||||
{{ studentDetails.data.progress }}% {{ __('Complete') }}
|
||||
</Badge>
|
||||
</div>
|
||||
<div class="text-sm text-ink-gray-7">
|
||||
{{ studentDetails.data.email }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-8">
|
||||
<!-- Assessments -->
|
||||
<ListView
|
||||
:columns="assessmentColumns"
|
||||
:rows="studentDetails.data.assessments"
|
||||
row-key="title"
|
||||
class="border rounded-lg"
|
||||
:options="{
|
||||
selectable: false,
|
||||
showTooltip: false,
|
||||
onRowClick: (row: any) => {
|
||||
redirectToAssessment(row)
|
||||
}
|
||||
}"
|
||||
>
|
||||
<ListHeader
|
||||
class="mb-2 grid items-center space-x-4 rounded-none rounded-t bg-surface-gray-2 p-2"
|
||||
>
|
||||
</ListHeader>
|
||||
<ListRows v-for="row in studentDetails.data.assessments">
|
||||
<ListRow :row="row" class="!rounded-none">
|
||||
<template #default="{ column, item }">
|
||||
<ListRowItem
|
||||
:item="row[column.key]"
|
||||
:align="column.align"
|
||||
class="w-full"
|
||||
>
|
||||
<div
|
||||
v-if="column.key == 'status' && isAssignment(row.status)"
|
||||
>
|
||||
<Badge :theme="getStatusTheme(row[column.key])">
|
||||
{{ row[column.key] }}
|
||||
</Badge>
|
||||
</div>
|
||||
<div v-else>
|
||||
{{ row[column.key] }}
|
||||
</div>
|
||||
</ListRowItem>
|
||||
</template>
|
||||
</ListRow>
|
||||
</ListRows>
|
||||
</ListView>
|
||||
|
||||
<!-- Courses -->
|
||||
<ListView
|
||||
:columns="courseColumns"
|
||||
:rows="studentDetails.data.courses"
|
||||
row-key="title"
|
||||
class="border rounded-lg"
|
||||
:options="{
|
||||
selectable: false,
|
||||
showTooltip: false,
|
||||
onRowClick: (row: any) => {
|
||||
redirectToCourse(row)
|
||||
}
|
||||
}"
|
||||
>
|
||||
<ListHeader
|
||||
class="mb-2 grid items-center space-x-4 rounded-none rounded-t bg-surface-gray-2 p-2"
|
||||
>
|
||||
</ListHeader>
|
||||
<ListRows v-for="row in studentDetails.data.courses">
|
||||
<ListRow :row="row" class="!rounded-none">
|
||||
<template #default="{ column, item }">
|
||||
<ListRowItem
|
||||
:item="row[column.key]"
|
||||
:align="column.align"
|
||||
class="w-full"
|
||||
>
|
||||
<template #prefix>
|
||||
<ProgressBar
|
||||
v-if="column.key == 'progress'"
|
||||
:progress="Math.ceil(row[column.key])"
|
||||
class="!mx-0 !mr-4 max-w-32"
|
||||
/>
|
||||
</template>
|
||||
<div
|
||||
v-if="column.key == 'progress'"
|
||||
class="text-xs !ml-0 !mr-3 w-5"
|
||||
>
|
||||
{{ Math.ceil(row[column.key]) }}%
|
||||
</div>
|
||||
<div v-else>
|
||||
{{ row[column.key] }}
|
||||
</div>
|
||||
</ListRowItem>
|
||||
</template>
|
||||
</ListRow>
|
||||
</ListRows>
|
||||
</ListView>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
Avatar,
|
||||
Badge,
|
||||
createResource,
|
||||
Dialog,
|
||||
ListView,
|
||||
ListHeader,
|
||||
ListRows,
|
||||
ListRow,
|
||||
ListRowItem,
|
||||
} from 'frappe-ui'
|
||||
import { useRouter } from 'vue-router'
|
||||
import ProgressBar from '@/components/ProgressBar.vue'
|
||||
|
||||
const show = defineModel()
|
||||
const router = useRouter()
|
||||
const props = defineProps<{
|
||||
student: string
|
||||
batch: string
|
||||
}>()
|
||||
|
||||
const studentDetails = createResource({
|
||||
url: 'lms.lms.utils.get_batch_student_progress',
|
||||
makeParams() {
|
||||
return {
|
||||
member: props.student,
|
||||
batch: props.batch,
|
||||
}
|
||||
},
|
||||
auto: true,
|
||||
})
|
||||
|
||||
const redirectToAssessment = (row: any) => {
|
||||
console.log(row)
|
||||
if (!row.submission) return
|
||||
if (row.type == 'LMS Assignment') {
|
||||
router.push({
|
||||
name: 'AssignmentSubmission',
|
||||
params: {
|
||||
assignmentID: row.assessment,
|
||||
submissionName: row.submission,
|
||||
},
|
||||
})
|
||||
} else if (row.type == 'LMS Programming Exercise') {
|
||||
router.push({
|
||||
name: 'ProgrammingExerciseSubmission',
|
||||
params: {
|
||||
exerciseID: row.assessment,
|
||||
submissionID: row.submission,
|
||||
},
|
||||
})
|
||||
} else if (row.type == 'LMS Quiz') {
|
||||
router.push({
|
||||
name: 'QuizSubmission',
|
||||
params: {
|
||||
submission: row.submission,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const redirectToCourse = (row: any) => {
|
||||
router.push({
|
||||
name: 'CourseDetail',
|
||||
params: {
|
||||
courseName: row.course,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const assessmentColumns = [
|
||||
{ key: 'title', label: 'Assessment', align: 'left' },
|
||||
{ key: 'status', label: 'Percentage/Status', align: 'right' },
|
||||
]
|
||||
|
||||
const courseColumns = [
|
||||
{ key: 'title', label: 'Course', align: 'left', width: '70%' },
|
||||
{ key: 'progress', label: 'Progress', align: 'right' },
|
||||
]
|
||||
|
||||
const isAssignment = (value: any) => {
|
||||
return isNaN(value)
|
||||
}
|
||||
|
||||
const getStatusTheme = (status: string) => {
|
||||
if (status === 'Pass') {
|
||||
return 'green'
|
||||
} else if (status == 'Not Graded') {
|
||||
return 'orange'
|
||||
} else {
|
||||
return 'red'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user