Merge branch 'develop' of https://github.com/frappe/lms into lesson-md-parser
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="text-lg font-semibold mb-4">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<div class="text-lg font-semibold">
|
||||
{{ __('Assessments') }}
|
||||
</div>
|
||||
<Button v-if="canSeeAddButton()" @click="showModal = true">
|
||||
@@ -38,7 +38,10 @@
|
||||
<ListRow :row="row" v-for="row in assessments.data">
|
||||
<template #default="{ column, item }">
|
||||
<ListRowItem :item="row[column.key]" :align="column.align">
|
||||
<div>
|
||||
<div v-if="column.key == 'assessment_type'">
|
||||
{{ row[column.key] == 'LMS Quiz' ? 'Quiz' : 'Assignment' }}
|
||||
</div>
|
||||
<div v-else>
|
||||
{{ row[column.key] }}
|
||||
</div>
|
||||
</ListRowItem>
|
||||
@@ -177,10 +180,12 @@ const getAssessmentColumns = () => {
|
||||
{
|
||||
label: 'Assessment',
|
||||
key: 'title',
|
||||
width: '30rem',
|
||||
},
|
||||
{
|
||||
label: 'Type',
|
||||
key: 'assessment_type',
|
||||
width: '10rem',
|
||||
},
|
||||
]
|
||||
|
||||
@@ -189,6 +194,7 @@ const getAssessmentColumns = () => {
|
||||
label: 'Status/Score',
|
||||
key: 'status',
|
||||
align: 'center',
|
||||
width: '10rem',
|
||||
})
|
||||
}
|
||||
return columns
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<div class="text-xl font-semibold">
|
||||
<div class="text-lg font-semibold">
|
||||
{{ __('Courses') }}
|
||||
</div>
|
||||
<Button v-if="canSeeAddButton()" @click="openCourseModal()">
|
||||
@@ -118,13 +118,13 @@ const getCoursesColumns = () => {
|
||||
},
|
||||
{
|
||||
label: 'Lessons',
|
||||
key: 'lesson_count',
|
||||
key: 'lessons',
|
||||
align: 'right',
|
||||
},
|
||||
{
|
||||
label: 'Enrollments',
|
||||
align: 'right',
|
||||
key: 'enrollment_count',
|
||||
key: 'enrollments',
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
<template>
|
||||
<Button class="float-right mb-3" @click="openStudentModal()">
|
||||
<template #prefix>
|
||||
<Plus class="h-4 w-4" />
|
||||
</template>
|
||||
{{ __('Add') }}
|
||||
</Button>
|
||||
<div class="text-lg font-semibold mb-4">
|
||||
{{ __('Students') }}
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<div class="text-lg font-semibold">
|
||||
{{ __('Students') }}
|
||||
</div>
|
||||
<Button @click="openStudentModal()">
|
||||
<template #prefix>
|
||||
<Plus class="h-4 w-4" />
|
||||
</template>
|
||||
{{ __('Add') }}
|
||||
</Button>
|
||||
</div>
|
||||
<div v-if="students.data?.length">
|
||||
<ListView
|
||||
@@ -18,12 +20,16 @@
|
||||
<ListHeader
|
||||
class="mb-2 grid items-center space-x-4 rounded bg-gray-100 p-2"
|
||||
>
|
||||
<ListHeaderItem :item="item" v-for="item in getStudentColumns()">
|
||||
<ListHeaderItem
|
||||
:item="item"
|
||||
v-for="item in getStudentColumns()"
|
||||
:title="item.label"
|
||||
>
|
||||
<template #prefix="{ item }">
|
||||
<component
|
||||
<FeatherIcon
|
||||
v-if="item.icon"
|
||||
:is="item.icon"
|
||||
class="h-4 w-4 stroke-1.5 ml-4"
|
||||
:name="item.icon"
|
||||
class="h-4 w-4 stroke-1.5"
|
||||
/>
|
||||
</template>
|
||||
</ListHeaderItem>
|
||||
@@ -42,9 +48,22 @@
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<div>
|
||||
<div v-if="column.key == 'courses'">
|
||||
{{ row[column.key] }}
|
||||
</div>
|
||||
<div v-else-if="column.icon == 'book-open'">
|
||||
{{ Math.ceil(row.courses[column.key]) }}%
|
||||
</div>
|
||||
<div v-else-if="column.icon == 'help-circle'">
|
||||
<Badge
|
||||
v-if="isAssignment(row.assessments[column.key])"
|
||||
:theme="getStatusTheme(row.assessments[column.key])"
|
||||
class="text-xs"
|
||||
>
|
||||
{{ row.assessments[column.key] }}
|
||||
</Badge>
|
||||
<div v-else>{{ parseInt(row.assessments[column.key]) }}%</div>
|
||||
</div>
|
||||
</ListRowItem>
|
||||
</template>
|
||||
</ListRow>
|
||||
@@ -74,7 +93,11 @@
|
||||
</template>
|
||||
<script setup>
|
||||
import {
|
||||
Avatar,
|
||||
Badge,
|
||||
Button,
|
||||
createResource,
|
||||
FeatherIcon,
|
||||
ListHeader,
|
||||
ListHeaderItem,
|
||||
ListSelectBanner,
|
||||
@@ -82,8 +105,6 @@ import {
|
||||
ListRows,
|
||||
ListView,
|
||||
ListRowItem,
|
||||
Avatar,
|
||||
Button,
|
||||
} from 'frappe-ui'
|
||||
import { Trash2, Plus } from 'lucide-vue-next'
|
||||
import { ref } from 'vue'
|
||||
@@ -109,27 +130,40 @@ const students = createResource({
|
||||
})
|
||||
|
||||
const getStudentColumns = () => {
|
||||
return [
|
||||
let columns = [
|
||||
{
|
||||
label: 'Full Name',
|
||||
key: 'full_name',
|
||||
width: 2,
|
||||
},
|
||||
{
|
||||
label: 'Courses Done',
|
||||
key: 'courses_completed',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
label: 'Assessments Done',
|
||||
key: 'assessments_completed',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
label: 'Last Active',
|
||||
key: 'last_active',
|
||||
width: '10rem',
|
||||
},
|
||||
]
|
||||
|
||||
if (students.data?.[0].courses) {
|
||||
Object.keys(students.data?.[0].courses).forEach((course) => {
|
||||
columns.push({
|
||||
label: course,
|
||||
key: course,
|
||||
width: '10rem',
|
||||
icon: 'book-open',
|
||||
align: 'center',
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
if (students.data?.[0].assessments) {
|
||||
Object.keys(students.data?.[0].assessments).forEach((assessment) => {
|
||||
columns.push({
|
||||
label: assessment,
|
||||
key: assessment,
|
||||
width: '10rem',
|
||||
icon: 'help-circle',
|
||||
align: isAssignment(students.data?.[0].assessments[assessment])
|
||||
? 'left'
|
||||
: 'center',
|
||||
})
|
||||
})
|
||||
}
|
||||
return columns
|
||||
}
|
||||
|
||||
const openStudentModal = () => {
|
||||
@@ -160,4 +194,18 @@ const removeStudents = (selections, unselectAll) => {
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const getStatusTheme = (status) => {
|
||||
if (status === 'Pass') {
|
||||
return 'green'
|
||||
} else if (status == 'Not Graded') {
|
||||
return 'orange'
|
||||
} else {
|
||||
return 'red'
|
||||
}
|
||||
}
|
||||
|
||||
const isAssignment = (value) => {
|
||||
return isNaN(value)
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
import { Dialog, createResource } from 'frappe-ui'
|
||||
import { ref } from 'vue'
|
||||
import Link from '@/components/Controls/Link.vue'
|
||||
import { showToast } from '@/utils'
|
||||
|
||||
const students = defineModel('reloadStudents')
|
||||
const student = ref()
|
||||
@@ -61,8 +62,11 @@ const addStudent = (close) => {
|
||||
{
|
||||
onSuccess() {
|
||||
students.value.reload()
|
||||
close()
|
||||
student.value = null
|
||||
close()
|
||||
},
|
||||
onError(err) {
|
||||
showToast(__('Error'), __(err.messages?.[0] || err), 'x')
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -252,7 +252,7 @@ import {
|
||||
} from 'frappe-ui'
|
||||
import Link from '@/components/Controls/Link.vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { showToast } from '../utils'
|
||||
import { showToast } from '@/utils'
|
||||
import { Image } from 'lucide-vue-next'
|
||||
import { capture } from '@/telemetry'
|
||||
import MultiSelect from '@/components/Controls/MultiSelect.vue'
|
||||
@@ -345,6 +345,10 @@ const batchDetail = createResource({
|
||||
data.instructors.forEach((instructor) => {
|
||||
instructors.value.push(instructor.instructor)
|
||||
})
|
||||
} else if (['start_time', 'end_time'].includes(key)) {
|
||||
let [hours, minutes, seconds] = data[key].split(':')
|
||||
hours = hours.length == 1 ? '0' + hours : hours
|
||||
batch[key] = `${hours}:${minutes}`
|
||||
} else if (Object.hasOwn(batch, key)) batch[key] = data[key]
|
||||
})
|
||||
let checkboxes = ['published', 'paid_batch', 'allow_self_enrollment']
|
||||
|
||||
Reference in New Issue
Block a user