Merged in bugfix/VBV-470-abgabe-aufträge-homogenisierung (pull request #166)

Bugfix/VBV-470 Homogenisierung Abgabe Aufträge

* Make feedback non-checkable without submission

* Submit preparation_assignment on close

# Conflicts:
#	client/src/pages/learningPath/learningContentPage/assignment/AssignmentView.vue

* Also submit reflection on close

* Fix CourseSessionCompletionData reloading

* User can self toggle LearningContentFeedback completion

* Show submission view for all assignments

* Fix cockpit for condition acceptance

* Close assignments after submission


Approved-by: Dario Aebersold
Approved-by: Daniel Egger
This commit is contained in:
Elia Bieri 2023-08-03 06:32:47 +00:00
parent ed1fce2bd6
commit 667ef96b14
7 changed files with 75 additions and 64 deletions

View File

@ -103,7 +103,7 @@ const sendFeedback = () => {
preparation_task_clarity: preparationTaskClarity,
course_negative_feedback: courseNegativeFeedback,
course_positive_feedback: coursePositiveFeedback,
goald_attainment: goalAttainment,
goal_attainment: goalAttainment,
instructor_competence: instructorCompetence,
instructor_respect: instructorRespect,
instructor_open_feedback: instructorOpenFeedback,
@ -119,9 +119,8 @@ const sendFeedback = () => {
});
log.debug(variables);
executeMutation(variables)
.then(({ data, error }) => {
.then(({ data }) => {
log.debug(data);
log.error(error);
mutationResult.value = data;
})
.catch((e) => log.error(e));
@ -142,6 +141,7 @@ const sendFeedback = () => {
:start-badge-text="$t('general.introduction')"
:end-badge-text="$t('general.submission')"
:base-url="props.page.frontend_url"
close-button-variant="close"
@previous="previousStep()"
@next="nextStep()"
>

View File

@ -83,7 +83,9 @@ const totalCount = (status: StatusCount) => {
</div>
<div
v-else-if="
props.learningContentAssignment.assignment_type === 'PREP_ASSIGNMENT'
props.learningContentAssignment.assignment_type === 'PREP_ASSIGNMENT' ||
props.learningContentAssignment.assignment_type === 'CONDITION_ACCEPTANCE' ||
props.learningContentAssignment.assignment_type === 'REFLECTION'
"
>
{{
@ -93,14 +95,6 @@ const totalCount = (status: StatusCount) => {
})
}}
</div>
<div v-else-if="props.learningContentAssignment.assignment_type === 'REFLECTION'">
{{
$t("x von y abgeschlossen", {
x: doneCount(state.submissionProgressStatusCount),
y: totalCount(state.submissionProgressStatusCount),
})
}}
</div>
</div>
</div>
</template>

View File

@ -15,6 +15,7 @@ import type { Dayjs } from "dayjs";
import log from "loglevel";
import { computed, reactive } from "vue";
import { useTranslation } from "i18next-vue";
import eventBus from "@/utils/eventBus";
const props = defineProps<{
assignment: Assignment;
@ -53,6 +54,15 @@ const completionData = computed(() => {
return props.assignmentCompletion?.completion_data ?? {};
});
const canSubmit = computed(() => {
return (
!state.confirmInput ||
(props.assignment.assignment_type === "CASEWORK" && !state.confirmPerson)
);
});
const isCasework = computed(() => props.assignment.assignment_type === "CASEWORK");
const upsertAssignmentCompletionMutation = useMutation(
UPSERT_ASSIGNMENT_COMPLETION_MUTATION
);
@ -76,6 +86,7 @@ const onSubmit = async () => {
bustItGetCache(
`/api/course/completion/${courseSession.value.id}/${useUserStore().id}/`
);
eventBus.emit("finishedLearningContent", true);
} catch (error) {
log.error("Could not submit assignment", error);
}
@ -98,7 +109,7 @@ const onSubmit = async () => {
data-cy="confirm-submit-results"
@toggle="state.confirmInput = !state.confirmInput"
></ItCheckbox>
<div class="w-full border-b border-gray-400">
<div v-if="isCasework" class="w-full border-b border-gray-400">
<ItCheckbox
class="py-6"
:checkbox-item="{
@ -121,21 +132,21 @@ const onSubmit = async () => {
</div>
<!-- TODO: find way to find user that will do the corrections -->
</div>
<div class="flex flex-col space-x-2 pt-6 text-base sm:flex-row">
<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 class="pt-6">
<p v-if="isCasework" class="pt-6">
{{ $t("assignment.dueDateSubmission") }}
<DateEmbedding :single-date="dueDate"></DateEmbedding>
</p>
<ItButton
class="mt-6"
variant="primary"
variant="blue"
size="large"
:disabled="!state.confirmInput || !state.confirmPerson"
:disabled="canSubmit"
data-cy="submit-assignment"
@click="onSubmit"
>
@ -147,7 +158,7 @@ const onSubmit = async () => {
:text="t('assignment.assignmentSubmitted')"
data-cy="success-text"
></ItSuccessAlert>
<p class="pt-6">
<p v-if="isCasework" class="pt-6">
{{
$t("assignment.submissionNotificationDisclaimer", { name: circleExpertName })
}}

View File

@ -12,6 +12,7 @@ import { useUserStore } from "@/stores/user";
import type {
Assignment,
AssignmentCompletion,
AssignmentCompletionStatus,
AssignmentTask,
CourseSessionAssignment,
CourseSessionUser,
@ -23,6 +24,7 @@ import dayjs from "dayjs";
import * as log from "loglevel";
import { computed, onMounted, reactive } from "vue";
import { useTranslation } from "i18next-vue";
import { bustItGetCache } from "@/fetchHelpers";
import { learningContentTypeData } from "@/utils/typeMaps";
const { t } = useTranslation();
@ -87,16 +89,7 @@ onMounted(async () => {
// create initial `AssignmentCompletion` first, so that it exists and we don't
// have reactivity problem accessing it.
await upsertAssignmentCompletionMutation.executeMutation({
assignmentId: props.learningContent.content_assignment_id.toString(),
courseSessionId: courseSession.value.id.toString(),
learningContentId: props.learningContent.id.toString(),
completionDataString: JSON.stringify({}),
completionStatus: "IN_PROGRESS",
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
id: assignmentCompletion.value?.id,
});
await upsertAssignmentCompletion("IN_PROGRESS");
queryResult.resume();
try {
@ -113,16 +106,10 @@ onMounted(async () => {
const numTasks = computed(() => assignment.value?.tasks?.length ?? 0);
const numPages = computed(() => {
if (assignmentType.value === "CASEWORK") {
// casework has extra submission page
return numTasks.value + 2;
}
return numTasks.value + 1;
});
const showPreviousButton = computed(() => stepIndex.value != 0);
const showNextButton = computed(() => stepIndex.value + 1 < numPages.value);
const showExitButton = computed(() => numPages.value === stepIndex.value + 1);
const dueDate = computed(() =>
dayjs(state.courseSessionAssignment?.submission_deadline_start)
);
@ -133,6 +120,26 @@ const currentTask = computed(() => {
return undefined;
});
const upsertAssignmentCompletion = async (status: AssignmentCompletionStatus) => {
try {
await upsertAssignmentCompletionMutation.executeMutation({
assignmentId: props.learningContent.content_assignment_id.toString(),
courseSessionId: courseSession.value.id.toString(),
learningContentId: props.learningContent.id.toString(),
completionDataString: JSON.stringify({}),
completionStatus: status,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
id: assignmentCompletion.value?.id,
});
bustItGetCache(
`/api/course/completion/${courseSession.value.id}/${useUserStore().id}/`
);
} catch (error) {
log.error("Could not submit assignment", error);
}
};
const handleBack = () => {
log.debug("handleBack");
if (stepIndex.value > 0) {
@ -161,19 +168,12 @@ const jumpToTask = (task: AssignmentTask) => {
const getTitle = () => {
if (0 === stepIndex.value) {
return t("general.introduction");
} else if (
assignmentType.value === "CASEWORK" &&
stepIndex.value === numPages.value - 1
) {
} else if (stepIndex.value === numPages.value - 1) {
return t("general.submission");
}
return currentTask?.value?.value.title ?? "Unknown";
};
const assignmentType = computed(() => {
return assignment.value?.assignment_type ?? "CASEWORK";
});
const subTitle = computed(() => {
if (assignment.value) {
const prefix = learningContentTypeData(props.learningContent).title;
@ -187,19 +187,6 @@ const assignmentUser = computed(() => {
(user) => user.user_id === userStore.id
) as CourseSessionUser;
});
const endBadgeText = computed(() => {
if (assignmentType.value === "PREP_ASSIGNMENT") {
return t("Aufgaben");
} else if (assignmentType.value === "CASEWORK") {
return t("Abgabe");
} else if (assignmentType.value === "CONDITION_ACCEPTANCE") {
return t("Akzeptieren");
}
// just return the number of tasks as default
return (assignment.value?.tasks.length ?? 0).toString();
});
</script>
<template>
@ -215,13 +202,13 @@ const endBadgeText = computed(() => {
:learning-content="props.learningContent"
:steps-count="numPages"
:show-next-button="showNextButton && stepIndex !== 0"
:show-exit-button="showExitButton"
:show-exit-button="false"
:show-start-button="showNextButton && stepIndex === 0"
:show-previous-button="showPreviousButton"
:base-url="props.learningContent.frontend_url"
step-query-param="step"
start-badge-text="Einleitung"
:end-badge-text="endBadgeText"
:end-badge-text="$t('Abgabe')"
close-button-variant="close"
@previous="handleBack()"
@next="handleContinue()"
@ -241,7 +228,7 @@ const endBadgeText = computed(() => {
:learning-content-id="props.learningContent.id"
></AssignmentTaskView>
<AssignmentSubmissionView
v-else-if="assignmentType === 'CASEWORK' && stepIndex + 1 === numPages"
v-else-if="stepIndex + 1 === numPages"
:due-date="dueDate"
:assignment="assignment"
:assignment-completion="assignmentCompletion"

View File

@ -1,5 +1,4 @@
<script setup lang="ts">
import eventBus from "@/utils/eventBus";
import { computed } from "vue";
import { useTranslation } from "i18next-vue";
@ -15,6 +14,8 @@ const props = defineProps<{
const { t } = useTranslation();
const emit = defineEmits(["start", "previous", "next", "exit"]);
// eslint-disable-next-line vue/return-in-computed-property
const closingButtonText = computed(() => {
switch (props.closingButtonVariant) {
@ -24,8 +25,6 @@ const closingButtonText = computed(() => {
return t("learningContent.markAsDone");
}
});
defineEmits(["start", "previous", "next"]);
</script>
<template>
@ -64,7 +63,7 @@ defineEmits(["start", "previous", "next"]);
type="button"
class="btn-blue z-10 flex items-center"
data-cy="complete-and-continue"
@click="eventBus.emit('finishedLearningContent', true)"
@click="emit('exit')"
>
{{ closingButtonText }}
<it-icon-check

View File

@ -6,6 +6,7 @@ import LearningContentFooter from "@/pages/learningPath/learningContentPage/layo
import type { LearningContent } from "@/types";
import { learningContentTypeData } from "@/utils/typeMaps";
import { computed } from "vue";
import eventBus from "@/utils/eventBus";
interface Props {
title?: string;
@ -23,6 +24,7 @@ interface Props {
closeButtonVariant?: ClosingButtonVariant;
baseUrl?: string;
stepQueryParam?: string;
beforeExitCallback?: () => Promise<void>;
}
const props = withDefaults(defineProps<Props>(), {
@ -35,6 +37,7 @@ const props = withDefaults(defineProps<Props>(), {
closeButtonVariant: "mark_as_done",
baseUrl: undefined,
stepQueryParam: undefined,
beforeExitCallback: async () => Promise.resolve(),
});
const subTitle = computed(() => {
@ -57,6 +60,11 @@ const icon = computed(() => {
return "";
});
const onExit = async () => {
await props.beforeExitCallback();
eventBus.emit("finishedLearningContent", true);
};
const emit = defineEmits(["previous", "next", "exit"]);
</script>
@ -98,5 +106,6 @@ const emit = defineEmits(["previous", "next", "exit"]);
@previous="emit('previous')"
@next="emit('next')"
@start="emit('next')"
@exit="onExit"
></LearningContentFooter>
</template>

View File

@ -4,6 +4,7 @@ import LearningContentFooter from "@/pages/learningPath/learningContentPage/layo
import type { LearningContent } from "@/types";
import { learningContentTypeData } from "@/utils/typeMaps";
import { computed } from "vue";
import eventBus from "@/utils/eventBus";
export interface Props {
title?: string;
@ -38,6 +39,15 @@ const icon = computed(() => {
}
return "";
});
const closingButtonVariant = computed(() => {
if (props.learningContent) {
return props.learningContent.can_user_self_toggle_course_completion
? "mark_as_done"
: "close";
}
return "close";
});
</script>
<template>
@ -62,6 +72,7 @@ const icon = computed(() => {
:show-previous-button="false"
:show-exit-button="true"
:show-start-button="false"
:closing-button-variant="'close'"
:closing-button-variant="closingButtonVariant"
@exit="eventBus.emit('finishedLearningContent', true)"
></LearningContentFooter>
</template>