139 lines
4.1 KiB
Vue
139 lines
4.1 KiB
Vue
<script setup lang="ts">
|
|
import ItCheckbox from "@/components/ui/ItCheckbox.vue";
|
|
import ItTextarea from "@/components/ui/ItTextarea.vue";
|
|
import { useAssignmentStore } from "@/stores/assignmentStore";
|
|
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
|
import type {
|
|
AssignmentCompletionData,
|
|
AssignmentTask,
|
|
UserDataConfirmation,
|
|
UserDataText,
|
|
} from "@/types";
|
|
import { useDebounceFn } from "@vueuse/core";
|
|
import dayjs from "dayjs";
|
|
import { computed, reactive, ref } from "vue";
|
|
|
|
const props = defineProps<{
|
|
assignmentId: number;
|
|
task: AssignmentTask;
|
|
}>();
|
|
|
|
const lastSaved = ref(dayjs());
|
|
const lastSaveUnsuccessful = ref(false);
|
|
|
|
const checkboxState = reactive({} as Record<string, boolean>);
|
|
|
|
const courseSessionStore = useCourseSessionsStore();
|
|
const assignmentStore = useAssignmentStore();
|
|
|
|
async function upsertAssignmentCompletion(completion_data: AssignmentCompletionData) {
|
|
try {
|
|
const courseSessionId = courseSessionStore.currentCourseSession?.id;
|
|
if (!courseSessionId) {
|
|
console.error("Invalid courseSessionId");
|
|
return;
|
|
}
|
|
await assignmentStore.upsertAssignmentCompletion({
|
|
assignment_id: props.assignmentId,
|
|
course_session_id: courseSessionId,
|
|
completion_data: completion_data,
|
|
completion_status: "in_progress",
|
|
});
|
|
lastSaved.value = dayjs();
|
|
lastSaveUnsuccessful.value = false;
|
|
console.debug("Saved user input");
|
|
} catch (error) {
|
|
lastSaveUnsuccessful.value = true;
|
|
console.error("Could not save user input", error);
|
|
}
|
|
}
|
|
|
|
const upsertAssignmentCompletionDebounced = useDebounceFn(
|
|
upsertAssignmentCompletion,
|
|
500
|
|
);
|
|
|
|
const onUpdateText = (id: string, value: string) => {
|
|
const data: AssignmentCompletionData = {};
|
|
data[id] = {
|
|
user_data: {
|
|
text: value,
|
|
} as UserDataText,
|
|
};
|
|
upsertAssignmentCompletionDebounced(data);
|
|
};
|
|
|
|
const onUpdateConfirmation = (id: string, value: boolean) => {
|
|
const data: AssignmentCompletionData = {};
|
|
data[id] = {
|
|
user_data: {
|
|
confirmation: value,
|
|
} as UserDataConfirmation,
|
|
};
|
|
upsertAssignmentCompletion(data);
|
|
};
|
|
|
|
const getBlockData = (id: string) => {
|
|
const userData = assignmentStore.getCompletionDataForUserInput(id)?.user_data;
|
|
if (userData && "text" in userData) {
|
|
return userData.text;
|
|
} else if (userData && "confirmation" in userData) {
|
|
return userData.confirmation;
|
|
}
|
|
return null;
|
|
};
|
|
|
|
const onToggleCheckbox = (id: string) => {
|
|
checkboxState[id] = !checkboxState[id];
|
|
onUpdateConfirmation(id, checkboxState[id]);
|
|
};
|
|
|
|
const completionStatus = computed(() => {
|
|
return assignmentStore.assignmentCompletion?.completion_status ?? "in_progress";
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<div class="flex flex-col space-y-10">
|
|
<div v-for="(block, index) in props.task.value.content" :key="block.id">
|
|
<div v-if="block.type === 'explanation'">
|
|
<p class="default-wagtail-rich-text text-large" v-html="block.value.text"></p>
|
|
</div>
|
|
|
|
<div v-if="block.type === 'user_confirmation'">
|
|
<ItCheckbox
|
|
:checkbox-item="{
|
|
label: block.value.text,
|
|
value: `confirmation-${index}`,
|
|
checked: getBlockData(block.id) as boolean,
|
|
}"
|
|
:disabled="completionStatus !== 'in_progress'"
|
|
@toggle="onToggleCheckbox(block.id)"
|
|
></ItCheckbox>
|
|
</div>
|
|
<div v-if="block.type === 'user_text_input'">
|
|
<p class="text-large pb-4">{{ block.value.text }}</p>
|
|
<ItTextarea
|
|
:model-value="(getBlockData(block.id) as string) ?? ''"
|
|
:cy-key="`user-text-input-${index}`"
|
|
:disabled="completionStatus !== 'in_progress'"
|
|
label=""
|
|
@update:model-value="onUpdateText(block.id, $event)"
|
|
></ItTextarea>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-if="lastSaveUnsuccessful" class="text-red-600">
|
|
{{ $t("assignment.lastChangesNotSaved") }}
|
|
</div>
|
|
</div>
|
|
|
|
<div v-if="props.task.value.file_submission_required">
|
|
<p class="text-large">Datei hochladen</p>
|
|
|
|
<p class="text-sm text-gray-900">
|
|
Mögliche Formate: .JPG, .PNG, .PDF, .DOC, .MOV, .PPT
|
|
</p>
|
|
</div>
|
|
</template>
|