Files
htwkalender/frontend/src/components/RoomOccupation.vue
2024-02-24 04:14:54 +01:00

199 lines
5.4 KiB
Vue

<script lang="ts" setup>
import FullCalendar from "@fullcalendar/vue3";
import dayGridPlugin from "@fullcalendar/daygrid";
import interactionPlugin from "@fullcalendar/interaction";
import timeGridPlugin from "@fullcalendar/timegrid";
import { computed, ComputedRef, inject, ref, Ref, watch } from "vue";
import { CalendarOptions, DatesSetArg, EventInput } from "@fullcalendar/core";
import { fetchEventsByRoomAndDuration } from "../api/fetchRoom.ts";
import { useI18n } from "vue-i18n";
import allLocales from "@fullcalendar/core/locales-all";
import router from "@/router";
import { formatYearMonthDay } from "@/helpers/dates";
const { t } = useI18n({ useScope: "global" });
const props = defineProps({
room: {
type: String,
required: true,
},
});
type CalenderEvent = {
id: number;
start: string;
end: string;
showFree: boolean;
};
const date: Ref<Date> = ref(new Date());
// Watch for changes in URL parameter
router.afterEach(setDateFromQuery);
// Set the selected date from the URL
function setDateFromQuery() {
const queryDate = router.currentRoute.value.query.date;
if (typeof queryDate === "string") {
if (queryDate === formatYearMonthDay(date.value)) {
return;
}
// date is in format like YYYYMMDD
// TODO check if date is valid
const year = queryDate.substring(0, 4);
const month = queryDate.substring(4, 6);
const day = queryDate.substring(6, 8);
date.value = new Date(`${year}-${month}-${day}`);
}
}
setDateFromQuery();
const currentDateFrom: Ref<string> = ref("");
const currentDateTo: Ref<string> = ref("");
const occupations: Ref<CalenderEvent[]> = ref([]);
const mobilePage = inject("mobilePage") as Ref<boolean>;
const selectedRoom = computed(() => props.room);
watch(selectedRoom, () => {
getOccupation();
});
const fullCalendar = ref<InstanceType<typeof FullCalendar>>();
async function getOccupation() {
if (selectedRoom.value === "") {
return;
}
fetchEventsByRoomAndDuration(
selectedRoom.value,
currentDateFrom.value,
currentDateTo.value,
)
.then((events) => {
occupations.value = events?.map((event, index) => {
return {
id: index,
start: event.start.replace(/\s\+\d{4}\s\w+$/, "").replace(" ", "T"),
end: event.end.replace(/\s\+\d{4}\s\w+$/, "").replace(" ", "T"),
showFree: event.free,
};
});
const calendar = fullCalendar.value?.getApi();
calendar?.refetchEvents();
})
.catch((error) => {
console.error(error);
});
}
const calendarOptions: ComputedRef<CalendarOptions> = computed(() => ({
locales: allLocales,
locale: t("languageCode"),
plugins: [dayGridPlugin, interactionPlugin, timeGridPlugin],
// local debugging of mobilePage variable in object creation on ternary expression
initialView: mobilePage.value ? "Day" : "week",
initialDate: date.value,
dayHeaderFormat: { weekday: "short", omitCommas: true },
slotDuration: "00:15:00",
eventTimeFormat: {
hour: "2-digit",
minute: "2-digit",
hour12: false,
},
height: "auto",
views: {
week: {
type: "timeGrid",
slotLabelFormat: {
hour: "numeric",
minute: "2-digit",
omitZeroMinute: false,
meridiem: false,
hour12: false,
},
dateAlignment: "week",
titleFormat: { month: "short", day: "numeric" },
slotMinTime: "06:00:00",
slotMaxTime: "22:00:00",
duration: { days: 7 },
firstDay: 1,
allDaySlot: false,
hiddenDays: [0],
},
Day: {
type: "timeGrid",
slotLabelFormat: {
hour: "numeric",
minute: "2-digit",
omitZeroMinute: false,
meridiem: false,
hour12: false,
},
titleFormat: { month: "short", day: "numeric" },
slotMinTime: "06:00:00",
slotMaxTime: "22:00:00",
duration: { days: 1 },
allDaySlot: false,
hiddenDays: [0],
},
},
headerToolbar: {
end: "prev,next today",
center: "title",
start: "week,Day",
},
datesSet: function (dateInfo: DatesSetArg) {
const view = dateInfo.view;
const offset = new Date().getTimezoneOffset();
const startDate = new Date(view.activeStart.getTime() - offset * 60 * 1000);
const endDate = new Date(view.activeEnd.getTime() - offset * 60 * 1000);
currentDateFrom.value = startDate.toISOString().split("T")[0];
currentDateTo.value = endDate.toISOString().split("T")[0];
router.replace({
query: {
...router.currentRoute.value.query,
date: formatYearMonthDay(startDate),
},
});
getOccupation();
},
events: function (
_info: unknown,
successCallback: (events: EventInput[]) => void,
) {
successCallback(
occupations.value.map((event) => {
return {
id: event.id.toString(),
start: event.start,
end: event.end,
color: event.showFree ? "var(--green-800)" : "var(--primary-color)",
textColor: event.showFree
? "var(--green-50)"
: "var(--primary-color-text)",
title: event.showFree
? t("roomFinderPage.available")
: t("roomFinderPage.occupied"),
} as EventInput;
}),
);
},
}));
</script>
<template>
<FullCalendar ref="fullCalendar" :options="calendarOptions" />
</template>
<style scoped>
:deep(.fc-toolbar.fc-header-toolbar) {
flex-wrap: wrap;
justify-content: space-between;
gap: 0.5rem;
}
</style>