owltide/pages/schedule.vue
Hornwitser e722876aae
All checks were successful
/ build (push) Successful in 3m29s
/ deploy (push) Successful in 47s
Use a pinia store to manage account state
Refactor the existing scattered code dealing with the account state into
a pinia store.
2025-05-24 20:01:23 +02:00

115 lines
3.4 KiB
Vue

<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 && accounts" label="Crew">
<option
v-for="account in accounts.filter(a => a.type === 'crew' || a.type === 'admin')"
:key="account.id"
:value="`crew-${account.id}`"
:selected="filter === `crew-${account.id}`"
>{{ account.name }}</option>
</optgroup>
</select>
</label>
<Timetable :schedule :eventSlotFilter :shiftSlotFilter />
<EventsEdit />
<h2>Events</h2>
<EventCard v-for="event in schedule.events.filter(e => e.slots.some(eventSlotFilter))" :event/>
<h2>Locations</h2>
<ul>
<li v-for="location in schedule.locations" :key="location.id">
<h3>{{ location.name }}</h3>
{{ location.description ?? "No description provided" }}
</li>
</ul>
</main>
</template>
<script setup lang="ts">
import type { ShiftSlot, TimeSlot } from '~/shared/types/schedule';
const accountStore = useAccountStore();
const { data: accounts } = await useAccounts();
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) {
return () => true;
}
const aid = accountStore.id;
if (filter.value === "my-schedule") {
const ids = new Set(accountStore.interestedIds);
for (const event of schedule.value.events) {
if (ids.has(event.id)) {
for (const slot of event.slots) {
ids.add(slot.id);
}
}
}
return (slot: TimeSlot) => ids.has(slot.id) || slot.assigned?.some(id => id === aid) || false;
}
if (filter.value === "assigned") {
return (slot: TimeSlot) => slot.assigned?.some(id => id === aid) || false;
}
if (filter.value.startsWith("crew-")) {
const cid = parseInt(filter.value.slice(5));
return (slot: TimeSlot) => slot.assigned?.some(id => id === 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: ShiftSlot) => slot.assigned?.some(id => id === aid) || false;
}
if (filter.value.startsWith("crew-")) {
const cid = parseInt(filter.value.slice(5));
return (slot: ShiftSlot) => slot.assigned?.some(id => id === cid) || false;
}
return () => false;
});
</script>