fix: assignment dirty state and comments view to student
This commit is contained in:
@@ -19,6 +19,7 @@
|
|||||||
:options="{
|
:options="{
|
||||||
showTooltip: false,
|
showTooltip: false,
|
||||||
getRowRoute: (row) => getRowRoute(row),
|
getRowRoute: (row) => getRowRoute(row),
|
||||||
|
selectable: user.data?.is_student ? false : true,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<ListHeader
|
<ListHeader
|
||||||
@@ -41,7 +42,10 @@
|
|||||||
<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'">
|
<div v-else-if="column.key == 'title'">
|
||||||
|
{{ row[column.key] }}
|
||||||
|
</div>
|
||||||
|
<div v-else-if="isNaN(row[column.key])">
|
||||||
<Badge :theme="getStatusTheme(row[column.key])">
|
<Badge :theme="getStatusTheme(row[column.key])">
|
||||||
{{ row[column.key] }}
|
{{ row[column.key] }}
|
||||||
</Badge>
|
</Badge>
|
||||||
@@ -191,15 +195,15 @@ const getAssessmentColumns = () => {
|
|||||||
{
|
{
|
||||||
label: 'Type',
|
label: 'Type',
|
||||||
key: 'assessment_type',
|
key: 'assessment_type',
|
||||||
width: '10rem',
|
width: '15rem',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
if (!user.data?.is_moderator) {
|
if (!user.data?.is_moderator) {
|
||||||
columns.push({
|
columns.push({
|
||||||
label: 'Status/Score',
|
label: 'Status/Percentage',
|
||||||
key: 'status',
|
key: 'status',
|
||||||
align: 'center',
|
align: 'left',
|
||||||
width: '10rem',
|
width: '10rem',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
v-if="assignment.data"
|
v-if="assignment.data"
|
||||||
class="grid grid-cols-[70%,30%] h-full"
|
class="grid grid-cols-[68%,32%] h-full"
|
||||||
:class="{ 'border rounded-lg': !showTitle }"
|
:class="{ 'border rounded-lg': !showTitle }"
|
||||||
>
|
>
|
||||||
<div class="border-r p-5 overflow-y-auto h-[calc(100vh-3.2rem)]">
|
<div class="border-r p-5 overflow-y-auto h-[calc(100vh-3.2rem)]">
|
||||||
<div v-if="showTitle">
|
<div v-if="showTitle" class="text-lg font-semibold mb-5">
|
||||||
<div v-if="submissionName === 'new'" class="text-lg font-semibold mb-4">
|
<div v-if="submissionName === 'new'">
|
||||||
{{ __('Submission by') }} {{ user.data?.full_name }}
|
{{ __('Submission by') }} {{ user.data?.full_name }}
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="text-lg font-semibold mb-4">
|
<div v-else>
|
||||||
{{ __('Submission by') }} {{ submissionResource.doc.member_name }}
|
{{ __('Submission by') }} {{ submissionResource.doc?.member_name }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="font-semibold mb-2">
|
<div class="text-sm text-gray-600 font-medium mb-2">
|
||||||
{{ __('Question') }}
|
{{ __('Question') }}:
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-html="assignment.data.question"
|
v-html="assignment.data.question"
|
||||||
@@ -24,22 +24,20 @@
|
|||||||
|
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<div class="p-5">
|
<div class="p-5">
|
||||||
<div class="flex items-center justify-between mb-5">
|
<div class="flex items-center justify-between mb-4">
|
||||||
<div class="font-semibold">
|
<div class="font-semibold">
|
||||||
{{ __('Submission') }}
|
{{ __('Submission') }}
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center space-x-2">
|
<div class="flex items-center space-x-2">
|
||||||
<Badge v-if="isDirty()" theme="orange">
|
<Badge v-if="isDirty" theme="orange">
|
||||||
{{ __('Not Saved') }}
|
{{ __('Not Saved') }}
|
||||||
</Badge>
|
</Badge>
|
||||||
<Badge
|
<Badge
|
||||||
v-else-if="
|
v-else-if="submissionResource.doc?.status"
|
||||||
submissionResource.doc?.status || !submissionResource.doc
|
|
||||||
"
|
|
||||||
:theme="statusTheme"
|
:theme="statusTheme"
|
||||||
size="lg"
|
size="lg"
|
||||||
>
|
>
|
||||||
{{ submissionResource.doc?.status || 'Not Saved' }}
|
{{ submissionResource.doc?.status }}
|
||||||
</Badge>
|
</Badge>
|
||||||
<Button variant="solid" @click="submitAssignment()">
|
<Button variant="solid" @click="submitAssignment()">
|
||||||
{{ __('Save') }}
|
{{ __('Save') }}
|
||||||
@@ -52,7 +50,7 @@
|
|||||||
!['Pass', 'Fail'].includes(submissionResource.doc?.status) &&
|
!['Pass', 'Fail'].includes(submissionResource.doc?.status) &&
|
||||||
submissionResource.doc?.owner == user.data?.name
|
submissionResource.doc?.owner == user.data?.name
|
||||||
"
|
"
|
||||||
class="bg-blue-100 p-2 rounded-md leading-5 text-sm mb-4"
|
class="bg-blue-100 p-3 rounded-md leading-5 text-sm mb-4"
|
||||||
>
|
>
|
||||||
{{ __("You've successfully submitted the assignment.") }}
|
{{ __("You've successfully submitted the assignment.") }}
|
||||||
{{
|
{{
|
||||||
@@ -63,7 +61,7 @@
|
|||||||
{{ __('Feel free to make edits to your submission if needed.') }}
|
{{ __('Feel free to make edits to your submission if needed.') }}
|
||||||
</div>
|
</div>
|
||||||
<div v-if="showUploader()">
|
<div v-if="showUploader()">
|
||||||
<div class="text-xs text-gray-600 mt-1 mb-1">
|
<div class="text-xs text-gray-600 mt-1 mb-2">
|
||||||
{{ __('Add your assignment as {0}').format(assignment.data.type) }}
|
{{ __('Add your assignment as {0}').format(assignment.data.type) }}
|
||||||
</div>
|
</div>
|
||||||
<FileUploader
|
<FileUploader
|
||||||
@@ -100,10 +98,7 @@
|
|||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
<X
|
<X
|
||||||
v-if="
|
v-if="canModifyAssignment"
|
||||||
!submissionResource.doc ||
|
|
||||||
submissionResource.doc?.owner == user.data?.name
|
|
||||||
"
|
|
||||||
@click="removeSubmission()"
|
@click="removeSubmission()"
|
||||||
class="bg-gray-200 rounded-md cursor-pointer stroke-1.5 w-5 h-5 p-1 ml-4"
|
class="bg-gray-200 rounded-md cursor-pointer stroke-1.5 w-5 h-5 p-1 ml-4"
|
||||||
/>
|
/>
|
||||||
@@ -114,7 +109,11 @@
|
|||||||
<div class="text-xs text-gray-600 mb-1">
|
<div class="text-xs text-gray-600 mb-1">
|
||||||
{{ __('Enter a URL') }}
|
{{ __('Enter a URL') }}
|
||||||
</div>
|
</div>
|
||||||
<FormControl v-model="answer" />
|
<FormControl
|
||||||
|
v-model="answer"
|
||||||
|
type="text"
|
||||||
|
:readonly="!canModifyAssignment"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<div class="text-sm mb-4">
|
<div class="text-sm mb-4">
|
||||||
@@ -129,6 +128,21 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="
|
||||||
|
user.data?.name == submissionResource.doc?.owner &&
|
||||||
|
submissionResource.doc?.comments
|
||||||
|
"
|
||||||
|
class="mt-8 p-3 bg-blue-100 rounded-md"
|
||||||
|
>
|
||||||
|
<div class="text-sm text-gray-600 font-medium mb-2">
|
||||||
|
{{ __('Comments by Evaluator') }}:
|
||||||
|
</div>
|
||||||
|
<div class="leading-5">
|
||||||
|
{{ submissionResource.doc.comments }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Grading -->
|
<!-- Grading -->
|
||||||
<div v-if="canGradeSubmission" class="mt-8 space-y-4">
|
<div v-if="canGradeSubmission" class="mt-8 space-y-4">
|
||||||
<div class="font-semibold mb-2">
|
<div class="font-semibold mb-2">
|
||||||
@@ -173,6 +187,7 @@ const answer = ref(null)
|
|||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const user = inject('$user')
|
const user = inject('$user')
|
||||||
const showTitle = router.currentRoute.value.name == 'AssignmentSubmission'
|
const showTitle = router.currentRoute.value.name == 'AssignmentSubmission'
|
||||||
|
const isDirty = ref(false)
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
assignmentID: {
|
assignmentID: {
|
||||||
@@ -249,7 +264,11 @@ const imageResource = createResource({
|
|||||||
const submissionResource = createDocumentResource({
|
const submissionResource = createDocumentResource({
|
||||||
doctype: 'LMS Assignment Submission',
|
doctype: 'LMS Assignment Submission',
|
||||||
name: props.submissionName,
|
name: props.submissionName,
|
||||||
|
onError(err) {
|
||||||
|
showToast(__('Error'), __(err.messages?.[0] || err), 'x')
|
||||||
|
},
|
||||||
auto: false,
|
auto: false,
|
||||||
|
cache: [user.data?.name, props.assignmentID],
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(submissionResource, () => {
|
watch(submissionResource, () => {
|
||||||
@@ -262,6 +281,22 @@ watch(submissionResource, () => {
|
|||||||
if (submissionResource.doc.answer) {
|
if (submissionResource.doc.answer) {
|
||||||
answer.value = submissionResource.doc.answer
|
answer.value = submissionResource.doc.answer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (submissionResource.isDirty) {
|
||||||
|
isDirty.value = true
|
||||||
|
} else if (showUploader() && !submissionFile.value) {
|
||||||
|
isDirty.value = true
|
||||||
|
} else if (!showUploader() && !answer.value) {
|
||||||
|
isDirty.value = true
|
||||||
|
} else {
|
||||||
|
isDirty.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(submissionFile, () => {
|
||||||
|
if (props.submissionName == 'new' && submissionFile.value) {
|
||||||
|
isDirty.value = true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -271,10 +306,17 @@ const submitAssignment = () => {
|
|||||||
submissionResource.doc && submissionResource.doc.owner != user.data?.name
|
submissionResource.doc && submissionResource.doc.owner != user.data?.name
|
||||||
? user.data?.name
|
? user.data?.name
|
||||||
: null
|
: null
|
||||||
submissionResource.setValue.submit({
|
submissionResource.setValue.submit(
|
||||||
...submissionResource.doc,
|
{
|
||||||
evaluator: evaluator,
|
...submissionResource.doc,
|
||||||
})
|
evaluator: evaluator,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onSuccess(data) {
|
||||||
|
showToast(__('Success'), __('Changes saved successfully'), 'check')
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
addNewSubmission()
|
addNewSubmission()
|
||||||
}
|
}
|
||||||
@@ -298,6 +340,8 @@ const addNewSubmission = () => {
|
|||||||
markLessonProgress()
|
markLessonProgress()
|
||||||
router.go()
|
router.go()
|
||||||
}
|
}
|
||||||
|
submissionResource.name = data.name
|
||||||
|
submissionResource.reload()
|
||||||
},
|
},
|
||||||
onError(err) {
|
onError(err) {
|
||||||
showToast('Error', err.messages?.[0] || err, 'x')
|
showToast('Error', err.messages?.[0] || err, 'x')
|
||||||
@@ -370,6 +414,14 @@ const canGradeSubmission = computed(() => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const canModifyAssignment = computed(() => {
|
||||||
|
return (
|
||||||
|
!submissionResource.doc ||
|
||||||
|
(submissionResource.doc?.owner == user.data?.name &&
|
||||||
|
submissionResource.doc?.status == 'Not Graded')
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
const submissionStatusOptions = computed(() => {
|
const submissionStatusOptions = computed(() => {
|
||||||
return [
|
return [
|
||||||
{ label: 'Not Graded', value: 'Not Graded' },
|
{ label: 'Not Graded', value: 'Not Graded' },
|
||||||
@@ -393,15 +445,4 @@ const statusTheme = computed(() => {
|
|||||||
const showUploader = () => {
|
const showUploader = () => {
|
||||||
return ['PDF', 'Image', 'Document'].includes(assignment.data?.type)
|
return ['PDF', 'Image', 'Document'].includes(assignment.data?.type)
|
||||||
}
|
}
|
||||||
|
|
||||||
const isDirty = () => {
|
|
||||||
if (submissionResource.doc) {
|
|
||||||
if (showUploader() && !submissionFile.value) {
|
|
||||||
return true
|
|
||||||
} else if (!showUploader() && !answer.value) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -201,7 +201,7 @@ const getStatusTheme = (status) => {
|
|||||||
if (status === 'Pass') {
|
if (status === 'Pass') {
|
||||||
return 'green'
|
return 'green'
|
||||||
} else if (status === 'Not Graded') {
|
} else if (status === 'Not Graded') {
|
||||||
return 'orange'
|
return 'blue'
|
||||||
} else {
|
} else {
|
||||||
return 'red'
|
return 'red'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,7 +89,7 @@
|
|||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
<div class="p-5">
|
<div class="p-5">
|
||||||
<div class="text-2xl font-semibold mb-2">
|
<div class="text-xl font-semibold mb-2">
|
||||||
{{ batch.data.title }}
|
{{ batch.data.title }}
|
||||||
</div>
|
</div>
|
||||||
<div v-html="batch.data.description" class="leading-5 mb-2"></div>
|
<div v-html="batch.data.description" class="leading-5 mb-2"></div>
|
||||||
|
|||||||
@@ -169,6 +169,7 @@ def get_user_info():
|
|||||||
user.is_instructor = "Course Creator" in user.roles
|
user.is_instructor = "Course Creator" in user.roles
|
||||||
user.is_moderator = "Moderator" in user.roles
|
user.is_moderator = "Moderator" in user.roles
|
||||||
user.is_evaluator = "Batch Evaluator" in user.roles
|
user.is_evaluator = "Batch Evaluator" in user.roles
|
||||||
|
user.is_student = "LMS Student" in user.roles
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1421,7 +1421,7 @@ def get_quiz_details(assessment, member):
|
|||||||
if len(existing_submission):
|
if len(existing_submission):
|
||||||
assessment.submission = existing_submission[0]
|
assessment.submission = existing_submission[0]
|
||||||
assessment.completed = True
|
assessment.completed = True
|
||||||
assessment.status = assessment.submission.score
|
assessment.status = assessment.submission.percentage or assessment.submission.score
|
||||||
else:
|
else:
|
||||||
assessment.status = "Not Attempted"
|
assessment.status = "Not Attempted"
|
||||||
assessment.color = "red"
|
assessment.color = "red"
|
||||||
|
|||||||
Reference in New Issue
Block a user