feat: split assigment submit

This commit is contained in:
Reto Aebersold 2023-12-14 09:21:17 +01:00
parent acd9c3959b
commit 1979dcd428
4 changed files with 327 additions and 120 deletions

View File

@ -0,0 +1,124 @@
<script setup lang="ts">
import ItCheckbox from "@/components/ui/ItCheckbox.vue";
import { computed, reactive } from "vue";
import type { CourseSessionUserObjectsType } from "@/gql/graphql";
import dayjs from "dayjs";
import DateEmbedding from "@/components/dueDates/DateEmbedding.vue";
import ItButton from "@/components/ui/ItButton.vue";
import { useMutation } from "@urql/vue";
import { UPSERT_ASSIGNMENT_COMPLETION_MUTATION } from "@/graphql/mutations";
import { useUserStore } from "@/stores/user";
import { bustItGetCache } from "@/fetchHelpers";
import eventBus from "@/utils/eventBus";
import log from "loglevel";
import type { Assignment } from "@/types";
const props = defineProps<{
evaluationDocumentUrl: string;
submissionDeadlineStart?: string | null;
circleExpert?: CourseSessionUserObjectsType;
courseSessionId: string;
assignment: Assignment;
learningContentId: string;
}>();
const state = reactive({
confirmInput: false,
confirmPerson: false,
});
const circleExpertName = computed(() => {
return `${props.circleExpert?.first_name} ${props.circleExpert?.last_name}`;
});
const cannotSubmit = computed(() => {
return !state.confirmInput || !state.confirmPerson;
});
const upsertAssignmentCompletionMutation = useMutation(
UPSERT_ASSIGNMENT_COMPLETION_MUTATION
);
const onSubmit = async () => {
try {
await upsertAssignmentCompletionMutation.executeMutation({
assignmentId: props.assignment.id,
courseSessionId: props.courseSessionId,
learningContentId: props.learningContentId,
completionDataString: JSON.stringify({}),
completionStatus: "SUBMITTED",
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
id: props.assignmentCompletion?.id,
});
bustItGetCache(
`/api/course/completion/${props.courseSessionId}/${useUserStore().id}/`
);
// if solution sample is available, do not close the assigment automatically
if (!props.assignment.solution_sample) {
eventBus.emit("finishedLearningContent", true);
}
} catch (error) {
log.error("Could not submit assignment", error);
}
};
</script>
<template>
<div>
<ItCheckbox
class="w-full border-b border-gray-400 py-10 sm:py-6"
:checkbox-item="{
label: $t('assignment.confirmSubmitResults'),
value: 'value',
checked: state.confirmInput,
}"
data-cy="confirm-submit-results"
@toggle="state.confirmInput = !state.confirmInput"
></ItCheckbox>
<div class="w-full border-b border-gray-400">
<ItCheckbox
class="py-6"
:checkbox-item="{
label: $t('assignment.confirmSubmitPerson'),
value: 'value',
checked: state.confirmPerson,
}"
data-cy="confirm-submit-person"
@toggle="state.confirmPerson = !state.confirmPerson"
></ItCheckbox>
<div v-if="circleExpert" class="flex flex-row items-center pb-6 pl-[49px]">
<img
alt="Notification icon"
class="mr-2 h-[45px] min-w-[45px] rounded-full"
:src="circleExpert.avatar_url"
/>
<p class="text-base font-bold">
{{ circleExpertName }}
</p>
</div>
</div>
<div class="flex flex-col space-x-2 pt-6 text-base sm:flex-row">
<p>{{ $t("assignment.assessmentDocumentDisclaimer") }}</p>
<a :href="evaluationDocumentUrl" class="underline">
{{ $t("assignment.showAssessmentDocument") }}
</a>
</div>
<p v-if="submissionDeadlineStart" class="pt-6">
{{ $t("assignment.dueDateSubmission") }}
<DateEmbedding
:single-date="dayjs(props.submissionDeadlineStart)"
></DateEmbedding>
</p>
<ItButton
class="mt-6"
variant="blue"
size="large"
:disabled="cannotSubmit"
data-cy="submit-assignment"
@click="onSubmit"
>
<p>{{ $t("assignment.submitAssignment") }}</p>
</ItButton>
</div>
</template>

View File

@ -0,0 +1,101 @@
<script setup lang="ts">
import ItButton from "@/components/ui/ItButton.vue";
import ItCheckbox from "@/components/ui/ItCheckbox.vue";
import { computed, ref } from "vue";
import { bustItGetCache, useCSRFFetch } from "@/fetchHelpers";
import { useUserStore } from "@/stores/user";
import eventBus from "@/utils/eventBus";
import log from "loglevel";
import dayjs from "dayjs";
import { useMutation } from "@urql/vue";
import { UPSERT_ASSIGNMENT_COMPLETION_MUTATION } from "@/graphql/mutations";
import type { CourseSessionUserObjectsType } from "@/gql/graphql";
import type { Assignment } from "@/types";
const props = defineProps<{
submissionDeadlineStart?: string | null;
circleExpert?: CourseSessionUserObjectsType;
courseSessionId: string;
assignment: Assignment;
learningContentId: string;
}>();
const confirmPerson = ref(false);
const upsertAssignmentCompletionMutation = useMutation(
UPSERT_ASSIGNMENT_COMPLETION_MUTATION
);
const circleExpertName = computed(() => {
return `${props.circleExpert?.first_name} ${props.circleExpert?.last_name}`;
});
const { data: learningMentors } = useCSRFFetch(
`/api/mentor/${props.courseSessionId}/mentors`
).json();
const onSubmit = async () => {
try {
await upsertAssignmentCompletionMutation.executeMutation({
assignmentId: props.assignment.id,
courseSessionId: props.courseSessionId,
learningContentId: props.learningContentId,
completionDataString: JSON.stringify({}),
completionStatus: "SUBMITTED",
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
id: props.assignmentCompletion?.id,
});
bustItGetCache(
`/api/course/completion/${props.courseSessionId}/${useUserStore().id}/`
);
// if solution sample is available, do not close the assigment automatically
if (!props.assignment.solution_sample) {
eventBus.emit("finishedLearningContent", true);
}
} catch (error) {
log.error("Could not submit assignment", error);
}
};
</script>
<template>
<div class="w-full border-b border-gray-400">
<ItCheckbox
class="py-6"
:checkbox-item="{
label: $t('a.confirmSubmitPersonPraxisAssignment'),
value: 'value',
checked: confirmPerson,
}"
data-cy="confirm-submit-person"
@toggle="confirmPerson = !confirmPerson"
></ItCheckbox>
<div v-if="circleExpert" class="flex flex-row items-center pb-6 pl-[49px]">
<img
alt="Notification icon"
class="mr-2 h-[45px] min-w-[45px] rounded-full"
:src="circleExpert.avatar_url"
/>
<p class="text-base font-bold">
{{ circleExpertName }}
</p>
</div>
{{ learningMentors }}
</div>
<p v-if="props.submissionDeadlineStart" class="pt-6">
{{ $t("assignment.dueDateSubmission") }}
<DateEmbedding :single-date="dayjs(props.submissionDeadlineStart)"></DateEmbedding>
</p>
<ItButton
class="mt-6"
variant="blue"
size="large"
:disabled="!confirmPerson"
data-cy="submit-assignment"
@click="onSubmit"
>
<p>{{ $t("assignment.submitAssignment") }}</p>
</ItButton>
</template>

View File

@ -0,0 +1,71 @@
<script setup lang="ts">
import ItButton from "@/components/ui/ItButton.vue";
import ItCheckbox from "@/components/ui/ItCheckbox.vue";
import { ref } from "vue";
import { bustItGetCache } from "@/fetchHelpers";
import { useUserStore } from "@/stores/user";
import eventBus from "@/utils/eventBus";
import log from "loglevel";
import { useMutation } from "@urql/vue";
import { UPSERT_ASSIGNMENT_COMPLETION_MUTATION } from "@/graphql/mutations";
import type { Assignment } from "@/types";
const props = defineProps<{
courseSessionId: string;
assignment: Assignment;
learningContentId: string;
}>();
const upsertAssignmentCompletionMutation = useMutation(
UPSERT_ASSIGNMENT_COMPLETION_MUTATION
);
const confirmInput = ref(false);
const onSubmit = async () => {
try {
await upsertAssignmentCompletionMutation.executeMutation({
assignmentId: props.assignment.id,
courseSessionId: props.courseSessionId,
learningContentId: props.learningContentId,
completionDataString: JSON.stringify({}),
completionStatus: "SUBMITTED",
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
id: props.assignmentCompletion?.id,
});
bustItGetCache(
`/api/course/completion/${props.courseSessionId}/${useUserStore().id}/`
);
// if solution sample is available, do not close the assigment automatically
if (!props.assignment.solution_sample) {
eventBus.emit("finishedLearningContent", true);
}
} catch (error) {
log.error("Could not submit assignment", error);
}
};
</script>
<template>
<ItCheckbox
class="w-full border-b border-gray-400 py-10 sm:py-6"
:checkbox-item="{
label: $t('assignment.confirmSubmitResults'),
value: 'value',
checked: confirmInput,
}"
data-cy="confirm-submit-results"
@toggle="confirmInput = !confirmInput"
></ItCheckbox>
<ItButton
class="mt-6"
variant="blue"
size="large"
:disabled="!confirmInput"
data-cy="submit-assignment"
@click="onSubmit"
>
<p>{{ $t("assignment.submitAssignment") }}</p>
</ItButton>
</template>

View File

@ -1,25 +1,19 @@
<script setup lang="ts"> <script setup lang="ts">
import DateEmbedding from "@/components/dueDates/DateEmbedding.vue";
import ItButton from "@/components/ui/ItButton.vue"; import ItButton from "@/components/ui/ItButton.vue";
import ItCheckbox from "@/components/ui/ItCheckbox.vue";
import ItSuccessAlert from "@/components/ui/ItSuccessAlert.vue"; import ItSuccessAlert from "@/components/ui/ItSuccessAlert.vue";
import { import {
useCourseData, useCourseData,
useCourseSessionDetailQuery, useCourseSessionDetailQuery,
useCurrentCourseSession, useCurrentCourseSession,
} from "@/composables"; } from "@/composables";
import { bustItGetCache, useCSRFFetch } from "@/fetchHelpers";
import { UPSERT_ASSIGNMENT_COMPLETION_MUTATION } from "@/graphql/mutations";
import AssignmentSubmissionResponses from "@/pages/learningPath/learningContentPage/assignment/AssignmentSubmissionResponses.vue"; import AssignmentSubmissionResponses from "@/pages/learningPath/learningContentPage/assignment/AssignmentSubmissionResponses.vue";
import { useUserStore } from "@/stores/user";
import type { Assignment, AssignmentCompletion, AssignmentTask } from "@/types"; import type { Assignment, AssignmentCompletion, AssignmentTask } from "@/types";
import { useMutation } from "@urql/vue"; import { computed } from "vue";
import log from "loglevel";
import { computed, reactive } from "vue";
import { useTranslation } from "i18next-vue"; import { useTranslation } from "i18next-vue";
import eventBus from "@/utils/eventBus";
import dayjs from "dayjs";
import type { AssignmentAssignmentAssignmentTypeChoices } from "@/gql/graphql"; import type { AssignmentAssignmentAssignmentTypeChoices } from "@/gql/graphql";
import CaseWorkSubmit from "@/components/learningPath/assignment/CaseWorkSubmit.vue";
import SimpleSubmit from "@/components/learningPath/assignment/SimpleSubmit.vue";
import PraxisAssignmentSubmit from "@/components/learningPath/assignment/PraxisAssignmentSubmit.vue";
const props = defineProps<{ const props = defineProps<{
assignment: Assignment; assignment: Assignment;
@ -39,11 +33,6 @@ const courseData = useCourseData(courseSession.value.course.slug);
const { t } = useTranslation(); const { t } = useTranslation();
const state = reactive({
confirmInput: false,
confirmPerson: false,
});
const learningContent = computed(() => { const learningContent = computed(() => {
return courseData.findLearningContent(props.learningContentId); return courseData.findLearningContent(props.learningContentId);
}); });
@ -61,10 +50,6 @@ const circleExpert = computed(() => {
return circleExperts.value[0]; return circleExperts.value[0];
}); });
const { data: learningMentors } = useCSRFFetch(
`/api/mentor/${courseSession.value.id}/mentors`
).json();
const circleExpertName = computed(() => { const circleExpertName = computed(() => {
return `${circleExpert.value?.first_name} ${circleExpert.value?.last_name}`; return `${circleExpert.value?.first_name} ${circleExpert.value?.last_name}`;
}); });
@ -81,13 +66,6 @@ const completionTaskData = computed(() => {
return props.assignmentCompletion?.task_completion_data ?? {}; return props.assignmentCompletion?.task_completion_data ?? {};
}); });
const cannotSubmit = computed(() => {
return (
(!state.confirmInput && !isPraxisAssignment.value) ||
(props.assignment.assignment_type === "CASEWORK" && !state.confirmPerson)
);
});
function checkAssignmentType( function checkAssignmentType(
assignmentType: AssignmentAssignmentAssignmentTypeChoices[] assignmentType: AssignmentAssignmentAssignmentTypeChoices[]
) { ) {
@ -95,15 +73,8 @@ function checkAssignmentType(
} }
const isCasework = computed(() => checkAssignmentType(["CASEWORK"])); const isCasework = computed(() => checkAssignmentType(["CASEWORK"]));
const mayBeEvaluated = computed(() =>
checkAssignmentType(["CASEWORK", "PRAXIS_ASSIGNMENT"])
);
const isPraxisAssignment = computed(() => checkAssignmentType(["PRAXIS_ASSIGNMENT"])); const isPraxisAssignment = computed(() => checkAssignmentType(["PRAXIS_ASSIGNMENT"]));
const upsertAssignmentCompletionMutation = useMutation(
UPSERT_ASSIGNMENT_COMPLETION_MUTATION
);
const onEditTask = (task: AssignmentTask) => { const onEditTask = (task: AssignmentTask) => {
emit("editTask", task); emit("editTask", task);
}; };
@ -115,30 +86,6 @@ const openSolutionSample = () => {
window.open(url, "_blank"); window.open(url, "_blank");
} }
}; };
const onSubmit = async () => {
try {
await upsertAssignmentCompletionMutation.executeMutation({
assignmentId: props.assignment.id,
courseSessionId: courseSession.value.id,
learningContentId: props.learningContentId,
completionDataString: JSON.stringify({}),
completionStatus: "SUBMITTED",
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
id: props.assignmentCompletion?.id,
});
bustItGetCache(
`/api/course/completion/${courseSession.value.id}/${useUserStore().id}/`
);
// if solution sample is available, do not close the assigment automatically
if (!props.assignment.solution_sample) {
eventBus.emit("finishedLearningContent", true);
}
} catch (error) {
log.error("Could not submit assignment", error);
}
};
</script> </script>
<template> <template>
<div class="w-full border border-gray-400 p-8" data-cy="confirm-container"> <div class="w-full border border-gray-400 p-8" data-cy="confirm-container">
@ -147,67 +94,31 @@ const onSubmit = async () => {
</h3> </h3>
<div v-if="completionStatus === 'IN_PROGRESS'"> <div v-if="completionStatus === 'IN_PROGRESS'">
<ItCheckbox <CaseWorkSubmit
v-if="!isPraxisAssignment" v-if="isCasework"
class="w-full border-b border-gray-400 py-10 sm:py-6" :course-session-id="courseSessionId"
:checkbox-item="{ :assignment="assignment"
label: $t('assignment.confirmSubmitResults'), :learning-content-id="learningContentId"
value: 'value', :circle-expert="circleExpert"
checked: state.confirmInput, :evaluation-document-url="assignment.evaluation_document_url"
}" :submission-deadline-start="submissionDeadlineStart"
data-cy="confirm-submit-results" />
@toggle="state.confirmInput = !state.confirmInput"
></ItCheckbox> <PraxisAssignmentSubmit
<div v-if="mayBeEvaluated" class="w-full border-b border-gray-400"> v-else-if="isPraxisAssignment"
<ItCheckbox :course-session-id="courseSessionId"
class="py-6" :assignment="assignment"
:checkbox-item="{ :learning-content-id="learningContentId"
label: isPraxisAssignment :circle-expert="circleExpert"
? $t('a.confirmSubmitPersonPraxisAssignment') :submission-deadline-start="submissionDeadlineStart"
: $t('assignment.confirmSubmitPerson'), />
value: 'value',
checked: state.confirmPerson, <SimpleSubmit
}" v-else
data-cy="confirm-submit-person" :course-session-id="courseSessionId"
@toggle="state.confirmPerson = !state.confirmPerson" :assignment="assignment"
></ItCheckbox> :learning-content-id="learningContentId"
<div v-if="circleExpert" class="flex flex-row items-center pb-6 pl-[49px]"> />
<img
alt="Notification icon"
class="mr-2 h-[45px] min-w-[45px] rounded-full"
:src="circleExpert.avatar_url"
/>
<p class="text-base font-bold">
{{ circleExpertName }}
</p>
</div>
<div>
{{ learningMentors }}
</div>
<!-- TODO: find way to find user that will do the corrections -->
</div>
<div v-if="isCasework" class="flex flex-col space-x-2 pt-6 text-base sm:flex-row">
<p>{{ $t("assignment.assessmentDocumentDisclaimer") }}</p>
<a :href="props.assignment.evaluation_document_url" class="underline">
{{ $t("assignment.showAssessmentDocument") }}
</a>
</div>
<p v-if="mayBeEvaluated && props.submissionDeadlineStart" class="pt-6">
{{ $t("assignment.dueDateSubmission") }}
<DateEmbedding
:single-date="dayjs(props.submissionDeadlineStart)"
></DateEmbedding>
</p>
<ItButton
class="mt-6"
variant="blue"
size="large"
:disabled="cannotSubmit"
data-cy="submit-assignment"
@click="onSubmit"
>
<p>{{ $t("assignment.submitAssignment") }}</p>
</ItButton>
</div> </div>
<div v-else class="pt-6"> <div v-else class="pt-6">
<ItSuccessAlert <ItSuccessAlert
@ -220,7 +131,7 @@ const onSubmit = async () => {
}} }}
</p> </p>
<div <div
v-if="props.assignment.solution_sample" v-if="assignment.solution_sample"
class="pt-2" class="pt-2"
data-cy="show-sample-solution" data-cy="show-sample-solution"
> >
@ -242,7 +153,7 @@ const onSubmit = async () => {
</div> </div>
</div> </div>
<AssignmentSubmissionResponses <AssignmentSubmissionResponses
:assignment="props.assignment" :assignment="assignment"
:assignment-completion-data="completionData" :assignment-completion-data="completionData"
:assignment-task-completion-data="completionTaskData" :assignment-task-completion-data="completionTaskData"
:allow-edit="completionStatus === 'IN_PROGRESS'" :allow-edit="completionStatus === 'IN_PROGRESS'"