owltide/pages/schedule.vue
Hornwitser 9592cd3160 Name the application Owltide
The name is inspired by the watchful owl perching from the tree tops
with complete overview of all that's going on combined with -tide in
the sense it's used for in words like summertide and eastertide.
2025-07-01 18:41:24 +02:00

133 lines
3.9 KiB
Vue

<!--
SPDX-FileCopyrightText: © 2025 Hornwitser <code@hornwitser.no>
SPDX-License-Identifier: AGPL-3.0-or-later
-->
<template>
<main>
<h1>Schedule & Events</h1>
<p>
Study carefully, we only hold these events once a year.
</p>
<p v-if="!accountStore.valid">
<NuxtLink to="/login">Login</NuxtLink> or <NuxtLink to="/login#create-account">Create an account</NuxtLink>
to get notified about updates to the schedule.
</p>
<p v-else>
Check out your <NuxtLink to="/account/settings">Account Setting</NuxtLink> to set up notifications for changes to schedule.
</p>
<h2>Schedule</h2>
<label v-if="accountStore.valid">
Filter:
<select
v-model="filter"
>
<option
:value="undefined"
:selected="filter === undefined"
>&lt;All events&gt;</option>
<option
value="my-schedule"
:selected='filter === "my-schedule"'
>My Schedule</option>
<option
v-if="accountStore.isCrew"
value="assigned"
:selected='filter === "assigned"'
>Assigned to Me</option>
<optgroup v-if="accountStore.isCrew" label="Crew">
<option
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>
</optgroup>
</select>
</label>
<Timetable :schedule :eventSlotFilter :shiftSlotFilter />
<h2>Events</h2>
<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>
<h2>Locations</h2>
<ul>
<li v-for="location in schedule.locations.values()" :key="location.id">
<h3>{{ location.name }}</h3>
{{ location.description ?? "No description provided" }}
</li>
</ul>
</main>
</template>
<script setup lang="ts">
useHead({
title: "Schedule",
});
const accountStore = useAccountStore();
const usersStore = useUsersStore();
await usersStore.fetch();
const schedule = await useSchedule();
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(() => {
if (filter.value === undefined || !accountStore.valid || schedule.value.deleted) {
return () => true;
}
const aid = accountStore.id;
if (filter.value === "my-schedule") {
const slotIds = new Set(accountStore.interestedEventSlotIds);
for (const event of schedule.value.events.values()) {
if (!event.deleted && accountStore.interestedEventIds.has(event.id)) {
for (const slot of event.slots.values()) {
slotIds.add(slot.id);
}
}
}
return (slot: ClientScheduleEventSlot) => slotIds.has(slot.id) || slot.assigned.has(aid!) || false;
}
if (filter.value === "assigned") {
return (slot: ClientScheduleEventSlot) => slot.assigned.has(aid!) || false;
}
if (filter.value.startsWith("crew-")) {
const cid = parseInt(filter.value.slice(5));
return (slot: ClientScheduleEventSlot) => slot.assigned.has(cid) || false;
}
return () => false;
});
const shiftSlotFilter = computed(() => {
if (filter.value === undefined || !accountStore.valid) {
return () => true;
}
if (filter.value === "my-schedule" || filter.value === "assigned") {
const aid = accountStore.id;
return (slot: ClientScheduleShiftSlot) => slot.assigned.has(aid!) || false;
}
if (filter.value.startsWith("crew-")) {
const cid = parseInt(filter.value.slice(5));
return (slot: ClientScheduleShiftSlot) => slot.assigned.has(cid) || false;
}
return () => false;
});
</script>