2025-06-30 18:58:24 +02:00
|
|
|
<!--
|
|
|
|
SPDX-FileCopyrightText: © 2025 Hornwitser <code@hornwitser.no>
|
|
|
|
SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
-->
|
2025-03-05 15:36:50 +01:00
|
|
|
<template>
|
2025-06-14 19:22:53 +02:00
|
|
|
<main>
|
2025-03-05 15:36:50 +01:00
|
|
|
<h1>Schedule & Events</h1>
|
|
|
|
<p>
|
|
|
|
Study carefully, we only hold these events once a year.
|
|
|
|
</p>
|
2025-05-24 20:01:23 +02:00
|
|
|
<p v-if="!accountStore.valid">
|
2025-03-07 23:53:57 +01:00
|
|
|
<NuxtLink to="/login">Login</NuxtLink> or <NuxtLink to="/login#create-account">Create an account</NuxtLink>
|
|
|
|
to get notified about updates to the schedule.
|
2025-03-07 18:43:24 +01:00
|
|
|
</p>
|
2025-03-07 23:53:57 +01:00
|
|
|
<p v-else>
|
2025-03-07 18:43:24 +01:00
|
|
|
Check out your <NuxtLink to="/account/settings">Account Setting</NuxtLink> to set up notifications for changes to schedule.
|
2025-03-05 15:36:50 +01:00
|
|
|
</p>
|
|
|
|
<h2>Schedule</h2>
|
2025-05-24 20:01:23 +02:00
|
|
|
<label v-if="accountStore.valid">
|
2025-03-15 22:47:32 +01:00
|
|
|
Filter:
|
|
|
|
<select
|
|
|
|
v-model="filter"
|
|
|
|
>
|
|
|
|
<option
|
|
|
|
:value="undefined"
|
|
|
|
:selected="filter === undefined"
|
|
|
|
><All events></option>
|
|
|
|
<option
|
|
|
|
value="my-schedule"
|
|
|
|
:selected='filter === "my-schedule"'
|
|
|
|
>My Schedule</option>
|
|
|
|
<option
|
2025-05-24 20:01:23 +02:00
|
|
|
v-if="accountStore.isCrew"
|
2025-03-15 22:47:32 +01:00
|
|
|
value="assigned"
|
|
|
|
:selected='filter === "assigned"'
|
|
|
|
>Assigned to Me</option>
|
2025-06-23 00:17:22 +02:00
|
|
|
<optgroup v-if="accountStore.isCrew" label="Crew">
|
2025-03-15 22:47:32 +01:00
|
|
|
<option
|
2025-06-23 00:17:22 +02:00
|
|
|
v-for="user in [...usersStore.users.values()].filter(a => a.type === 'crew' || a.type === 'admin')"
|
|
|
|
:key="user.id"
|
|
|
|
:value="`crew-${user.id}`"
|
|
|
|
:selected="filter === `crew-${user.id}`"
|
|
|
|
>{{ user.name }}</option>
|
2025-03-15 22:47:32 +01:00
|
|
|
</optgroup>
|
|
|
|
</select>
|
|
|
|
</label>
|
|
|
|
<Timetable :schedule :eventSlotFilter :shiftSlotFilter />
|
2025-03-05 15:36:50 +01:00
|
|
|
<h2>Events</h2>
|
2025-06-24 15:41:53 +02:00
|
|
|
<CardEvent
|
|
|
|
v-for="event in [...schedule.events.values()].filter(e => !e.deleted && [...e.slots.values()].some(eventSlotFilter))"
|
|
|
|
:key="event.id"
|
|
|
|
:event
|
|
|
|
/>
|
|
|
|
<template v-if="accountStore.isCrew">
|
|
|
|
<h2>Shifts</h2>
|
|
|
|
<CardShift
|
|
|
|
v-for="shift in [...schedule.shifts.values()].filter(s => !s.deleted && [...s.slots.values()].some(shiftSlotFilter))"
|
|
|
|
:key="shift.id"
|
|
|
|
:shift
|
|
|
|
/>
|
|
|
|
</template>
|
2025-03-05 15:36:50 +01:00
|
|
|
<h2>Locations</h2>
|
|
|
|
<ul>
|
2025-06-14 19:22:53 +02:00
|
|
|
<li v-for="location in schedule.locations.values()" :key="location.id">
|
2025-03-05 15:36:50 +01:00
|
|
|
<h3>{{ location.name }}</h3>
|
|
|
|
{{ location.description ?? "No description provided" }}
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
|
|
</main>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
2025-05-24 20:01:23 +02:00
|
|
|
const accountStore = useAccountStore();
|
2025-06-23 00:17:22 +02:00
|
|
|
const usersStore = useUsersStore();
|
|
|
|
await usersStore.fetch();
|
2025-03-10 15:41:32 +01:00
|
|
|
const schedule = await useSchedule();
|
2025-03-15 22:47:32 +01:00
|
|
|
|
|
|
|
const route = useRoute();
|
|
|
|
const filter = computed({
|
|
|
|
get: () => queryToString(route.query.filter),
|
|
|
|
set: (value: string | undefined) => navigateTo({
|
|
|
|
path: route.path,
|
|
|
|
query: {
|
|
|
|
...route.query,
|
|
|
|
filter: value,
|
|
|
|
},
|
|
|
|
}),
|
|
|
|
});
|
|
|
|
|
|
|
|
const eventSlotFilter = computed(() => {
|
2025-06-11 21:05:17 +02:00
|
|
|
if (filter.value === undefined || !accountStore.valid || schedule.value.deleted) {
|
2025-03-15 22:47:32 +01:00
|
|
|
return () => true;
|
|
|
|
}
|
2025-05-24 20:01:23 +02:00
|
|
|
const aid = accountStore.id;
|
2025-03-15 22:47:32 +01:00
|
|
|
if (filter.value === "my-schedule") {
|
2025-06-11 21:05:17 +02:00
|
|
|
const slotIds = new Set(accountStore.interestedEventSlotIds);
|
2025-06-14 19:22:53 +02:00
|
|
|
for (const event of schedule.value.events.values()) {
|
2025-06-11 21:05:17 +02:00
|
|
|
if (!event.deleted && accountStore.interestedEventIds.has(event.id)) {
|
2025-06-14 19:22:53 +02:00
|
|
|
for (const slot of event.slots.values()) {
|
2025-06-11 21:05:17 +02:00
|
|
|
slotIds.add(slot.id);
|
2025-03-15 22:47:32 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2025-06-14 19:22:53 +02:00
|
|
|
return (slot: ClientScheduleEventSlot) => slotIds.has(slot.id) || slot.assigned.has(aid!) || false;
|
2025-03-15 22:47:32 +01:00
|
|
|
}
|
|
|
|
if (filter.value === "assigned") {
|
2025-06-14 19:22:53 +02:00
|
|
|
return (slot: ClientScheduleEventSlot) => slot.assigned.has(aid!) || false;
|
2025-03-15 22:47:32 +01:00
|
|
|
}
|
|
|
|
if (filter.value.startsWith("crew-")) {
|
|
|
|
const cid = parseInt(filter.value.slice(5));
|
2025-06-14 19:22:53 +02:00
|
|
|
return (slot: ClientScheduleEventSlot) => slot.assigned.has(cid) || false;
|
2025-03-15 22:47:32 +01:00
|
|
|
}
|
|
|
|
return () => false;
|
|
|
|
});
|
|
|
|
const shiftSlotFilter = computed(() => {
|
2025-05-24 20:01:23 +02:00
|
|
|
if (filter.value === undefined || !accountStore.valid) {
|
2025-03-15 22:47:32 +01:00
|
|
|
return () => true;
|
|
|
|
}
|
|
|
|
if (filter.value === "my-schedule" || filter.value === "assigned") {
|
2025-05-24 20:01:23 +02:00
|
|
|
const aid = accountStore.id;
|
2025-06-14 19:22:53 +02:00
|
|
|
return (slot: ClientScheduleShiftSlot) => slot.assigned.has(aid!) || false;
|
2025-03-15 22:47:32 +01:00
|
|
|
}
|
|
|
|
if (filter.value.startsWith("crew-")) {
|
|
|
|
const cid = parseInt(filter.value.slice(5));
|
2025-06-14 19:22:53 +02:00
|
|
|
return (slot: ClientScheduleShiftSlot) => slot.assigned.has(cid) || false;
|
2025-03-15 22:47:32 +01:00
|
|
|
}
|
|
|
|
return () => false;
|
|
|
|
});
|
2025-03-05 15:36:50 +01:00
|
|
|
</script>
|