Use a pinia store to manage account state
Refactor the existing scattered code dealing with the account state into a pinia store.
This commit is contained in:
parent
fae8b4e2e4
commit
e722876aae
12 changed files with 126 additions and 98 deletions
|
@ -5,13 +5,13 @@
|
||||||
<p v-if="event.interested">
|
<p v-if="event.interested">
|
||||||
{{ event.interested }} interested
|
{{ event.interested }} interested
|
||||||
</p>
|
</p>
|
||||||
<p v-if="sessionStore.account">
|
<p v-if="accountStore.interestedIds">
|
||||||
<button
|
<button
|
||||||
class="interested"
|
class="interested"
|
||||||
:class="{ active: interestedIds.has(event.id) }"
|
:class="{ active: accountStore.interestedIds?.has(event.id) }"
|
||||||
@click="toggle(event.id, event.slots.map(slot => slot.id))"
|
@click="toggle(event.id, event.slots.map(slot => slot.id))"
|
||||||
>
|
>
|
||||||
{{ interestedIds.has(event.id) ? "✔ interested" : "🔔 interested?" }}
|
{{ accountStore.interestedIds?.has(event.id) ? "✔ interested" : "🔔 interested?" }}
|
||||||
</button>
|
</button>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
@ -20,13 +20,13 @@
|
||||||
<li v-for="slot in event.slots" :key="slot.id">
|
<li v-for="slot in event.slots" :key="slot.id">
|
||||||
{{ formatTime(slot.start) }} - {{ formatTime(slot.end) }}
|
{{ formatTime(slot.start) }} - {{ formatTime(slot.end) }}
|
||||||
<button
|
<button
|
||||||
v-if="sessionStore.account && event.slots.length > 1"
|
v-if="accountStore.interestedIds && event.slots.length > 1"
|
||||||
class="interested"
|
class="interested"
|
||||||
:disabled="interestedIds.has(event.id)"
|
:disabled="accountStore.interestedIds.has(event.id)"
|
||||||
:class="{ active: interestedIds.has(event.id) || interestedIds.has(slot.id) }"
|
:class="{ active: accountStore.interestedIds.has(event.id) || accountStore.interestedIds.has(slot.id) }"
|
||||||
@click="toggle(slot.id)"
|
@click="toggle(slot.id)"
|
||||||
>
|
>
|
||||||
{{ interestedIds.has(event.id) || interestedIds.has(slot.id) ? "✔ interested" : "🔔 interested?" }}
|
{{ accountStore.interestedIds.has(event.id) || accountStore.interestedIds.has(slot.id) ? "✔ interested" : "🔔 interested?" }}
|
||||||
</button>
|
</button>
|
||||||
<template v-if="slot.interested">
|
<template v-if="slot.interested">
|
||||||
({{ slot.interested }} interested)
|
({{ slot.interested }} interested)
|
||||||
|
@ -48,33 +48,16 @@ defineProps<{
|
||||||
event: ScheduleEvent
|
event: ScheduleEvent
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const runtimeConfig = useRuntimeConfig();
|
const accountStore = useAccountStore();
|
||||||
const sessionStore = useSessionStore();
|
|
||||||
const interestedIds = computed(() => new Set(sessionStore.account?.interestedIds ?? []));
|
|
||||||
const timezone = computed(() => sessionStore.account?.timezone ?? runtimeConfig.public.defaultTimezone);
|
|
||||||
const { data: accounts } = await useAccounts();
|
const { data: accounts } = await useAccounts();
|
||||||
const idToAccount = computed(() => new Map(accounts.value?.map(a => [a.id, a])));
|
const idToAccount = computed(() => new Map(accounts.value?.map(a => [a.id, a])));
|
||||||
|
|
||||||
function formatTime(time: string) {
|
function formatTime(time: string) {
|
||||||
return DateTime.fromISO(time, { zone: timezone.value }).toFormat("yyyy-LL-dd HH:mm");
|
return DateTime.fromISO(time, { zone: accountStore.activeTimezone }).toFormat("yyyy-LL-dd HH:mm");
|
||||||
}
|
}
|
||||||
|
|
||||||
async function toggle(id: string, slotIds?: string[]) {
|
async function toggle(id: string, slotIds?: string[]) {
|
||||||
let newIds = [...sessionStore.account!.interestedIds ?? []];
|
await accountStore.toggleInterestedId(id, slotIds);
|
||||||
if (interestedIds.value.has(id)) {
|
|
||||||
newIds = newIds.filter(newId => newId !== id);
|
|
||||||
} else {
|
|
||||||
newIds.push(id);
|
|
||||||
if (slotIds) {
|
|
||||||
const filterIds = new Set(slotIds);
|
|
||||||
newIds = newIds.filter(newId => !filterIds.has(newId));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
await $fetch("/api/account", {
|
|
||||||
method: "PATCH",
|
|
||||||
body: { interestedIds: newIds },
|
|
||||||
})
|
|
||||||
await sessionStore.fetch();
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@
|
||||||
<td>
|
<td>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
:disabled="!canEditPublic"
|
:disabled="!accountStore.canEditPublic"
|
||||||
:value="!event.crew"
|
:value="!event.crew"
|
||||||
:checked="!event.crew"
|
:checked="!event.crew"
|
||||||
@change="editEvent(event, { crew: !($event as any).target.value })"
|
@change="editEvent(event, { crew: !($event as any).target.value })"
|
||||||
|
@ -75,7 +75,7 @@
|
||||||
<td>
|
<td>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
:disabled="!canEditPublic"
|
:disabled="!accountStore.canEditPublic"
|
||||||
v-model="newEventPublic"
|
v-model="newEventPublic"
|
||||||
>
|
>
|
||||||
</td>
|
</td>
|
||||||
|
@ -135,11 +135,10 @@ defineProps<{
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const schedule = await useSchedule();
|
const schedule = await useSchedule();
|
||||||
const sessionStore = useSessionStore();
|
const accountStore = useAccountStore();
|
||||||
const canEditPublic = computed(() => sessionStore.account?.type === "admin");
|
|
||||||
|
|
||||||
function canEdit(event: ScheduleEvent) {
|
function canEdit(event: ScheduleEvent) {
|
||||||
return event.crew || canEditPublic.value;
|
return event.crew || accountStore.canEditPublic;
|
||||||
}
|
}
|
||||||
|
|
||||||
const changes = ref<ChangeRecord<ScheduleEvent>[]>([]);
|
const changes = ref<ChangeRecord<ScheduleEvent>[]>([]);
|
||||||
|
|
|
@ -8,18 +8,18 @@
|
||||||
<li>
|
<li>
|
||||||
<NuxtLink to="/schedule">Schedule</NuxtLink>
|
<NuxtLink to="/schedule">Schedule</NuxtLink>
|
||||||
</li>
|
</li>
|
||||||
<li v-if="sessionStore.account?.type === 'admin' || sessionStore.account?.type === 'crew'">
|
<li v-if="accountStore.canEdit">
|
||||||
<NuxtLink to="/edit">Edit</NuxtLink>
|
<NuxtLink to="/edit">Edit</NuxtLink>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="account">
|
<div class="account">
|
||||||
<template v-if="sessionStore.account">
|
<template v-if="accountStore.valid">
|
||||||
{{ sessionStore.account.name }}
|
{{ accountStore.name }}
|
||||||
(s:{{ sessionStore.id }} a:{{ sessionStore.account.id }}{{ sessionStore.push ? " push" : null }})
|
(s:{{ sessionStore.id }} a:{{ accountStore.id }}{{ sessionStore.push ? " push" : null }})
|
||||||
{{ sessionStore.account.type }}
|
{{ accountStore.type }}
|
||||||
<NuxtLink to="/account/settings">Settings</NuxtLink>
|
<NuxtLink to="/account/settings">Settings</NuxtLink>
|
||||||
<LogOutButton v-if="sessionStore.account.type !== 'anonymous'"/>
|
<LogOutButton v-if="accountStore.type !== 'anonymous'"/>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<NuxtLink to="/login">Log In</NuxtLink>
|
<NuxtLink to="/login">Log In</NuxtLink>
|
||||||
|
@ -29,6 +29,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
const accountStore = useAccountStore();
|
||||||
const sessionStore = useSessionStore();
|
const sessionStore = useSessionStore();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -424,12 +424,8 @@ function removeSlot(eventChanges: ChangeRecord<ScheduleEvent>[], event: Schedule
|
||||||
return eventChanges;
|
return eventChanges;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sessionStore = useSessionStore();
|
const accountStore = useAccountStore();
|
||||||
const schedule = await useSchedule();
|
const schedule = await useSchedule();
|
||||||
const runtimeConfig = useRuntimeConfig();
|
|
||||||
const timezone = computed(
|
|
||||||
() => sessionStore.account?.timezone ?? runtimeConfig.public.defaultTimezone
|
|
||||||
);
|
|
||||||
|
|
||||||
type EventSlotChange = { op: "set" | "del", data: EventSlot } ;
|
type EventSlotChange = { op: "set" | "del", data: EventSlot } ;
|
||||||
|
|
||||||
|
@ -466,12 +462,12 @@ const newEventStart = ref("");
|
||||||
const newEventDuration = ref("01:00");
|
const newEventDuration = ref("01:00");
|
||||||
const newEventEnd = computed({
|
const newEventEnd = computed({
|
||||||
get: () => (
|
get: () => (
|
||||||
DateTime.fromISO(newEventStart.value, { zone: timezone.value })
|
DateTime.fromISO(newEventStart.value, { zone: accountStore.activeTimezone })
|
||||||
.plus(Duration.fromISOTime(newEventDuration.value))
|
.plus(Duration.fromISOTime(newEventDuration.value))
|
||||||
.toFormat("HH:mm")
|
.toFormat("HH:mm")
|
||||||
),
|
),
|
||||||
set: (value: string) => {
|
set: (value: string) => {
|
||||||
const start = DateTime.fromISO(newEventStart.value, { zone: timezone.value });
|
const start = DateTime.fromISO(newEventStart.value, { zone: accountStore.activeTimezone });
|
||||||
const end = endFromTime(start, value);
|
const end = endFromTime(start, value);
|
||||||
newEventDuration.value = dropDay(end.diff(start)).toFormat("hh:mm");
|
newEventDuration.value = dropDay(end.diff(start)).toFormat("hh:mm");
|
||||||
},
|
},
|
||||||
|
@ -508,7 +504,7 @@ function editEventSlot(
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
if (edits.start) {
|
if (edits.start) {
|
||||||
const start = DateTime.fromISO(edits.start, { zone: timezone.value });
|
const start = DateTime.fromISO(edits.start, { zone: accountStore.activeTimezone });
|
||||||
eventSlot = {
|
eventSlot = {
|
||||||
...eventSlot,
|
...eventSlot,
|
||||||
start,
|
start,
|
||||||
|
@ -577,7 +573,7 @@ function newEventSlot(options: { start?: DateTime, end?: DateTime } = {}) {
|
||||||
end = options.end;
|
end = options.end;
|
||||||
start = options.end.minus(duration);
|
start = options.end.minus(duration);
|
||||||
} else {
|
} else {
|
||||||
start = DateTime.fromISO(newEventStart.value, { zone: timezone.value });
|
start = DateTime.fromISO(newEventStart.value, { zone: accountStore.activeTimezone });
|
||||||
end = endFromTime(start, newEventEnd.value);
|
end = endFromTime(start, newEventEnd.value);
|
||||||
}
|
}
|
||||||
if (!start.isValid || !end.isValid) {
|
if (!start.isValid || !end.isValid) {
|
||||||
|
@ -641,8 +637,8 @@ const eventSlots = computed(() => {
|
||||||
location,
|
location,
|
||||||
assigned: slot.assigned ?? [],
|
assigned: slot.assigned ?? [],
|
||||||
origLocation: location,
|
origLocation: location,
|
||||||
start: DateTime.fromISO(slot.start, { zone: timezone.value }),
|
start: DateTime.fromISO(slot.start, { zone: accountStore.activeTimezone }),
|
||||||
end: DateTime.fromISO(slot.end, { zone: timezone.value }),
|
end: DateTime.fromISO(slot.end, { zone: accountStore.activeTimezone }),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -394,12 +394,8 @@ function removeSlot(eventChanges: ChangeRecord<Shift>[], shift: Shift, shiftSlot
|
||||||
return eventChanges;
|
return eventChanges;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sessionStore = useSessionStore();
|
const accountStore = useAccountStore();
|
||||||
const schedule = await useSchedule();
|
const schedule = await useSchedule();
|
||||||
const runtimeConfig = useRuntimeConfig();
|
|
||||||
const timezone = computed(
|
|
||||||
() => sessionStore.account?.timezone ?? runtimeConfig.public.defaultTimezone
|
|
||||||
);
|
|
||||||
|
|
||||||
type ShiftSlotChange = { op: "set" | "del", data: ShiftSlot } ;
|
type ShiftSlotChange = { op: "set" | "del", data: ShiftSlot } ;
|
||||||
|
|
||||||
|
@ -436,12 +432,12 @@ const newShiftStart = ref("");
|
||||||
const newShiftDuration = ref("01:00");
|
const newShiftDuration = ref("01:00");
|
||||||
const newShiftEnd = computed({
|
const newShiftEnd = computed({
|
||||||
get: () => (
|
get: () => (
|
||||||
DateTime.fromISO(newShiftStart.value, { zone: timezone.value })
|
DateTime.fromISO(newShiftStart.value, { zone: accountStore.activeTimezone })
|
||||||
.plus(Duration.fromISOTime(newShiftDuration.value))
|
.plus(Duration.fromISOTime(newShiftDuration.value))
|
||||||
.toFormat("HH:mm")
|
.toFormat("HH:mm")
|
||||||
),
|
),
|
||||||
set: (value: string) => {
|
set: (value: string) => {
|
||||||
const start = DateTime.fromISO(newShiftStart.value, { zone: timezone.value });
|
const start = DateTime.fromISO(newShiftStart.value, { zone: accountStore.activeTimezone });
|
||||||
const end = endFromTime(start, value);
|
const end = endFromTime(start, value);
|
||||||
newShiftDuration.value = dropDay(end.diff(start)).toFormat("hh:mm");
|
newShiftDuration.value = dropDay(end.diff(start)).toFormat("hh:mm");
|
||||||
},
|
},
|
||||||
|
@ -478,7 +474,7 @@ function editShiftSlot(
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
if (edits.start) {
|
if (edits.start) {
|
||||||
const start = DateTime.fromISO(edits.start, { zone: timezone.value });
|
const start = DateTime.fromISO(edits.start, { zone: accountStore.activeTimezone });
|
||||||
shiftSlot = {
|
shiftSlot = {
|
||||||
...shiftSlot,
|
...shiftSlot,
|
||||||
start,
|
start,
|
||||||
|
@ -558,7 +554,7 @@ function newShiftSlot(options: { start?: DateTime, end?: DateTime } = {}) {
|
||||||
end = options.end;
|
end = options.end;
|
||||||
start = options.end.minus(duration);
|
start = options.end.minus(duration);
|
||||||
} else {
|
} else {
|
||||||
start = DateTime.fromISO(newShiftStart.value, { zone: timezone.value });
|
start = DateTime.fromISO(newShiftStart.value, { zone: accountStore.activeTimezone });
|
||||||
end = endFromTime(start, newShiftEnd.value);
|
end = endFromTime(start, newShiftEnd.value);
|
||||||
}
|
}
|
||||||
if (!start.isValid || !end.isValid) {
|
if (!start.isValid || !end.isValid) {
|
||||||
|
@ -621,8 +617,8 @@ const shiftSlots = computed(() => {
|
||||||
role: shift.role,
|
role: shift.role,
|
||||||
assigned: slot.assigned ?? [],
|
assigned: slot.assigned ?? [],
|
||||||
origRole: shift.role,
|
origRole: shift.role,
|
||||||
start: DateTime.fromISO(slot.start, { zone: timezone.value }),
|
start: DateTime.fromISO(slot.start, { zone: accountStore.activeTimezone }),
|
||||||
end: DateTime.fromISO(slot.end, { zone: timezone.value }),
|
end: DateTime.fromISO(slot.end, { zone: accountStore.activeTimezone }),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -503,16 +503,14 @@ const stretches = computed(() => [
|
||||||
)
|
)
|
||||||
])
|
])
|
||||||
|
|
||||||
const runtimeConfig = useRuntimeConfig();
|
const accountStore = useAccountStore();
|
||||||
const sessionStore = useSessionStore();
|
|
||||||
const debugTimezone = ref<undefined | string>();
|
|
||||||
const timezone = computed({
|
const timezone = computed({
|
||||||
get: () => debugTimezone.value ?? sessionStore.account?.timezone ?? runtimeConfig.public.defaultTimezone,
|
get: () => accountStore.activeTimezone,
|
||||||
set: (value: string) => { debugTimezone.value = value },
|
set: (value: string) => { accountStore.timezone = value },
|
||||||
});
|
});
|
||||||
|
|
||||||
const elements = computed(() => tableElementsFromStretches(
|
const elements = computed(() => tableElementsFromStretches(
|
||||||
stretches.value, schedule.value.events, schedule.value.locations, schedule.value.rota, schedule.value.roles, timezone.value
|
stretches.value, schedule.value.events, schedule.value.locations, schedule.value.rota, schedule.value.roles, accountStore.activeTimezone
|
||||||
));
|
));
|
||||||
const totalColumns = computed(() => elements.value.totalColumns);
|
const totalColumns = computed(() => elements.value.totalColumns);
|
||||||
const columnGroups = computed(() => elements.value.columnGroups);
|
const columnGroups = computed(() => elements.value.columnGroups);
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
export default defineNuxtRouteMiddleware(async (to, from) => {
|
export default defineNuxtRouteMiddleware(async (to, from) => {
|
||||||
const sessionStore = useSessionStore();
|
const accountStore = useAccountStore();
|
||||||
|
|
||||||
if (!sessionStore.account) {
|
if (!accountStore.valid) {
|
||||||
console.log("Not logged in, redirecting to /login");
|
console.log("Not logged in, redirecting to /login");
|
||||||
return navigateTo("/login");
|
return navigateTo("/login");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
to.meta.allowedAccountTypes
|
to.meta.allowedAccountTypes
|
||||||
&& !to.meta.allowedAccountTypes.includes(sessionStore.account.type)
|
&& !to.meta.allowedAccountTypes.includes(accountStore.type!)
|
||||||
) {
|
) {
|
||||||
throw createError({
|
throw createError({
|
||||||
status: 403,
|
status: 403,
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
<template>
|
<template>
|
||||||
<main>
|
<main>
|
||||||
<h1>Account Settings</h1>
|
<h1>Account Settings</h1>
|
||||||
<p v-if="sessionStore.account?.type !== 'anonymous'">
|
<p v-if="accountStore.type !== 'anonymous'">
|
||||||
Name: {{ sessionStore.account?.name }}
|
Name: {{ accountStore.name }}
|
||||||
</p>
|
</p>
|
||||||
<p>Access: {{ sessionStore.account?.type }}</p>
|
<p>Access: {{ accountStore.type }}</p>
|
||||||
<form @submit.prevent="changeSettings">
|
<form @submit.prevent="changeSettings">
|
||||||
<label>
|
<label>
|
||||||
Timezone
|
Timezone
|
||||||
<input type="text" v-model="timezone">
|
<input type="text" v-model="timezone" :placeholder="accountStore.defaultTimezone">
|
||||||
</label>
|
</label>
|
||||||
<button type="submit">
|
<button type="submit">
|
||||||
Save
|
Save
|
||||||
|
@ -35,8 +35,9 @@ definePageMeta({
|
||||||
});
|
});
|
||||||
|
|
||||||
const sessionStore = useSessionStore();
|
const sessionStore = useSessionStore();
|
||||||
|
const accountStore = useAccountStore();
|
||||||
|
|
||||||
const timezone = ref(sessionStore.account?.timezone ?? "");
|
const timezone = ref(accountStore.timezone ?? "");
|
||||||
|
|
||||||
async function changeSettings() {
|
async function changeSettings() {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
<h2>Locations</h2>
|
<h2>Locations</h2>
|
||||||
<LocationsTable :edit="isAdmin" />
|
<LocationsTable :edit="accountStore.canEditPublic" />
|
||||||
<h2>Schedule</h2>
|
<h2>Schedule</h2>
|
||||||
<label>
|
<label>
|
||||||
Location Filter:
|
Location Filter:
|
||||||
|
@ -77,7 +77,7 @@ definePageMeta({
|
||||||
|
|
||||||
const schedule = await useSchedule();
|
const schedule = await useSchedule();
|
||||||
const { data: accounts } = await useAccounts();
|
const { data: accounts } = await useAccounts();
|
||||||
const sessionStore = useSessionStore();
|
const accountStore = useAccountStore();
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const crewFilter = computed({
|
const crewFilter = computed({
|
||||||
|
@ -91,14 +91,14 @@ const crewFilter = computed({
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
const eventSlotFilter = computed(() => {
|
const eventSlotFilter = computed(() => {
|
||||||
if (crewFilter.value === undefined || !sessionStore.account) {
|
if (crewFilter.value === undefined || !accountStore.valid) {
|
||||||
return () => true;
|
return () => true;
|
||||||
}
|
}
|
||||||
const cid = parseInt(crewFilter.value);
|
const cid = parseInt(crewFilter.value);
|
||||||
return (slot: TimeSlot) => slot.assigned?.some(id => id === cid) || false;
|
return (slot: TimeSlot) => slot.assigned?.some(id => id === cid) || false;
|
||||||
});
|
});
|
||||||
const shiftSlotFilter = computed(() => {
|
const shiftSlotFilter = computed(() => {
|
||||||
if (crewFilter.value === undefined || !sessionStore.account) {
|
if (crewFilter.value === undefined || !accountStore.valid) {
|
||||||
return () => true;
|
return () => true;
|
||||||
}
|
}
|
||||||
const cid = parseInt(crewFilter.value);
|
const cid = parseInt(crewFilter.value);
|
||||||
|
@ -126,6 +126,4 @@ const roleFilter = computed({
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
const isAdmin = computed(() => sessionStore.account?.type === "admin")
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -5,13 +5,13 @@
|
||||||
<li>
|
<li>
|
||||||
<NuxtLink to="/schedule">View Schedule</NuxtLink>
|
<NuxtLink to="/schedule">View Schedule</NuxtLink>
|
||||||
</li>
|
</li>
|
||||||
<li v-if="sessionStore.account?.type === 'admin' || sessionStore.account?.type === 'crew'">
|
<li v-if="accountStore.canEdit">
|
||||||
<NuxtLink to="/edit">Edit Schedule</NuxtLink>
|
<NuxtLink to="/edit">Edit Schedule</NuxtLink>
|
||||||
</li>
|
</li>
|
||||||
<li v-if="sessionStore.account">
|
<li v-if="accountStore.valid">
|
||||||
<NuxtLink to="/account/settings">Account Settings</NuxtLink>
|
<NuxtLink to="/account/settings">Account Settings</NuxtLink>
|
||||||
</li>
|
</li>
|
||||||
<li v-if="!sessionStore.account">
|
<li v-else>
|
||||||
<NuxtLink to="/login">Log In / Create Account</NuxtLink>
|
<NuxtLink to="/login">Log In / Create Account</NuxtLink>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -19,5 +19,5 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
const sessionStore = useSessionStore();
|
const accountStore = useAccountStore();
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<p>
|
<p>
|
||||||
Study carefully, we only hold these events once a year.
|
Study carefully, we only hold these events once a year.
|
||||||
</p>
|
</p>
|
||||||
<p v-if="!sessionStore.account">
|
<p v-if="!accountStore.valid">
|
||||||
<NuxtLink to="/login">Login</NuxtLink> or <NuxtLink to="/login#create-account">Create an account</NuxtLink>
|
<NuxtLink to="/login">Login</NuxtLink> or <NuxtLink to="/login#create-account">Create an account</NuxtLink>
|
||||||
to get notified about updates to the schedule.
|
to get notified about updates to the schedule.
|
||||||
</p>
|
</p>
|
||||||
|
@ -12,7 +12,7 @@
|
||||||
Check out your <NuxtLink to="/account/settings">Account Setting</NuxtLink> to set up notifications for changes to schedule.
|
Check out your <NuxtLink to="/account/settings">Account Setting</NuxtLink> to set up notifications for changes to schedule.
|
||||||
</p>
|
</p>
|
||||||
<h2>Schedule</h2>
|
<h2>Schedule</h2>
|
||||||
<label v-if="sessionStore.account">
|
<label v-if="accountStore.valid">
|
||||||
Filter:
|
Filter:
|
||||||
<select
|
<select
|
||||||
v-model="filter"
|
v-model="filter"
|
||||||
|
@ -26,11 +26,11 @@
|
||||||
:selected='filter === "my-schedule"'
|
:selected='filter === "my-schedule"'
|
||||||
>My Schedule</option>
|
>My Schedule</option>
|
||||||
<option
|
<option
|
||||||
v-if="isCrew"
|
v-if="accountStore.isCrew"
|
||||||
value="assigned"
|
value="assigned"
|
||||||
:selected='filter === "assigned"'
|
:selected='filter === "assigned"'
|
||||||
>Assigned to Me</option>
|
>Assigned to Me</option>
|
||||||
<optgroup v-if="isCrew && accounts" label="Crew">
|
<optgroup v-if="accountStore.isCrew && accounts" label="Crew">
|
||||||
<option
|
<option
|
||||||
v-for="account in accounts.filter(a => a.type === 'crew' || a.type === 'admin')"
|
v-for="account in accounts.filter(a => a.type === 'crew' || a.type === 'admin')"
|
||||||
:key="account.id"
|
:key="account.id"
|
||||||
|
@ -57,13 +57,9 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { ShiftSlot, TimeSlot } from '~/shared/types/schedule';
|
import type { ShiftSlot, TimeSlot } from '~/shared/types/schedule';
|
||||||
|
|
||||||
const sessionStore = useSessionStore();
|
const accountStore = useAccountStore();
|
||||||
const { data: accounts } = await useAccounts();
|
const { data: accounts } = await useAccounts();
|
||||||
const schedule = await useSchedule();
|
const schedule = await useSchedule();
|
||||||
const isCrew = computed(() => (
|
|
||||||
sessionStore.account?.type === "crew"
|
|
||||||
|| sessionStore.account?.type === "admin"
|
|
||||||
));
|
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const filter = computed({
|
const filter = computed({
|
||||||
|
@ -78,12 +74,12 @@ const filter = computed({
|
||||||
});
|
});
|
||||||
|
|
||||||
const eventSlotFilter = computed(() => {
|
const eventSlotFilter = computed(() => {
|
||||||
if (filter.value === undefined || !sessionStore.account) {
|
if (filter.value === undefined || !accountStore.valid) {
|
||||||
return () => true;
|
return () => true;
|
||||||
}
|
}
|
||||||
const aid = sessionStore.account?.id;
|
const aid = accountStore.id;
|
||||||
if (filter.value === "my-schedule") {
|
if (filter.value === "my-schedule") {
|
||||||
const ids = new Set(sessionStore.account?.interestedIds);
|
const ids = new Set(accountStore.interestedIds);
|
||||||
for (const event of schedule.value.events) {
|
for (const event of schedule.value.events) {
|
||||||
if (ids.has(event.id)) {
|
if (ids.has(event.id)) {
|
||||||
for (const slot of event.slots) {
|
for (const slot of event.slots) {
|
||||||
|
@ -103,11 +99,11 @@ const eventSlotFilter = computed(() => {
|
||||||
return () => false;
|
return () => false;
|
||||||
});
|
});
|
||||||
const shiftSlotFilter = computed(() => {
|
const shiftSlotFilter = computed(() => {
|
||||||
if (filter.value === undefined || !sessionStore.account) {
|
if (filter.value === undefined || !accountStore.valid) {
|
||||||
return () => true;
|
return () => true;
|
||||||
}
|
}
|
||||||
if (filter.value === "my-schedule" || filter.value === "assigned") {
|
if (filter.value === "my-schedule" || filter.value === "assigned") {
|
||||||
const aid = sessionStore.account?.id;
|
const aid = accountStore.id;
|
||||||
return (slot: ShiftSlot) => slot.assigned?.some(id => id === aid) || false;
|
return (slot: ShiftSlot) => slot.assigned?.some(id => id === aid) || false;
|
||||||
}
|
}
|
||||||
if (filter.value.startsWith("crew-")) {
|
if (filter.value.startsWith("crew-")) {
|
||||||
|
|
60
stores/account.ts
Normal file
60
stores/account.ts
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
import type { Account } from "~/shared/types/account";
|
||||||
|
|
||||||
|
export const useAccountStore = defineStore("account", () => {
|
||||||
|
const runtimeConfig = useRuntimeConfig();
|
||||||
|
const sessionStore = useSessionStore();
|
||||||
|
const state = {
|
||||||
|
valid: ref<boolean>(false),
|
||||||
|
id: ref<number>(),
|
||||||
|
name: ref<string>(),
|
||||||
|
timezone: ref<string>(),
|
||||||
|
type: ref<Account["type"]>(),
|
||||||
|
interestedIds: ref<Set<string>>(),
|
||||||
|
};
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
const account = sessionStore.account;
|
||||||
|
state.valid.value = Boolean(account);
|
||||||
|
state.id.value = account?.id;
|
||||||
|
state.name.value = account?.name;
|
||||||
|
state.timezone.value = account?.timezone;
|
||||||
|
state.type.value = account?.type;
|
||||||
|
state.interestedIds.value = account?.interestedIds ? new Set(account.interestedIds) : undefined;
|
||||||
|
});
|
||||||
|
|
||||||
|
const getters = {
|
||||||
|
isCrew: computed(() => state.type.value === "crew" || state.type.value === "admin"),
|
||||||
|
canEdit: computed(() => state.type.value === "admin" || state.type.value === "crew" ),
|
||||||
|
canEditPublic: computed(() => state.type.value === "admin"),
|
||||||
|
activeTimezone: computed(() => state.timezone.value || runtimeConfig.public.defaultTimezone),
|
||||||
|
defaultTimezone: computed(() => runtimeConfig.public.defaultTimezone),
|
||||||
|
};
|
||||||
|
|
||||||
|
const actions = {
|
||||||
|
async toggleInterestedId(id: string, slotIds?: string[]) {
|
||||||
|
if (!state.interestedIds.value) {
|
||||||
|
throw Error("accountStore.toggleInterestedId: Invalid state")
|
||||||
|
}
|
||||||
|
let newIds = [...state.interestedIds.value ?? []];
|
||||||
|
if (state.interestedIds.value.has(id)) {
|
||||||
|
newIds = newIds.filter(newId => newId !== id);
|
||||||
|
} else {
|
||||||
|
newIds.push(id);
|
||||||
|
if (slotIds) {
|
||||||
|
const filterIds = new Set(slotIds);
|
||||||
|
newIds = newIds.filter(newId => !filterIds.has(newId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await $fetch("/api/account", {
|
||||||
|
method: "PATCH",
|
||||||
|
body: { interestedIds: newIds },
|
||||||
|
})
|
||||||
|
await sessionStore.fetch();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
...getters,
|
||||||
|
...actions,
|
||||||
|
};
|
||||||
|
});
|
Loading…
Add table
Add a link
Reference in a new issue