feat:#30 reactive URL parameters RoomOccupation

This commit is contained in:
survellow
2024-02-24 04:14:11 +01:00
parent 27b1b591cc
commit 62acdcbc88
6 changed files with 105 additions and 74 deletions

View File

@ -11,7 +11,7 @@ services:
- "8090:8090" - "8090:8090"
volumes: volumes:
- pb_data:/htwkalender/data # for production with volume - pb_data:/htwkalender/data # for production with volume
# - ./backend/pb_data:/pb_data # for development with bind mount from project directory # - ./backend:/htwkalender/data # for development with bind mount from project directory
htwkalender-frontend: htwkalender-frontend:
build: build:

View File

@ -7,6 +7,9 @@ import { computed, ComputedRef, inject, ref, Ref, watch } from "vue";
import { CalendarOptions, DatesSetArg, EventInput } from "@fullcalendar/core"; import { CalendarOptions, DatesSetArg, EventInput } from "@fullcalendar/core";
import { fetchEventsByRoomAndDuration } from "../api/fetchRoom.ts"; import { fetchEventsByRoomAndDuration } from "../api/fetchRoom.ts";
import { useI18n } from "vue-i18n"; 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 { t } = useI18n({ useScope: "global" });
const props = defineProps({ const props = defineProps({
@ -14,11 +17,6 @@ const props = defineProps({
type: String, type: String,
required: true, required: true,
}, },
date: {
type: Date,
required: false,
default: null,
},
}); });
type CalenderEvent = { type CalenderEvent = {
@ -28,7 +26,28 @@ type CalenderEvent = {
showFree: boolean; showFree: boolean;
}; };
const propDate: Ref<Date | null> = ref(props.date); 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 currentDateFrom: Ref<string> = ref("");
const currentDateTo: Ref<string> = ref(""); const currentDateTo: Ref<string> = ref("");
@ -71,15 +90,13 @@ async function getOccupation() {
}); });
} }
import allLocales from "@fullcalendar/core/locales-all";
const calendarOptions: ComputedRef<CalendarOptions> = computed(() => ({ const calendarOptions: ComputedRef<CalendarOptions> = computed(() => ({
locales: allLocales, locales: allLocales,
locale: t("languageCode"), locale: t("languageCode"),
plugins: [dayGridPlugin, interactionPlugin, timeGridPlugin], plugins: [dayGridPlugin, interactionPlugin, timeGridPlugin],
// local debugging of mobilePage variable in object creation on ternary expression // local debugging of mobilePage variable in object creation on ternary expression
initialView: mobilePage.value ? "Day" : "week", initialView: mobilePage.value ? "Day" : "week",
initialDate: propDate.value ? propDate.value : new Date(), initialDate: date.value,
dayHeaderFormat: { weekday: "short", omitCommas: true }, dayHeaderFormat: { weekday: "short", omitCommas: true },
slotDuration: "00:15:00", slotDuration: "00:15:00",
eventTimeFormat: { eventTimeFormat: {
@ -131,28 +148,18 @@ const calendarOptions: ComputedRef<CalendarOptions> = computed(() => ({
}, },
datesSet: function (dateInfo: DatesSetArg) { datesSet: function (dateInfo: DatesSetArg) {
if (propDate.value !== null) { const view = dateInfo.view;
console.debug("props.date: ", props.date); const offset = new Date().getTimezoneOffset();
const date = new Date(props.date); const startDate = new Date(view.activeStart.getTime() - offset * 60 * 1000);
const endDate = new Date(view.activeEnd.getTime() - offset * 60 * 1000);
// calculate the week start and end date which includes the date currentDateFrom.value = startDate.toISOString().split("T")[0];
const weekStart = new Date(date); currentDateTo.value = endDate.toISOString().split("T")[0];
weekStart.setDate(date.getDate() - date.getDay() + 1); router.replace({
const weekEnd = new Date(date); query: {
weekEnd.setDate(date.getDate() - date.getDay() + 6); ...router.currentRoute.value.query,
currentDateFrom.value = weekStart.toISOString().split("T")[0]; date: formatYearMonthDay(startDate),
currentDateTo.value = weekEnd.toISOString().split("T")[0]; },
propDate.value = null; });
} else {
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];
}
getOccupation(); getOccupation();
}, },
events: function ( events: function (

View File

@ -0,0 +1,8 @@
/**
* Format a date to a string in the format "YYYYMMDD".
* @param date - The date to format
* @returns string - The formatted date
*/
export function formatYearMonthDay(date: Date): string {
return date.toISOString().split("T")[0].replace(/-/g, "");
}

View File

@ -134,6 +134,7 @@ import { requestFreeRooms } from "@/api/requestFreeRooms.ts";
import { FilterMatchMode } from "primevue/api"; import { FilterMatchMode } from "primevue/api";
import { padStart } from "@fullcalendar/core/internal"; import { padStart } from "@fullcalendar/core/internal";
import router from "@/router"; import router from "@/router";
import { formatYearMonthDay } from "@/helpers/dates";
const mobilePage = inject("mobilePage") as Ref<boolean>; const mobilePage = inject("mobilePage") as Ref<boolean>;
const filters = ref({ const filters = ref({
@ -146,7 +147,7 @@ function occupationRoute(room: string): void {
name: "room-schedule", name: "room-schedule",
query: { query: {
room: room, room: room,
date: date.value.toISOString().split("T")[0].replace(/-/g, ""), date: formatYearMonthDay(date.value),
}, },
}); });
} }

View File

@ -1,53 +1,67 @@
<script lang="ts" setup> <script lang="ts" setup>
import { Ref, ref } from "vue"; import { Ref, computed, ref, watch } from "vue";
import { fetchRoom } from "../api/fetchRoom.ts"; import { fetchRoom } from "../api/fetchRoom.ts";
import DynamicPage from "./DynamicPage.vue"; import DynamicPage from "./DynamicPage.vue";
import RoomOccupation from "../components/RoomOccupation.vue"; import RoomOccupation from "../components/RoomOccupation.vue";
import { computedAsync } from "@vueuse/core"; import { computedAsync } from "@vueuse/core";
import router from "@/router";
function isRoomAvailable(room: string, rooms: { name: string }[]) { type Room = {
name: string;
};
const selectedRoom: Ref<Room> = ref({ name: "" });
// Watch for changes in URL parameter
router.afterEach(async (to) => {
const room = to.query.room;
if (room && typeof room === "string") {
setRoomFromList(room, rooms.value);
}
});
const rooms = computedAsync<Set<string>>(async () => {
let rooms: Set<string> = new Set();
return await fetchRoom()
.then((data) => {
rooms = new Set(data);
return rooms;
})
.finally(() => {
const room = router.currentRoute.value.query.room;
if (room && typeof room === "string") {
// check if room is available in roomsList
setRoomFromList(room, rooms);
}
});
}, new Set());
const roomsList = computed(() => {
return Array.from(rooms.value).map((room) => {
return { name: room } as Room;
});
});
/**
* Set the room from the list of rooms
* @param room Name of the room
* @param rooms List of available rooms
*/
function setRoomFromList(room: string, rooms: Set<string>) {
// wait for the roomsList to be available // wait for the roomsList to be available
const roomInList = rooms.some((r) => r.name === room); const roomInList: boolean = rooms.has(room);
if (roomInList) { if (roomInList) {
selectedRoom.value.name = room; selectedRoom.value.name = room;
} }
} }
const selectedRoom: Ref<{ name: string }> = ref({ name: "" }); watch(selectedRoom, (newRoom: Room) => {
if (newRoom.name !== "") {
// Get Query Parameters if available router.push({
// room, date query: { ...router.currentRoute.value.query, room: newRoom.name },
const urlParams = new URLSearchParams(window.location.search);
const room = urlParams.get("room");
const roomsList = computedAsync(async () => {
let rooms: { name: string }[] = [];
return await fetchRoom()
.then(
(data) =>
(rooms = data.map((room: string) => {
return { name: room };
})),
)
.finally(() => {
if (room) {
// check if room is available in roomsList
isRoomAvailable(room, rooms);
}
}); });
}, []); }
});
const selectedDate: Ref<Date | undefined> = ref(undefined);
// Set the selected date from the UR
const date = urlParams.get("date");
if (date) {
// date is in format like YYYYMMDD
const year = date.substring(0, 4);
const month = date.substring(4, 6);
const day = date.substring(6, 8);
selectedDate.value = new Date(`${year}-${month}-${day}`);
}
</script> </script>
<template> <template>
@ -70,7 +84,7 @@ if (date) {
/> />
</template> </template>
<template #content> <template #content>
<RoomOccupation :room="selectedRoom.name" :date="selectedDate" /> <RoomOccupation :room="selectedRoom.name" />
</template> </template>
</DynamicPage> </DynamicPage>
</template> </template>

View File

@ -73,9 +73,10 @@ http {
geo $admin { geo $admin {
default 1; default 1;
10.0.0.0/8 0; # Private Network 10.0.0.0/8 0; # Private Network
192.168.0.0/24 0; # Localhost Network 127.0.0.0/8 0; # Localhost Network
192.168.0.0/16 0; # Localhost Network
141.57.0.0/16 0; # HTWK Leipzig Network 141.57.0.0/16 0; # HTWK Leipzig Network
172.18.0.0/24 0; # Docker Internal Network 172.16.0.0/12 0; # Private Network
} }
map $admin $limit_key { map $admin $limit_key {
@ -154,7 +155,7 @@ http {
proxy_cache_lock on; proxy_cache_lock on;
proxy_cache_use_stale timeout updating; proxy_cache_use_stale timeout updating;
add_header X-Proxy-Cache $upstream_cache_status; add_header X-Proxy-Cache $upstream_cache_status;
limit_req zone=modules burst=5 nodelay; limit_req zone=modules burst=30 nodelay;
} }
location /api/courses { location /api/courses {