feat: assignment grading

This commit is contained in:
Jannat Patel
2024-12-26 18:16:46 +05:30
parent 85f85063ac
commit 7f768e81f4
6 changed files with 342 additions and 211 deletions

View File

@@ -41,6 +41,11 @@
<div v-if="column.key == 'assessment_type'"> <div v-if="column.key == 'assessment_type'">
{{ row[column.key] == 'LMS Quiz' ? 'Quiz' : 'Assignment' }} {{ row[column.key] == 'LMS Quiz' ? 'Quiz' : 'Assignment' }}
</div> </div>
<div v-else-if="column.key == 'status'">
<Badge :theme="getStatusTheme(row[column.key])">
{{ row[column.key] }}
</Badge>
</div>
<div v-else> <div v-else>
{{ row[column.key] }} {{ row[column.key] }}
</div> </div>
@@ -83,6 +88,7 @@ import {
ListSelectBanner, ListSelectBanner,
createResource, createResource,
Button, Button,
Badge,
} from 'frappe-ui' } from 'frappe-ui'
import { inject, ref } from 'vue' import { inject, ref } from 'vue'
import AssessmentModal from '@/components/Modals/AssessmentModal.vue' import AssessmentModal from '@/components/Modals/AssessmentModal.vue'
@@ -180,7 +186,7 @@ const getAssessmentColumns = () => {
{ {
label: 'Assessment', label: 'Assessment',
key: 'title', key: 'title',
width: '30rem', width: '25rem',
}, },
{ {
label: 'Type', label: 'Type',
@@ -199,4 +205,14 @@ const getAssessmentColumns = () => {
} }
return columns return columns
} }
const getStatusTheme = (status) => {
if (status === 'Pass') {
return 'green'
} else if (status === 'Not Graded') {
return 'orange'
} else {
return 'red'
}
}
</script> </script>

View File

@@ -29,8 +29,8 @@
<slot name="item-label" v-bind="{ active, selected, option }" /> <slot name="item-label" v-bind="{ active, selected, option }" />
</template> </template>
<template v-if="attrs.onCreate" #footer="{ value, close }"> <template #footer="{ value, close }">
<div> <div v-if="attrs.onCreate">
<Button <Button
variant="ghost" variant="ghost"
class="w-full !justify-start" class="w-full !justify-start"
@@ -42,6 +42,18 @@
</template> </template>
</Button> </Button>
</div> </div>
<div>
<Button
variant="ghost"
class="w-full !justify-start"
:label="__('Clear')"
@click="() => clearValue(close)"
>
<template #prefix>
<X class="h-4 w-4 stroke-1.5" />
</template>
</Button>
</div>
</template> </template>
</Autocomplete> </Autocomplete>
<p v-if="description" class="text-sm text-gray-600">{{ description }}</p> <p v-if="description" class="text-sm text-gray-600">{{ description }}</p>
@@ -52,7 +64,7 @@
import Autocomplete from '@/components/Controls/Autocomplete.vue' import Autocomplete from '@/components/Controls/Autocomplete.vue'
import { watchDebounced } from '@vueuse/core' import { watchDebounced } from '@vueuse/core'
import { createResource, Button } from 'frappe-ui' import { createResource, Button } from 'frappe-ui'
import { Plus } from 'lucide-vue-next' import { Plus, X } from 'lucide-vue-next'
import { useAttrs, computed, ref } from 'vue' import { useAttrs, computed, ref } from 'vue'
const props = defineProps({ const props = defineProps({
@@ -75,9 +87,7 @@ const props = defineProps({
}) })
const emit = defineEmits(['update:modelValue', 'change']) const emit = defineEmits(['update:modelValue', 'change'])
const attrs = useAttrs() const attrs = useAttrs()
const valuePropPassed = computed(() => 'value' in attrs) const valuePropPassed = computed(() => 'value' in attrs)
const value = computed({ const value = computed({
@@ -131,7 +141,7 @@ const options = createResource({
}, },
}) })
function reload(val) { const reload = (val) => {
options.update({ options.update({
params: { params: {
txt: val, txt: val,
@@ -142,6 +152,11 @@ function reload(val) {
options.reload() options.reload()
} }
const clearValue = (close) => {
emit(valuePropPassed.value ? 'change' : 'update:modelValue', '')
close()
}
const labelClasses = computed(() => { const labelClasses = computed(() => {
return [ return [
{ {

View File

@@ -8,7 +8,7 @@
v-if="assignment.doc?.name" v-if="assignment.doc?.name"
:to="{ :to="{
name: 'AssignmentSubmissionList', name: 'AssignmentSubmissionList',
params: { query: {
assignmentID: assignment.doc.name, assignmentID: assignment.doc.name,
}, },
}" }"

View File

@@ -3,110 +3,128 @@
class="flex justify-between sticky top-0 z-10 border-b bg-white px-3 py-2.5 sm:px-5" class="flex justify-between sticky top-0 z-10 border-b bg-white px-3 py-2.5 sm:px-5"
> >
<Breadcrumbs :items="breadcrumbs" /> <Breadcrumbs :items="breadcrumbs" />
<Button variant="solid" @click="submitAssignment()"> <div class="flex items-center space-x-2">
{{ __('Save') }} <Badge
</Button> v-if="submissionResource.doc?.status"
</header> :theme="statusTheme"
<div class="container py-5"> size="lg"
<div >
v-if="submissionResource.data" {{ submissionResource.doc.status }}
class="bg-blue-100 p-2 rounded-md leading-5 text-sm mb-4" </Badge>
> <Button variant="solid" @click="submitAssignment()">
{{ __("You've successfully submitted the assignment.") }} {{ __('Save') }}
{{ </Button>
__(
"Once the moderator grades your submission, you'll find the details here."
)
}}
{{ __('Feel free to make edits to your submission if needed.') }}
</div> </div>
<div v-if="assignment.data"> </header>
<div> <div class="p-5 overflow-hidden h-[calc(100vh-3.2rem)]">
<div class="text-xl font-semibold hidden"> <div v-if="assignment.data" class="grid grid-cols-2 gap-5">
<div class="p-5 border rounded-md overflow-y-auto h-[calc(100vh-6rem)]">
<div class="text-sm text-gray-600 font-medium mb-2">
{{ __('Question') }} {{ __('Question') }}
</div> </div>
<div class="text-sm mt-1 hidden">
{{
__('Read the question carefully before attempting the assignment.')
}}
</div>
<div <div
v-html="assignment.data.question" v-html="assignment.data.question"
class="ProseMirror prose prose-table:table-fixed prose-td:p-2 prose-th:p-2 prose-td:border prose-th:border prose-td:border-gray-300 prose-th:border-gray-300 prose-td:relative prose-th:relative prose-th:bg-gray-100 prose-sm max-w-none !whitespace-normal" class="ProseMirror prose prose-table:table-fixed prose-td:p-2 prose-th:p-2 prose-td:border prose-th:border prose-td:border-gray-300 prose-th:border-gray-300 prose-td:relative prose-th:relative prose-th:bg-gray-100 prose-sm max-w-none !whitespace-normal"
></div> ></div>
</div> </div>
<div class="">
<div class="text-xl font-semibold mt-10"> <div class="flex flex-col">
{{ __('Submission') }} <div class="p-5 border rounded-md">
</div> <div class="text-sm text-gray-600 font-medium mb-4">
<div v-if="showUploader()"> {{ __('Submission') }}
<div class="text-sm mt-1 mb-4">
{{ __('Add your assignment as {0}').format(assignment.data.type) }}
</div> </div>
<FileUploader <div
v-if="!submissionFile" v-if="submissionName != 'new'"
:fileTypes="getType()" class="bg-blue-100 p-2 rounded-md leading-5 text-sm mb-4"
:validateFile="validateFile"
@success="(file) => saveSubmission(file)"
> >
<template {{ __("You've successfully submitted the assignment.") }}
#default="{ {{
file, __(
uploading, "Once the moderator grades your submission, you'll find the details here."
progress, )
uploaded, }}
message, {{ __('Feel free to make edits to your submission if needed.') }}
error, </div>
total, <div v-if="showUploader()">
success, <div class="text-xs text-gray-600 mt-1 mb-2">
openFileSelector, {{
}" __('Add your assignment as {0}').format(assignment.data.type)
}}
</div>
<FileUploader
v-if="!submissionFile"
:fileTypes="getType()"
:validateFile="validateFile"
@success="(file) => saveSubmission(file)"
> >
<Button @click="openFileSelector" :loading="uploading"> <template #default="{ uploading, progress, openFileSelector }">
{{ <Button @click="openFileSelector" :loading="uploading">
uploading {{
? __('Uploading {0}%').format(progress) uploading
: __('Upload File') ? __('Uploading {0}%').format(progress)
}} : __('Upload File')
</Button> }}
</template> </Button>
</FileUploader> </template>
<div v-else> </FileUploader>
<div class="flex items-center"> <div v-else>
<div class="border rounded-md p-2 mr-2"> <div class="flex items-center">
<FileText class="h-5 w-5 stroke-1.5 text-gray-700" /> <div class="border rounded-md p-2 mr-2">
<FileText class="h-5 w-5 stroke-1.5 text-gray-700" />
</div>
<div class="flex flex-col">
<span>
{{ submissionFile.file_name }}
</span>
<span class="text-sm text-gray-500 mt-1">
{{ getFileSize(submissionFile.file_size) }}
</span>
</div>
<X
v-if="submissionResource.doc.owner == user.data?.name"
@click="removeSubmission()"
class="bg-gray-200 rounded-md cursor-pointer stroke-1.5 w-5 h-5 p-1 ml-4"
/>
</div> </div>
<div class="flex flex-col">
<span>
{{ submissionFile.file_name }}
</span>
<span class="text-sm text-gray-500 mt-1">
{{ getFileSize(submissionFile.file_size) }}
</span>
</div>
<X
@click="removeSubmission()"
class="bg-gray-200 rounded-md cursor-pointer stroke-1.5 w-5 h-5 p-1 ml-4"
/>
</div> </div>
</div> </div>
</div> <div v-else-if="assignment.data.type == 'URL'" class="mt-5">
<div v-else-if="assignment.data.type == 'URL'"> <div class="text-xs text-gray-600 mb-1">
<div class="text-sm mb-4"> {{ __('Enter a URL') }}
{{ __('Enter a URL') }} </div>
<FormControl v-model="answer" />
</div> </div>
<FormControl v-model="answer" /> <div v-else>
</div> <div class="text-sm mb-4">
<div v-else> {{ __('Write your answer here') }}
<div class="text-sm mb-4"> </div>
{{ __('Write your answer here') }} <TextEditor
:content="answer"
@change="(val) => (answer = val)"
:editable="true"
:fixedMenu="true"
editorClass="prose-sm max-w-none border-b border-x bg-gray-100 rounded-b-md py-1 px-2 min-h-[7rem]"
/>
</div> </div>
<TextEditor </div>
:content="answer" <!-- Grading -->
@change="(val) => (answer = val)" <div
:editable="true" v-if="canGradeSubmission"
:fixedMenu="true" class="p-3 border rounded-md mt-5 space-y-4"
editorClass="prose-sm max-w-none border-b border-x bg-gray-100 rounded-b-md py-1 px-2 min-h-[7rem]" >
<div class="text-sm text-gray-600 font-medium mb-5">
{{ __('Grading') }}
</div>
<FormControl
v-model="submissionResource.doc.status"
:label="__('Grade')"
type="select"
:options="submissionStatusOptions"
/>
<FormControl
v-model="submissionResource.doc.comments"
:label="__('Comments')"
type="textarea"
/> />
</div> </div>
</div> </div>
@@ -119,8 +137,10 @@ import {
createResource, createResource,
FileUploader, FileUploader,
Button, Button,
Badge,
FormControl, FormControl,
TextEditor, TextEditor,
createDocumentResource,
} from 'frappe-ui' } from 'frappe-ui'
import { FileText, X } from 'lucide-vue-next' import { FileText, X } from 'lucide-vue-next'
import { computed, inject, onMounted, ref } from 'vue' import { computed, inject, onMounted, ref } from 'vue'
@@ -143,6 +163,12 @@ const props = defineProps({
}, },
}) })
onMounted(() => {
if (!user.data) {
window.location.href = '/login'
}
})
const assignment = createResource({ const assignment = createResource({
url: 'frappe.client.get', url: 'frappe.client.get',
params: { params: {
@@ -150,29 +176,17 @@ const assignment = createResource({
name: props.assignmentID, name: props.assignmentID,
}, },
auto: true, auto: true,
onSuccess(data) {
if (props.submissionName != 'new') {
submissionResource.reload()
}
},
}) })
const showUploader = () => { const showUploader = () => {
return ['PDF', 'Image', 'Document'].includes(assignment.data?.type) return ['PDF', 'Image', 'Document'].includes(assignment.data?.type)
} }
const updateSubmission = createResource({
url: 'frappe.client.set_value',
makeParams(values) {
let fieldname = {}
if (showUploader()) {
fieldname.assignment_attachment = submissionFile.value.file_url
} else {
fieldname.answer = answer.value
}
return {
doctype: 'LMS Assignment Submission',
name: props.submissionName,
fieldname: fieldname,
}
},
})
const imageResource = createResource({ const imageResource = createResource({
url: 'lms.lms.api.get_file_info', url: 'lms.lms.api.get_file_info',
makeParams(values) { makeParams(values) {
@@ -205,43 +219,25 @@ const newSubmission = createResource({
}, },
}) })
const submissionResource = createResource({ const submissionResource = createDocumentResource({
url: 'frappe.client.get_value', doctype: 'LMS Assignment Submission',
params: { name: props.submissionName,
doctype: 'LMS Assignment Submission', auto: false,
fieldname: showUploader() ? 'assignment_attachment' : 'answer',
filters: {
name: props.submissionName,
},
},
onSuccess(data) { onSuccess(data) {
if (data.assignment_attachment) if (data.assignment_attachment) {
imageResource.reload({ image: data.assignment_attachment }) imageResource.reload({ image: data.assignment_attachment })
if (data.answer) answer.value = data.answer }
}, },
}) })
onMounted(() => {
if (!user.data) {
window.location.href = '/login'
}
if (props.submissionName != 'new') {
submissionResource.reload()
}
})
const submitAssignment = () => { const submitAssignment = () => {
if (props.submissionName != 'new') { if (props.submissionName != 'new') {
updateSubmission.submit( let evaluator =
{}, submissionResource.doc.owner != user.data?.name ? user.data?.name : null
{ submissionResource.setValue.submit({
onSuccess(data) { ...submissionResource.doc,
showToast('Success', 'Submission updated successfully.', 'check') evaluator: evaluator,
}, })
onError(err) {
showToast('Error', err.messages?.[0] || err, 'x')
},
}
)
} else { } else {
addNewSubmission() addNewSubmission()
} }
@@ -271,7 +267,8 @@ const addNewSubmission = () => {
const breadcrumbs = computed(() => { const breadcrumbs = computed(() => {
let crumbs = [ let crumbs = [
{ {
label: 'Assignment', label: 'Submissions',
route: { name: 'AssignmentSubmissionList' },
}, },
{ {
label: assignment.data?.title, label: assignment.data?.title,
@@ -325,4 +322,31 @@ const validateFile = (file) => {
const removeSubmission = () => { const removeSubmission = () => {
submissionFile.value = null submissionFile.value = null
} }
const canGradeSubmission = computed(() => {
return (
(user.data?.is_moderator ||
user.data?.is_evaluator ||
user.data?.is_instructor) &&
props.submissionName != 'new'
)
})
const submissionStatusOptions = computed(() => {
return [
{ label: 'Not Graded', value: 'Not Graded' },
{ label: 'Pass', value: 'Pass' },
{ label: 'Fail', value: 'Fail' },
]
})
const statusTheme = computed(() => {
if (submissionResource.doc.status == 'Pass') {
return 'green'
} else if (submissionResource.doc.status == 'Not Graded') {
return 'orange'
} else {
return 'red'
}
})
</script> </script>

View File

@@ -4,19 +4,23 @@
> >
<Breadcrumbs :items="breadcrumbs" /> <Breadcrumbs :items="breadcrumbs" />
</header> </header>
<div <div class="md:w-3/4 md:mx-auto py-5 mx-5">
v-if="submissions.loading || submissions.data?.length" <div class="grid grid-cols-3 gap-5 mb-5">
class="md:w-3/4 md:mx-auto py-5 mx-5"
>
<div class="grid grid-cols-4 gap-5 mb-5">
<Link <Link
doctype="LMS Assignment" doctype="LMS Assignment"
v-model="assignmentID" v-model="assignmentID"
:placeholder="__('Assignment')" :placeholder="__('Assignment')"
/> />
<Link doctype="User" v-model="member" :placeholder="__('Member')" /> <Link doctype="User" v-model="member" :placeholder="__('Member')" />
<FormControl
v-model="status"
type="select"
:options="statusOptions"
:placeholder="__('Status')"
/>
</div> </div>
<ListView <ListView
v-if="submissions.loading || submissions.data?.length"
:columns="submissionColumns" :columns="submissionColumns"
:rows="submissions.data" :rows="submissions.data"
rowKey="name" rowKey="name"
@@ -54,22 +58,18 @@
</router-link> </router-link>
</ListRows> </ListRows>
</ListView> </ListView>
</div> <div
<div v-else
v-else class="text-center p-5 text-gray-600 mt-52 w-3/4 md:w-1/2 mx-auto space-y-2"
class="text-center p-5 text-gray-600 mt-52 w-3/4 md:w-1/2 mx-auto space-y-2" >
> <Pencil class="size-8 mx-auto stroke-1 text-gray-500" />
<Pencil class="size-8 mx-auto stroke-1 text-gray-500" /> <div class="text-xl font-medium">
<div class="text-xl font-medium"> {{ __('No submissions') }}
{{ __('No submissions') }} </div>
<div class="leading-5">
{{ __('There are no submissions for this assignment.') }}
</div>
</div> </div>
<!-- <div class="leading-5">
{{
__(
'There are no submissions for the assignment {0}.',
).format(assignmentTitle.data?.title)
}}
</div> -->
</div> </div>
</template> </template>
<script setup> <script setup>
@@ -77,7 +77,7 @@ import {
Badge, Badge,
Breadcrumbs, Breadcrumbs,
createListResource, createListResource,
createResource, FormControl,
ListView, ListView,
ListHeader, ListHeader,
ListHeaderItem, ListHeaderItem,
@@ -95,33 +95,34 @@ const dayjs = inject('$dayjs')
const router = useRouter() const router = useRouter()
const assignmentID = ref('') const assignmentID = ref('')
const member = ref('') const member = ref('')
const status = ref('')
onMounted(() => { onMounted(() => {
if (!user.data?.is_instructor && !user.data?.is_moderator) { if (!user.data?.is_instructor && !user.data?.is_moderator) {
router.push({ name: 'Courses' }) router.push({ name: 'Courses' })
} }
assignmentID.value = router.currentRoute.value.query.assignmentID
assignmentID.value = router.currentRoute.value.params.assignmentID member.value = router.currentRoute.value.query.member
submissions.reload() status.value = router.currentRoute.value.query.status
reloadSubmissions()
}) })
const getAssignmentFilters = () => { const getAssignmentFilters = () => {
let filters = {} let filters = {}
if (assignmentID.value) { if (assignmentID.value) {
console.log(assignmentID.value)
filters.assignment = assignmentID.value filters.assignment = assignmentID.value
} }
if (member.value) { if (member.value) {
console.log(member.value)
filters.member = member.value filters.member = member.value
} }
console.log(filters) if (status.value) {
filters.status = status.value
}
return filters return filters
} }
const submissions = createListResource({ const submissions = createListResource({
doctype: 'LMS Assignment Submission', doctype: 'LMS Assignment Submission',
filters: getAssignmentFilters(),
fields: [ fields: [
'name', 'name',
'assignment', 'assignment',
@@ -141,26 +142,31 @@ const submissions = createListResource({
}, },
}) })
watch([assignmentID, member], () => { // watch changes in assignmentID, member, and status and if changes in any then reload submissions. Also update the url query params for the same
console.log('watch called') watch([assignmentID, member, status], () => {
submissions.reload() router.push({
query: {
assignmentID: assignmentID.value,
member: member.value,
status: status.value,
},
})
reloadSubmissions()
}) })
/* const assignmentTitle = createResource({ const reloadSubmissions = () => {
url: "frappe.client.get_value", submissions.update({
params: { filters: getAssignmentFilters(),
doctype: "LMS Assignment", })
fieldname: "title", submissions.reload()
filters: { name: props.assignmentID }, }
},
}) */
const submissionColumns = computed(() => { const submissionColumns = computed(() => {
return [ return [
{ {
label: 'Member', label: 'Member',
key: 'member_name', key: 'member_name',
width: 2, width: 1,
}, },
{ {
label: 'Assignment', label: 'Assignment',
@@ -182,6 +188,15 @@ const submissionColumns = computed(() => {
] ]
}) })
const statusOptions = computed(() => {
return [
{ label: '', value: '' },
{ label: 'Pass', value: 'Pass' },
{ label: 'Fail', value: 'Fail' },
{ label: 'Not Graded', value: 'Not Graded' },
]
})
const getStatusTheme = (status) => { const getStatusTheme = (status) => {
if (status === 'Pass') { if (status === 'Pass') {
return 'green' return 'green'

View File

@@ -20,8 +20,18 @@
</router-link> </router-link>
</header> </header>
<div v-if="assignments.data?.length" class="md:w-3/4 md:mx-auto py-5 mx-5"> <div class="md:w-3/4 md:mx-auto py-5 mx-5">
<div class="grid grid-cols-3 gap-5 mb-5">
<FormControl v-model="titleFilter" :placeholder="__('Search by title')" />
<FormControl
v-model="typeFilter"
type="select"
:options="assignmentTypes"
:placeholder="__('Type')"
/>
</div>
<ListView <ListView
v-if="assignments.data?.length"
:columns="assignmentColumns" :columns="assignmentColumns"
:rows="assignments.data" :rows="assignments.data"
row-key="name" row-key="name"
@@ -37,50 +47,91 @@
}" }"
> >
</ListView> </ListView>
<div class="flex justify-center my-5"> <div
<Button v-if="assignments.hasNextPage" @click="assignments.next()"> v-else
class="text-center p-5 text-gray-600 mt-52 w-3/4 md:w-1/2 mx-auto space-y-2"
>
<Pencil class="size-10 mx-auto stroke-1 text-gray-500" />
<div class="text-xl font-medium">
{{ __('No assignments found') }}
</div>
<div class="leading-5">
{{
__(
'You have not created any assignments yet. To create a new assignment, click on the "New" button above.'
)
}}
</div>
</div>
<div v-if="assignments.hasNextPage" class="flex justify-center my-5">
<Button @click="assignments.next()">
{{ __('Load More') }} {{ __('Load More') }}
</Button> </Button>
</div> </div>
</div> </div>
<div
v-else
class="text-center p-5 text-gray-600 mt-52 w-3/4 md:w-1/2 mx-auto space-y-2"
>
<Pencil class="size-10 mx-auto stroke-1 text-gray-500" />
<div class="text-xl font-medium">
{{ __('No assignments found') }}
</div>
<div class="leading-5">
{{
__(
'You have not created any assignments yet. To create a new assignment, click on the "New" button above.'
)
}}
</div>
</div>
</template> </template>
<script setup> <script setup>
import { Breadcrumbs, Button, createListResource, ListView } from 'frappe-ui' import {
import { computed, inject } from 'vue' Breadcrumbs,
Button,
createListResource,
FormControl,
ListView,
} from 'frappe-ui'
import { computed, inject, onMounted, ref, watch } from 'vue'
import { Plus, Pencil } from 'lucide-vue-next' import { Plus, Pencil } from 'lucide-vue-next'
import { useRouter } from 'vue-router'
const user = inject('$user') const user = inject('$user')
const dayjs = inject('$dayjs') const dayjs = inject('$dayjs')
const titleFilter = ref('')
const typeFilter = ref('')
const router = useRouter()
onMounted(() => {
if (!user.data?.is_moderator && !user.data?.is_instructor) {
router.push({ name: 'Courses' })
}
titleFilter.value = router.currentRoute.value.query.title
typeFilter.value = router.currentRoute.value.query.type
})
watch([titleFilter, typeFilter], () => {
router.push({
query: {
title: titleFilter.value,
type: typeFilter.value,
},
})
reloadAssignments()
})
const reloadAssignments = () => {
assignments.update({
filters: assignmentFilter.value,
})
assignments.reload()
}
const assignmentFilter = computed(() => { const assignmentFilter = computed(() => {
if (user.data?.is_moderator) return {} let filters = {}
return { if (titleFilter.value) {
owner: user.data?.name, filters.title = ['like', `%${titleFilter.value}%`]
} }
if (typeFilter.value) {
filters.type = typeFilter.value
}
if (!user.data?.is_moderator) {
filters.owner = user.data?.email
}
return filters
}) })
const assignments = createListResource({ const assignments = createListResource({
doctype: 'LMS Assignment', doctype: 'LMS Assignment',
fields: ['name', 'title', 'type', 'creation'], fields: ['name', 'title', 'type', 'creation'],
filters: assignmentFilter,
orderBy: 'modified desc', orderBy: 'modified desc',
auto: true,
cache: ['assignments'], cache: ['assignments'],
transform(data) { transform(data) {
return data.map((row) => { return data.map((row) => {
@@ -103,7 +154,7 @@ const assignmentColumns = computed(() => {
label: __('Type'), label: __('Type'),
key: 'type', key: 'type',
width: 1, width: 1,
align: 'center', align: 'left',
}, },
{ {
label: __('Created'), label: __('Created'),
@@ -114,6 +165,16 @@ const assignmentColumns = computed(() => {
] ]
}) })
const assignmentTypes = computed(() => {
let types = ['', 'Document', 'Image', 'PDF', 'URL', 'Text']
return types.map((type) => {
return {
label: __(type),
value: type,
}
})
})
const breadcrumbs = computed(() => [ const breadcrumbs = computed(() => [
{ {
label: 'Assignments', label: 'Assignments',