Merge pull request #2054 from pateljannat/issues-178
feat: student progress in course dashboard
This commit is contained in:
@@ -99,18 +99,17 @@
|
||||
name="item-label"
|
||||
v-bind="{ active, selected, option }"
|
||||
>
|
||||
<div class="flex flex-col space-y-1 text-ink-gray-8">
|
||||
<div>
|
||||
{{ option.label }}
|
||||
<div class="flex flex-col gap-1 p-1">
|
||||
<div class="text-base font-medium text-ink-gray-8">
|
||||
{{
|
||||
option.value == option.label
|
||||
? option.description
|
||||
: option.label
|
||||
}}
|
||||
</div>
|
||||
<div class="text-sm text-ink-gray-5">
|
||||
{{ option.value }}
|
||||
</div>
|
||||
<div
|
||||
v-if="
|
||||
option.description &&
|
||||
option.description != option.label
|
||||
"
|
||||
class="text-xs text-ink-gray-7"
|
||||
v-html="option.description"
|
||||
></div>
|
||||
</div>
|
||||
</slot>
|
||||
</li>
|
||||
|
||||
@@ -112,6 +112,14 @@
|
||||
v-else-if="lesson.icon === 'icon-quiz'"
|
||||
class="h-4 w-4 stroke-1 mr-2"
|
||||
/>
|
||||
<NotebookPen
|
||||
v-else-if="lesson.icon === 'icon-assignment'"
|
||||
class="h-4 w-4 stroke-1 mr-2"
|
||||
/>
|
||||
<SquareCode
|
||||
v-else-if="lesson.icon === 'icon-code'"
|
||||
class="h-4 w-4 stroke-1 mr-2"
|
||||
/>
|
||||
<FileText
|
||||
v-else-if="lesson.icon === 'icon-list'"
|
||||
class="h-4 w-4 text-ink-gray-9 stroke-1 mr-2"
|
||||
@@ -177,8 +185,11 @@ import {
|
||||
FilePenLine,
|
||||
HelpCircle,
|
||||
MonitorPlay,
|
||||
NotebookPen,
|
||||
Plus,
|
||||
SquareCode,
|
||||
Trash2,
|
||||
Notebook,
|
||||
} from 'lucide-vue-next'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import ChapterModal from '@/components/Modals/ChapterModal.vue'
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<Dialog
|
||||
v-model="show"
|
||||
:options="{
|
||||
title: __('Add a Student'),
|
||||
title: __('Enroll a Student'),
|
||||
size: 'sm',
|
||||
actions: [
|
||||
{
|
||||
@@ -19,9 +19,24 @@
|
||||
doctype="User"
|
||||
v-model="student"
|
||||
:filters="{ ignore_user_type: 1 }"
|
||||
placeholder=" "
|
||||
:label="__('Student')"
|
||||
:onCreate="
|
||||
(value, close) => {
|
||||
openSettings('Members', close)
|
||||
() => {
|
||||
openSettings('Members')
|
||||
show = false
|
||||
}
|
||||
"
|
||||
:required="true"
|
||||
/>
|
||||
<Link
|
||||
doctype="LMS Payment"
|
||||
v-model="payment"
|
||||
placeholder=" "
|
||||
:label="__('Payment')"
|
||||
:onCreate="
|
||||
() => {
|
||||
openSettings('Transactions')
|
||||
show = false
|
||||
}
|
||||
"
|
||||
@@ -31,15 +46,16 @@
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup>
|
||||
import { Dialog, createResource, toast } from 'frappe-ui'
|
||||
import { call, Dialog, toast } from 'frappe-ui'
|
||||
import { ref, inject } from 'vue'
|
||||
import Link from '@/components/Controls/Link.vue'
|
||||
import { useOnboarding } from 'frappe-ui/frappe'
|
||||
import { openSettings } from '@/utils'
|
||||
import Link from '@/components/Controls/Link.vue'
|
||||
|
||||
const students = defineModel('reloadStudents')
|
||||
const batchModal = defineModel('batchModal')
|
||||
const student = ref()
|
||||
const student = ref(null)
|
||||
const payment = ref(null)
|
||||
const user = inject('$user')
|
||||
const { updateOnboardingStep } = useOnboarding('learning')
|
||||
const show = defineModel()
|
||||
@@ -51,36 +67,28 @@ const props = defineProps({
|
||||
},
|
||||
})
|
||||
|
||||
const studentResource = createResource({
|
||||
url: 'frappe.client.insert',
|
||||
makeParams(values) {
|
||||
return {
|
||||
doc: {
|
||||
doctype: 'LMS Batch Enrollment',
|
||||
batch: props.batch,
|
||||
member: student.value,
|
||||
},
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const addStudent = (close) => {
|
||||
studentResource.submit(
|
||||
{},
|
||||
{
|
||||
onSuccess() {
|
||||
if (user.data?.is_system_manager)
|
||||
updateOnboardingStep('add_batch_student')
|
||||
call('frappe.client.insert', {
|
||||
doc: {
|
||||
doctype: 'LMS Batch Enrollment',
|
||||
batch: props.batch,
|
||||
member: student.value,
|
||||
payment: payment.value,
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
if (user.data?.is_system_manager)
|
||||
updateOnboardingStep('add_batch_student')
|
||||
|
||||
students.value.reload()
|
||||
batchModal.value.reload()
|
||||
student.value = null
|
||||
close()
|
||||
},
|
||||
onError(err) {
|
||||
toast.error(err.messages?.[0] || err)
|
||||
},
|
||||
}
|
||||
)
|
||||
students.value.reload()
|
||||
batchModal.value.reload()
|
||||
student.value = null
|
||||
payment.value = null
|
||||
close()
|
||||
})
|
||||
.catch((err) => {
|
||||
toast.error(err.messages?.[0] || err)
|
||||
console.error(err)
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
</div>
|
||||
<div class="flex items-center space-x-2">
|
||||
<slot name="prefix" />
|
||||
<div class="font-semibold text-2xl">
|
||||
<div class="font-semibold text-ink-gray-9 text-2xl">
|
||||
{{ value }}
|
||||
</div>
|
||||
<slot name="suffix" />
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
<div v-else>
|
||||
<div class="flex items-center text-sm space-x-2">
|
||||
<div
|
||||
class="flex items-center justify-center rounded border border-outline-gray-1 bg-surface-gray-2"
|
||||
class="flex items-center justify-center rounded border border-outline-gray-modals bg-surface-gray-2"
|
||||
:class="field.size == 'lg' ? 'px-5 py-5' : 'px-20 py-8'"
|
||||
>
|
||||
<img
|
||||
@@ -90,7 +90,7 @@
|
||||
</div>
|
||||
<X
|
||||
@click="data[field.name] = null"
|
||||
class="border text-ink-gray-7 border-outline-gray-3 rounded-md cursor-pointer stroke-1.5 w-5 h-5 p-1 ml-4"
|
||||
class="border text-ink-gray-7 border-outline-gray-modals rounded-md cursor-pointer stroke-1.5 w-5 h-5 p-1 ml-4"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<div class="grid grid-cols-[2fr_1fr] gap-5 items-start">
|
||||
<div v-if="course.data?.enrollments" class="border rounded-lg py-3 px-4">
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
<div class="text-lg font-semibold">
|
||||
<div class="text-lg text-ink-gray-9 font-semibold">
|
||||
{{ __('Students') }}
|
||||
</div>
|
||||
<div class="flex items-center space-x-2">
|
||||
@@ -63,50 +63,52 @@
|
||||
</ListHeaderItem>
|
||||
</ListHeader>
|
||||
<ListRows v-for="row in progressList.data" class="max-h-[500px]">
|
||||
<router-link
|
||||
:to="{
|
||||
name: 'Profile',
|
||||
params: { username: row.member_username },
|
||||
}"
|
||||
<ListRow
|
||||
:row="row"
|
||||
@click="
|
||||
() => {
|
||||
showProgressModal = true
|
||||
currentStudent = row
|
||||
}
|
||||
"
|
||||
class="cursor-pointer"
|
||||
>
|
||||
<ListRow :row="row">
|
||||
<template #default="{ column, item }">
|
||||
<ListRowItem
|
||||
:item="row[column.key]"
|
||||
:align="column.align"
|
||||
class="w-full"
|
||||
>
|
||||
<template #prefix>
|
||||
<div v-if="column.key == 'member_name'">
|
||||
<Avatar
|
||||
class="flex items-center"
|
||||
:image="row['member_image']"
|
||||
:label="item"
|
||||
size="sm"
|
||||
/>
|
||||
</div>
|
||||
<ProgressBar
|
||||
v-else-if="column.key == 'progress'"
|
||||
:progress="Math.ceil(row[column.key])"
|
||||
class="!mx-0 !mr-4"
|
||||
<template #default="{ column, item }">
|
||||
<ListRowItem
|
||||
:item="row[column.key]"
|
||||
:align="column.align"
|
||||
class="w-full"
|
||||
>
|
||||
<template #prefix>
|
||||
<div v-if="column.key == 'member_name'">
|
||||
<Avatar
|
||||
class="flex items-center"
|
||||
:image="row['member_image']"
|
||||
:label="item"
|
||||
size="sm"
|
||||
/>
|
||||
</template>
|
||||
<div v-if="column.key == 'creation'">
|
||||
{{ dayjs(row[column.key]).format('DD MMM YYYY') }}
|
||||
</div>
|
||||
<div
|
||||
<ProgressBar
|
||||
v-else-if="column.key == 'progress'"
|
||||
class="text-xs !mx-0 w-5"
|
||||
>
|
||||
{{ Math.ceil(row[column.key]) }}%
|
||||
</div>
|
||||
<div v-else>
|
||||
{{ row[column.key].toString() }}
|
||||
</div>
|
||||
</ListRowItem>
|
||||
</template>
|
||||
</ListRow>
|
||||
</router-link>
|
||||
:progress="Math.ceil(row[column.key])"
|
||||
class="!mx-0 !mr-4"
|
||||
/>
|
||||
</template>
|
||||
<div v-if="column.key == 'creation'">
|
||||
{{ dayjs(row[column.key]).format('DD MMM YYYY') }}
|
||||
</div>
|
||||
<div
|
||||
v-else-if="column.key == 'progress'"
|
||||
class="text-xs !mx-0 w-5"
|
||||
>
|
||||
{{ Math.ceil(row[column.key]) }}%
|
||||
</div>
|
||||
<div v-else>
|
||||
{{ row[column.key].toString() }}
|
||||
</div>
|
||||
</ListRowItem>
|
||||
</template>
|
||||
</ListRow>
|
||||
</ListRows>
|
||||
</ListView>
|
||||
<div
|
||||
@@ -130,7 +132,7 @@
|
||||
<div class="grid grid-cols-[2fr_1fr] items-center justify-between">
|
||||
<div class="flex flex-col space-y-4 flex-1 text-sm">
|
||||
<div
|
||||
class="flex items-center"
|
||||
class="flex items-center text-ink-gray-7"
|
||||
v-for="row in chartDetails.data?.progress_distribution"
|
||||
>
|
||||
<div
|
||||
@@ -142,6 +144,8 @@
|
||||
? 'red'
|
||||
: row.name.startsWith('In')
|
||||
? 'amber'
|
||||
: row.name.startsWith('Adv')
|
||||
? 'blue'
|
||||
: 'green'
|
||||
][400],
|
||||
}"
|
||||
@@ -151,11 +155,13 @@
|
||||
{{ row.name.split('(')[0] }}
|
||||
</div>
|
||||
</Tooltip>
|
||||
<div class="ml-auto">
|
||||
{{
|
||||
Math.round((row.value / course.data?.enrollments) * 100)
|
||||
}}%
|
||||
</div>
|
||||
<Tooltip :text="row.value">
|
||||
<div class="ml-auto">
|
||||
{{
|
||||
Math.round((row.value / course.data?.enrollments) * 100)
|
||||
}}%
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<ECharts
|
||||
@@ -205,7 +211,7 @@
|
||||
class="!w-32"
|
||||
/>
|
||||
</div>
|
||||
<div class="divide-y max-h-[43vh] overflow-y-auto">
|
||||
<div class="divide-y max-h-[43vh] text-ink-gray-7 overflow-y-auto">
|
||||
<div
|
||||
v-for="progress in lessonProgress.data"
|
||||
class="flex justify-between text-sm py-2 my-1"
|
||||
@@ -239,6 +245,13 @@
|
||||
v-model="showEnrollmentModal"
|
||||
:course="course"
|
||||
/>
|
||||
<StudentCourseProgress
|
||||
v-if="showProgressModal"
|
||||
v-model="showProgressModal"
|
||||
:course="course"
|
||||
:student="currentStudent"
|
||||
:lessons="lessonProgress"
|
||||
/>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
@@ -260,12 +273,13 @@ import {
|
||||
Tooltip,
|
||||
} from 'frappe-ui'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { ChevronDown, Plus, Star } from 'lucide-vue-next'
|
||||
import { Plus, Star } from 'lucide-vue-next'
|
||||
import { formatAmount } from '@/utils'
|
||||
import colors from '@/utils/frappe-ui-colors.json'
|
||||
import CourseEnrollmentModal from '@/pages/Courses/CourseEnrollmentModal.vue'
|
||||
import NumberChartGraph from '@/components/NumberChartGraph.vue'
|
||||
import ProgressBar from '@/components/ProgressBar.vue'
|
||||
import StudentCourseProgress from '@/pages/Courses/StudentCourseProgress.vue'
|
||||
|
||||
const props = defineProps<{
|
||||
course: any
|
||||
@@ -273,6 +287,8 @@ const props = defineProps<{
|
||||
|
||||
const showEnrollmentModal = ref(false)
|
||||
const searchFilter = ref<string | null>(null)
|
||||
const showProgressModal = ref(false)
|
||||
const currentStudent = ref<any>(null)
|
||||
const theme = ref<'darkMode' | 'lightMode'>(
|
||||
localStorage.getItem('theme') == 'dark' ? 'darkMode' : 'lightMode'
|
||||
)
|
||||
@@ -307,6 +323,7 @@ const progressList = createListResource({
|
||||
],
|
||||
pageLength: 100,
|
||||
auto: true,
|
||||
cache: ['courseProgress', props.course.data?.name],
|
||||
})
|
||||
|
||||
const lessonProgress = createResource({
|
||||
@@ -357,6 +374,7 @@ const progressColors = computed(() => {
|
||||
let colorList = []
|
||||
colorList.push(colors[theme.value]['red'][400])
|
||||
colorList.push(colors[theme.value]['amber'][400])
|
||||
colorList.push(colors[theme.value]['blue'][400])
|
||||
colorList.push(colors[theme.value]['green'][400])
|
||||
return colorList
|
||||
})
|
||||
|
||||
@@ -19,8 +19,7 @@
|
||||
placeholder=" "
|
||||
v-model="student"
|
||||
:required="true"
|
||||
:allowCreate="true"
|
||||
@create="
|
||||
:onCreate="
|
||||
() => {
|
||||
openSettings('Members')
|
||||
show = false
|
||||
@@ -33,8 +32,7 @@
|
||||
:label="__('Payment')"
|
||||
placeholder=" "
|
||||
v-model="payment"
|
||||
:allowCreate="true"
|
||||
@create="
|
||||
:onCreate="
|
||||
() => {
|
||||
openSettings('Transactions')
|
||||
show = false
|
||||
@@ -54,9 +52,9 @@
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { Button, call, Dialog, FormControl, toast } from 'frappe-ui'
|
||||
import { Link } from 'frappe-ui/frappe'
|
||||
import { ref } from 'vue'
|
||||
import { openSettings } from '@/utils'
|
||||
import Link from '@/components/Controls/Link.vue'
|
||||
|
||||
const show = defineModel<boolean>({ required: true, default: false })
|
||||
const student = ref<string | null>(null)
|
||||
|
||||
220
frontend/src/pages/Courses/StudentCourseProgress.vue
Normal file
220
frontend/src/pages/Courses/StudentCourseProgress.vue
Normal file
@@ -0,0 +1,220 @@
|
||||
<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>
|
||||
@@ -72,7 +72,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="myCourses.data?.length" class="mt-10">
|
||||
<div v-if="myCourses.data?.length">
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
<span class="font-semibold text-lg text-ink-gray-9">
|
||||
{{
|
||||
|
||||
@@ -513,7 +513,8 @@ const getSidebarItems = () => {
|
||||
: settings.data?.contact_us_email,
|
||||
condition: () => {
|
||||
return (
|
||||
settings?.data?.contact_us_email ||
|
||||
(settings?.data?.contact_us_email &&
|
||||
userResource?.data) ||
|
||||
settings?.data?.contact_us_url
|
||||
)
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user