vbv/client/src/pages/cockpit/attendanceCheckPage/AttendanceCheckPage.vue

253 lines
8.0 KiB
Vue

<script setup lang="ts">
import ItCheckbox from "@/components/ui/ItCheckbox.vue";
import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue";
import ItPersonRow from "@/components/ui/ItPersonRow.vue";
import { useCourseSessionDetailQuery, useCurrentCourseSession } from "@/composables";
import type { AttendanceUserStatus } from "@/gql/graphql";
import { ATTENDANCE_CHECK_MUTATION } from "@/graphql/mutations";
import type { DropdownSelectable } from "@/types";
import { useMutation } from "@urql/vue";
import dayjs from "dayjs";
import log from "loglevel";
import { computed, onMounted, reactive, watch } from "vue";
import { useTranslation } from "i18next-vue";
import { ATTENDANCE_CHECK_QUERY } from "@/graphql/queries";
import { graphqlClient } from "@/graphql/client";
import { exportAttendance } from "@/services/dashboard";
import { openDataAsXls } from "@/utils/export";
import { useUserStore } from "@/stores/user";
const { t } = useTranslation();
const attendanceMutation = useMutation(ATTENDANCE_CHECK_MUTATION);
const courseSessionDetailResult = useCourseSessionDetailQuery();
const userStore = useUserStore();
const courseSession = useCurrentCourseSession();
const attendanceCourses = computed(() => {
return courseSessionDetailResult.courseSessionDetail.value?.attendance_courses ?? [];
});
const courseSessionDetail = computed(() => {
return courseSessionDetailResult.courseSessionDetail.value;
});
const attendanceCourseCircleId = computed(() => {
const selectedAttendandeCourse = attendanceCourses.value.find(
(course) => course.id === state.attendanceCourseSelected.id
);
return selectedAttendandeCourse?.learning_content?.circle?.id;
});
const presenceCoursesDropdownOptions = computed(() => {
return attendanceCourses.value.map(
(attendanceCourse) =>
({
id: attendanceCourse.id,
name: `${t("Präsenzkurs")} ${
attendanceCourse.learning_content.circle?.title
} ${dayjs(attendanceCourse.due_date?.start).format("DD.MM.YYYY")}`,
}) as DropdownSelectable
);
});
const state = reactive({
userPresence: new Map<string, boolean>(),
attendanceCourseSelected: presenceCoursesDropdownOptions.value[0],
disclaimerConfirmed: false,
attendanceSaved: false,
});
watch(
attendanceCourses,
(newVal) => {
if (newVal && newVal.length > 0) {
state.attendanceCourseSelected = presenceCoursesDropdownOptions.value[0];
}
},
{ immediate: true }
);
function resetState() {
state.userPresence = new Map<string, boolean>();
state.disclaimerConfirmed = false;
state.attendanceSaved = false;
}
const onSubmit = async () => {
type UserPresence = {
user_id: string;
status: AttendanceUserStatus;
};
const attendanceUserList: UserPresence[] = Array.from(state.userPresence.keys()).map(
(key) => ({
user_id: key,
status: state.userPresence.get(key) ? "PRESENT" : "ABSENT",
})
);
const res = await attendanceMutation.executeMutation({
attendanceCourseId: state.attendanceCourseSelected.id.toString(),
attendanceUserList: attendanceUserList,
});
if (res.error) {
log.error("Could not submit attendance check: ", res.error);
return;
}
state.disclaimerConfirmed = false;
state.attendanceSaved = true;
log.info("Attendance check submitted: ", res);
};
const loadAttendanceData = async () => {
resetState();
// with changing variables `useQuery` does not seem to work correctly
if (state.attendanceCourseSelected) {
const res = await graphqlClient.query(
ATTENDANCE_CHECK_QUERY,
{
courseSessionId: state.attendanceCourseSelected.id.toString(),
},
{
requestPolicy: "network-only",
}
);
const attendanceUserList =
res.data?.course_session_attendance_course?.attendance_user_list ?? [];
for (const user of attendanceUserList) {
if (!user) continue;
state.userPresence.set(user.user_id, user.status === "PRESENT");
}
if (attendanceUserList.length !== 0) {
state.attendanceSaved = true;
}
}
};
function editAgain() {
state.attendanceSaved = false;
}
async function exportData() {
const data = await exportAttendance(
{
courseSessionIds: [Number(courseSession.value.id)],
circleIds: [Number(attendanceCourseCircleId.value)],
},
userStore.language
);
openDataAsXls(data.encoded_data, data.file_name);
}
onMounted(() => {
log.debug("AttendanceCheckPage mounted");
loadAttendanceData();
});
watch(
() => state.attendanceCourseSelected,
() => {
log.debug("attendanceCourseSelected changed", state.attendanceCourseSelected);
loadAttendanceData();
},
{ immediate: true }
);
</script>
<template>
<div class="bg-gray-200">
<div v-if="courseSessionDetail" class="container-large">
<nav class="py-4 pb-4">
<router-link
class="btn-text inline-flex items-center pl-0"
:to="`/course/${courseSessionDetail.course.slug}/cockpit`"
>
<it-icon-arrow-left />
<span>{{ $t("general.back") }}</span>
</router-link>
</nav>
<div class="flex items-center justify-between">
<h3 class="pb-4 text-xl font-bold">{{ $t("Anwesenheit Präsenzkurse") }}</h3>
<button
v-if="state.attendanceSaved"
class="flex"
data-cy="export-button"
@click="exportData"
>
<it-icon-export></it-icon-export>
<span class="ml inline-block">{{ $t("a.Als Excel exportieren") }}</span>
</button>
</div>
<section v-if="attendanceCourses.length && state.attendanceCourseSelected">
<div class="flex flex-row justify-between bg-white p-6">
<ItDropdownSelect
v-model="state.attendanceCourseSelected"
:items="presenceCoursesDropdownOptions ?? []"
></ItDropdownSelect>
<div v-if="!state.attendanceSaved" class="flex flex-row items-center">
<ItCheckbox
:checkbox-item="{
value: true,
checked: state.disclaimerConfirmed,
}"
@toggle="state.disclaimerConfirmed = !state.disclaimerConfirmed"
></ItCheckbox>
<p class="w-64 pr-4 text-sm">
{{
$t(
"Ich will die Anwesenheit der untenstehenden Personen definitiv bestätigen."
)
}}
</p>
<button
class="btn-primary"
:disabled="!state.disclaimerConfirmed"
@click="onSubmit"
>
{{ $t("Anwesenheit bestätigen") }}
</button>
</div>
<div v-else class="self-center">
<p class="text-base">
{{ $t("a.Die Anwesenheit wurde definitiv bestätigt") }}
</p>
<button class="btn-link link" @click="editAgain()">
{{ $t("a.Erneut bearbeiten") }}
</button>
</div>
</div>
<div class="mt-4 flex flex-col bg-white p-6">
<div
v-for="(csu, index) in courseSessionDetailResult.filterMembers()"
:key="csu.user_id"
>
<ItPersonRow
:name="`${csu.first_name} ${csu.last_name}`"
:avatar-url="csu.avatar_url"
:class="0 === index ? 'border-none' : ''"
:extra-info="
csu.optional_attendance ? `${$t('a.Optionale Anwesenheit')}` : ''
"
>
<template #leading>
<ItCheckbox
:disabled="state.attendanceSaved"
:checkbox-item="{
value: true,
checked: state.userPresence.get(csu.user_id) as boolean,
}"
@toggle="
state.userPresence.set(
csu.user_id,
!state.userPresence.get(csu.user_id)
)
"
></ItCheckbox>
</template>
</ItPersonRow>
</div>
</div>
</section>
</div>
</div>
</template>