Many changes to try to improve reliability

This commit is contained in:
2024-05-17 18:17:25 -04:00
parent dd631b71bb
commit b506ab7ca9
11 changed files with 218 additions and 134 deletions

20
docs/time.md Normal file
View File

@@ -0,0 +1,20 @@
# Dealing with Time
Dealing with time sucks, okay? We have three different formats we need to deal with:
1. ES Date - The native ECMAScript Date object. This is saddled with all the legacy of the decades. Hopefully, we will be able to retire this one day... Ref: https://tc39.es/proposal-temporal/docs/index.html
2. ISO 8601 Date - Used by Appwrite backend. This is just a string, but can represent any date, with or without a timezone.
3. Timestamp - Used internally by QCalendar.
We can't just use one format. We need ISO8601 format for Appwrite, and we get passed Timestamp objects by QCalendar. In the middle of that, we need ES Date objects to do some underlying math.
Componentization:
In order to make things clean and modular, we will rely on Timestamp as the main format in a component.
In data that comes from, or goes to the backend, we will store absolute dates in ISO format.
For any user-facing dates / times, the data will be rendered in the users local time.
For time-only data (as used in Intervals, eg: '09:00'), the template will be stored as a string of 'hh:mm', and represent the users local time. We may want to change this in the future, as this could prove a problem when a user is travelling, but wants to apply a template to their home location.
For now, we'll use the Timestamp object provided by QCalendar. We might need to refactor this in the future.

View File

@@ -107,7 +107,6 @@ import {
QCalendarResource, QCalendarResource,
TimestampOrNull, TimestampOrNull,
today, today,
parseDate,
parseTimestamp, parseTimestamp,
addToDate, addToDate,
Timestamp, Timestamp,
@@ -172,8 +171,8 @@ function monthFormatter() {
} }
function getEvents(scope: ResourceIntervalScope) { function getEvents(scope: ResourceIntervalScope) {
const resourceEvents = reservationStore.getBoatReservations( const resourceEvents = reservationStore.getReservationsByDate(
parseDate(date.extractDate(selectedDate.value, 'YYYY-MM-DD')) as Timestamp, selectedDate.value,
scope.resource.$id scope.resource.$id
); );

View File

@@ -70,6 +70,7 @@ import {
parseTimestamp, parseTimestamp,
parseDate, parseDate,
addToDate, addToDate,
getDate,
} from '@quasar/quasar-ui-qcalendar'; } from '@quasar/quasar-ui-qcalendar';
import CalendarHeaderComponent from './CalendarHeaderComponent.vue'; import CalendarHeaderComponent from './CalendarHeaderComponent.vue';
@@ -189,13 +190,13 @@ function getBoatBlocks(scope: DayBodyScope): Interval[] {
function getBoatReservations(scope: DayBodyScope): Reservation[] { function getBoatReservations(scope: DayBodyScope): Reservation[] {
const boat = boats.value[scope.columnIndex]; const boat = boats.value[scope.columnIndex];
return boat return boat
? reservationStore.getBoatReservations(scope.timestamp, boat.$id) ? reservationStore.getReservationsByDate(getDate(scope.timestamp), boat.$id)
: []; : [];
} }
// function changeEvent({ start }: { start: string }) { // function changeEvent({ start }: { start: string }) {
// const newBlocks = scheduleStore.getIntervalsForDate(start); // const newBlocks = scheduleStore.getIntervalsForDate(start);
// const reservations = scheduleStore.getBoatReservations( // const reservations = scheduleStore.getReservationsByDate(
// parsed(start) as Timestamp // parsed(start) as Timestamp
// ); // );
// boats.value.map((boat) => { // boats.value.map((boat) => {
@@ -268,4 +269,7 @@ const disabledBefore = computed(() => {
font-size: 0.8em font-size: 0.8em
.q-calendar-day__day.q-current-day .q-calendar-day__day.q-current-day
padding: 1px padding: 1px
.q-calendar-day__head--days__column
background: $primary
color: white
</style> </style>

View File

@@ -56,7 +56,12 @@ import { useReservationStore } from 'src/stores/reservation';
import { Reservation } from 'src/stores/schedule.types'; import { Reservation } from 'src/stores/schedule.types';
import { ref } from 'vue'; import { ref } from 'vue';
const reservationStore = useReservationStore(); const reservationStore = useReservationStore();
import { TimestampOrNull, parsed, today } from '@quasar/quasar-ui-qcalendar'; import {
TimestampOrNull,
getDate,
parsed,
today,
} from '@quasar/quasar-ui-qcalendar';
import { QCalendarDay } from '@quasar/quasar-ui-qcalendar'; import { QCalendarDay } from '@quasar/quasar-ui-qcalendar';
import { date } from 'quasar'; import { date } from 'quasar';
import { Timestamp } from '@quasar/quasar-ui-qcalendar'; import { Timestamp } from '@quasar/quasar-ui-qcalendar';
@@ -87,7 +92,7 @@ function slotStyle(
} }
function reservationEvents(timestamp: Timestamp) { function reservationEvents(timestamp: Timestamp) {
return reservationStore.getBoatReservations(timestamp); return reservationStore.getReservationsByDate(getDate(timestamp));
} }
function onMoved(data: Event) { function onMoved(data: Event) {
console.log('onMoved', data); console.log('onMoved', data);

View File

@@ -23,7 +23,7 @@
> >
<template #day="{ scope }"> <template #day="{ scope }">
<div <div
v-if="getIntervals(scope.timestamp, scope.resource)" v-if="filteredIntervals(scope.timestamp, scope.resource).length"
style=" style="
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
@@ -33,21 +33,22 @@
" "
> >
<template <template
v-for="block in getIntervals( v-for="block in sortedIntervals(
scope.timestamp, scope.timestamp,
scope.resource scope.resource
).sort((a, b) => Date.parse(a.start) - Date.parse(b.start))" )"
:key="block.id" :key="block.id"
> >
<q-chip class="cursor-pointer"> <q-chip class="cursor-pointer">
{{ date.formatDate(block.start, 'HH:mm') }} - {{ date.formatDate(block.start, 'HH:mm') }} -
{{ date.formatDate(block.end, 'HH:mm') }} {{ date.formatDate(block.end, 'HH:mm') }}
<q-popup-edit <!-- <q-popup-edit
:model-value="block" :model-value="block"
v-slot="scope" v-slot="scope"
buttons buttons
@save="updateInterval(block)" @save="saveInterval"
> >
TODO: Why isn't this saving?
<q-input <q-input
:model-value="date.formatDate(scope.value.start, 'HH:mm')" :model-value="date.formatDate(scope.value.start, 'HH:mm')"
dense dense
@@ -56,13 +57,14 @@
label="start" label="start"
@keyup.enter="scope.set" @keyup.enter="scope.set"
@update:model-value=" @update:model-value="
(t) => (t) => {
(block.start = buildISODate( block.start = new Date(
date.formatDate(scope.value.start, 'YYYY-MM-DD'),t as string scope.value.start.split('T')[0] + 'T' + t
)) ).toISOString();
}
" "
/> />
<!-- TODO: Clean this up --> TODO: Clean this up
<q-input <q-input
:model-value="date.formatDate(scope.value.end, 'HH:mm')" :model-value="date.formatDate(scope.value.end, 'HH:mm')"
dense dense
@@ -71,12 +73,12 @@
@keyup.enter="scope.set" @keyup.enter="scope.set"
@update:model-value=" @update:model-value="
(t) => (t) =>
(block.end = buildISODate( (block.end = new Date(
date.formatDate(scope.value.end, 'YYYY-MM-DD'),t as string scope.value.end.split('T')[0] + 'T' + t
)) ).toISOString())
" "
/> />
</q-popup-edit> </q-chip </q-popup-edit>--> </q-chip
><q-btn ><q-btn
size="xs" size="xs"
icon="delete" icon="delete"
@@ -89,7 +91,7 @@
</q-calendar-scheduler> </q-calendar-scheduler>
</div> </div>
</div> </div>
<div class="q-pa-md" style="width: 400"> <div class="q-pa-md" style="width: 400px">
<q-list padding bordered class="rounded-borders"> <q-list padding bordered class="rounded-borders">
<q-item> <q-item>
<q-item-section> <q-item-section>
@@ -125,8 +127,12 @@
<div class="text-h6">Warning!</div> <div class="text-h6">Warning!</div>
</q-card-section> </q-card-section>
<q-card-section class="q-pt-none"> <q-card-section class="q-pt-none">
This will overwrite existing blocks! Conflicting times! Please delete overlapped items!
{{ overlapped }} <q-chip v-for="item in overlapped" :key="item.index"
>{{ boats.find((b) => b.$id === item.boatId)?.name }}:
{{ date.formatDate(item.start, 'hh:mm') }} -
{{ date.formatDate(item.end, 'hh:mm') }}
</q-chip>
</q-card-section> </q-card-section>
<q-card-actions align="right"> <q-card-actions align="right">
<q-btn flat label="OK" color="primary" v-close-popup /> <q-btn flat label="OK" color="primary" v-close-popup />
@@ -147,7 +153,6 @@ import {
useScheduleStore, useScheduleStore,
} from 'src/stores/schedule'; } from 'src/stores/schedule';
import { onMounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
import { buildISODate } from 'src/utils/misc';
import type { import type {
Interval, Interval,
IntervalTemplate, IntervalTemplate,
@@ -160,19 +165,17 @@ import { storeToRefs } from 'pinia';
const selectedDate = ref(today()); const selectedDate = ref(today());
const { fetchBoats } = useBoatStore(); const { fetchBoats } = useBoatStore();
const { getIntervals, fetchIntervals, updateInterval, fetchIntervalTemplates } = const scheduleStore = useScheduleStore();
useScheduleStore();
const { boats } = storeToRefs(useBoatStore()); const { boats } = storeToRefs(useBoatStore());
const { timeblockTemplates } = storeToRefs(useScheduleStore()); const { timeblockTemplates } = storeToRefs(useScheduleStore());
const calendar = ref(); const calendar = ref();
const overlapped = ref(); const overlapped = ref();
const blankTemplate: IntervalTemplate = { const alert = ref(false);
const newTemplate = ref<IntervalTemplate>({
$id: '', $id: '',
name: 'NewTemplate', name: 'NewTemplate',
timeTuples: [['09:00', '12:00']], timeTuples: [['09:00', '12:00']],
}; });
const newTemplate = ref<IntervalTemplate>({ ...blankTemplate });
const alert = ref(false);
/* TODOS: /* TODOS:
* Need more validation: * Need more validation:
@@ -183,20 +186,37 @@ const alert = ref(false);
onMounted(async () => { onMounted(async () => {
await fetchBoats(); await fetchBoats();
await fetchIntervals(); await scheduleStore.fetchIntervals();
await fetchIntervalTemplates(); await scheduleStore.fetchIntervalTemplates();
}); });
const filteredIntervals = (date: Timestamp, boat: Boat) => {
return scheduleStore.getIntervals(date, boat).value;
};
const sortedIntervals = (date: Timestamp, boat: Boat) => {
return filteredIntervals(date, boat).sort(
(a, b) => Date.parse(a.start) - Date.parse(b.start)
);
};
function resetNewTemplate() { function resetNewTemplate() {
newTemplate.value = { ...blankTemplate }; newTemplate.value = {
$id: 'unsaved',
name: 'NewTemplate',
timeTuples: [['09:00', '12:00']],
};
} }
function createTemplate() { function createTemplate() {
newTemplate.value.$id = 'unsaved'; newTemplate.value.$id = 'unsaved';
} }
function createIntervals(boat: Boat, templateId: string, date: string) { function createIntervals(boat: Boat, templateId: string, date: string) {
timeBlocksFromTemplate(boat, templateId, date)?.map((block) => const intervals = timeBlocksFromTemplate(boat, templateId, date);
useScheduleStore().createInterval(block) intervals.forEach((interval) => scheduleStore.createInterval(interval));
); }
function getIntervals(date: Timestamp, boat: Boat) {
return scheduleStore.getIntervals(date, boat);
} }
function timeBlocksFromTemplate( function timeBlocksFromTemplate(
@@ -204,25 +224,28 @@ function timeBlocksFromTemplate(
templateId: string, templateId: string,
date: string date: string
): Interval[] { ): Interval[] {
const timeBlock = timeblockTemplates.value.find((t) => t.$id === templateId); const template = timeblockTemplates.value.find((t) => t.$id === templateId);
return ( return template
timeBlock?.timeTuples.map((tb: TimeTuple) => ? template.timeTuples.map((timeTuple: TimeTuple) =>
buildInterval(boat, tb, date) buildInterval(boat, timeTuple, date)
) || [] )
); : [];
} }
function deleteBlock(block: Interval) { function deleteBlock(block: Interval) {
if (block.$id) { if (block.$id) {
useScheduleStore().deleteInterval(block.$id); scheduleStore.deleteInterval(block.$id);
} }
return false;
} }
function onDragEnter(e: DragEvent, type: string) { function onDragEnter(e: DragEvent, type: string) {
if (type === 'day' || type === 'head-day') { if (type === 'day' || type === 'head-day') {
e.preventDefault(); e.preventDefault();
if (e.target instanceof HTMLDivElement) if (
e.target instanceof HTMLDivElement &&
(e.target.classList.contains('q-calendar-scheduler__head--day') ||
e.target.classList.contains('q-calendar-scheduler__day'))
)
e.target.classList.add('bg-secondary'); e.target.classList.add('bg-secondary');
} }
} }
@@ -230,16 +253,18 @@ function onDragEnter(e: DragEvent, type: string) {
function onDragOver(e: DragEvent, type: string) { function onDragOver(e: DragEvent, type: string) {
if (type === 'day' || type === 'head-day') { if (type === 'day' || type === 'head-day') {
e.preventDefault(); e.preventDefault();
return true;
} }
} }
function onDragLeave(e: DragEvent, type: string) { function onDragLeave(e: DragEvent, type: string) {
if (type === 'day' || type === 'head-day') { if (type === 'day' || type === 'head-day') {
e.preventDefault(); e.preventDefault();
if (e.target instanceof HTMLDivElement) if (
e.target instanceof HTMLDivElement &&
(e.target.classList.contains('q-calendar-scheduler__head--day') ||
e.target.classList.contains('q-calendar-scheduler__day'))
)
e.target.classList.remove('bg-secondary'); e.target.classList.remove('bg-secondary');
return false;
} }
} }
@@ -249,34 +274,28 @@ function onDrop(
type: string, type: string,
scope: { resource: Boat; timestamp: Timestamp } scope: { resource: Boat; timestamp: Timestamp }
) { ) {
if (e.target instanceof HTMLDivElement)
e.target.classList.remove('bg-secondary');
if ((type === 'day' || type === 'head-day') && e.dataTransfer) { if ((type === 'day' || type === 'head-day') && e.dataTransfer) {
const templateId = e.dataTransfer.getData('ID'); const templateId = e.dataTransfer.getData('ID');
const date = scope.timestamp.date; const date = scope.timestamp.date;
if (type === 'head-day') { const resource = scope.resource;
overlapped.value = boats.value.map((boat) => const existingIntervals = getIntervals(scope.timestamp, resource).value;
const boatsToApply = type === 'head-day' ? boats.value : [resource];
overlapped.value = boatsToApply
.map((boat) =>
blocksOverlapped( blocksOverlapped(
getIntervals(scope.timestamp, boat).concat( existingIntervals.concat(
timeBlocksFromTemplate(boat, templateId, date) timeBlocksFromTemplate(boat, templateId, date)
) )
) )
); )
if (overlapped.value.length === 0) { .flat(1);
boats.value.map((b) => createIntervals(b, templateId, date)); if (overlapped.value.length === 0) {
} else { boatsToApply.map((b) => createIntervals(b, templateId, date));
alert.value = true;
}
} else { } else {
overlapped.value = blocksOverlapped( alert.value = true;
getIntervals(scope.timestamp, scope.resource).concat(
timeBlocksFromTemplate(scope.resource, templateId, date)
)
);
if (overlapped.value.length === 0) {
createIntervals(scope.resource, templateId, date);
} else {
alert.value = true;
}
} }
} }
if (e.target instanceof HTMLDivElement) if (e.target instanceof HTMLDivElement)

View File

@@ -44,12 +44,16 @@ export const useAuthStore = defineStore('auth', () => {
'/userinfo/' + id, '/userinfo/' + id,
ExecutionMethod.GET ExecutionMethod.GET
) )
.then( .then((res) => {
(res) => (userNames.value[id] = JSON.parse(res.responseBody).name) if (res.responseBody) {
); userNames.value[id] = JSON.parse(res.responseBody).name;
} else {
console.error(res, id);
}
});
} }
} catch (e) { } catch (e) {
console.log('Failed to get username. Error: ' + e); console.error('Failed to get username. Error: ' + e);
} }
return userNames.value[id]; return userNames.value[id];
} }

View File

@@ -2,17 +2,20 @@ import { defineStore } from 'pinia';
import type { Reservation } from './schedule.types'; import type { Reservation } from './schedule.types';
import { ref } from 'vue'; import { ref } from 'vue';
import { AppwriteIds, databases } from 'src/boot/appwrite'; import { AppwriteIds, databases } from 'src/boot/appwrite';
import { Timestamp, parsed } from '@quasar/quasar-ui-qcalendar'; import { Query } from 'appwrite';
import { date } from 'quasar';
import { Timestamp, parseDate } from '@quasar/quasar-ui-qcalendar';
export const useReservationStore = defineStore('reservation', () => { export const useReservationStore = defineStore('reservation', () => {
const reservations = ref<Reservation[]>([]); const reservations = ref<Map<string, Reservation>>(new Map());
const datesLoaded = ref<Record<string, boolean>>({});
const getConflictingReservations = ( const getConflictingReservations = (
resource: string, resource: string,
start: Date, start: Date,
end: Date end: Date
): Reservation[] => { ): Reservation[] => {
const overlapped = reservations.value.filter( const overlapped = Array.from(reservations.value.values()).filter(
(entry: Reservation) => (entry: Reservation) =>
entry.resource == resource && entry.resource == resource &&
new Date(entry.start) < end && new Date(entry.start) < end &&
@@ -37,46 +40,75 @@ export const useReservationStore = defineStore('reservation', () => {
); );
}; };
const addOrCreateReservation = (reservation: Reservation) => { function setDateLoaded(start: Date, end: Date, state: boolean) {
const index = reservations.value.findIndex( let curDate = start;
(res) => res.id == reservation.id while (curDate < end) {
); datesLoaded.value[(parseDate(curDate) as Timestamp).date] = state;
index != -1 curDate = date.addToDate(curDate, { days: 1 });
? (reservations.value[index] = reservation) }
: reservations.value.push(reservation); }
}; async function fetchReservationsForDateRange(start: string, end?: string) {
async function fetchReservations() { const startDate = new Date(start + 'T00:00');
const endDate = new Date(end || start + 'T23:59');
datesLoaded.value[start] = false;
if (end) {
setDateLoaded(startDate, endDate, false);
}
try { try {
const response = await databases.listDocuments( databases
AppwriteIds.databaseId, .listDocuments(
AppwriteIds.collection.reservation AppwriteIds.databaseId,
); AppwriteIds.collection.reservation,
reservations.value = response.documents as Reservation[]; [
Query.and([
Query.greaterThanEqual('end', startDate.toISOString()),
Query.lessThanEqual('start', endDate.toISOString()),
]),
]
)
.then((response) => {
response.documents.forEach((d) =>
reservations.value.set(d.$id, d as Reservation)
);
setDateLoaded(startDate, endDate, true);
});
} catch (error) { } catch (error) {
console.error('Failed to fetch timeblocks', error); console.error('Failed to fetch reservations', error);
} }
} }
const getBoatReservations = ( async function fetchReservations() {
searchDate: Timestamp, return;
// fetchReservationsForDateRange(
// today(),
// date.addToDate(today(), { days: 7 }).toLocaleDateString()
// );
}
const getReservationsByDate = (
searchDate: string,
boat?: string boat?: string
): Reservation[] => { ): Reservation[] => {
const result = reservations.value.filter((x) => { if (!datesLoaded.value[searchDate]) {
return ( fetchReservationsForDateRange(searchDate);
((parsed(x.start)?.date == searchDate.date || }
parsed(x.end)?.date == searchDate.date) && // Part of reservation falls on day const result = Array.from(reservations.value.values()).filter((x) => {
x.resource != undefined && // A boat is defined return new Date(x.start) < new Date(searchDate + 'T' + '23:59') &&
!boat) || new Date(x.end) > new Date(searchDate + 'T' + '00:00') && // Part of reservation falls on day
x.resource == boat // A specific boat has been passed, and matches boat
); ? boat === x.boat
: true; // A specific boat has been passed, and matches
}); });
console.log(result);
return result; return result;
}; };
return { return {
getBoatReservations, getReservationsByDate,
fetchReservations, fetchReservations,
addOrCreateReservation,
isReservationOverlapped, isReservationOverlapped,
isResourceTimeOverlapped, isResourceTimeOverlapped,
getConflictingReservations, getConflictingReservations,

View File

@@ -1,5 +1,5 @@
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { ref } from 'vue'; import { ComputedRef, computed, ref } from 'vue';
import { Boat } from './boat'; import { Boat } from './boat';
import { import {
Timestamp, Timestamp,
@@ -11,7 +11,6 @@ import {
import { IntervalTemplate, TimeTuple, Interval } from './schedule.types'; import { IntervalTemplate, TimeTuple, Interval } from './schedule.types';
import { AppwriteIds, databases } from 'src/boot/appwrite'; import { AppwriteIds, databases } from 'src/boot/appwrite';
import { ID, Models } from 'appwrite'; import { ID, Models } from 'appwrite';
import { buildISODate } from 'src/utils/misc';
export function arrayToTimeTuples(arr: string[]) { export function arrayToTimeTuples(arr: string[]) {
const timeTuples: TimeTuple[] = []; const timeTuples: TimeTuple[] = [];
@@ -70,28 +69,34 @@ export function buildInterval(
as a UTC time and date-time forms are interpreted as local time. */ as a UTC time and date-time forms are interpreted as local time. */
const result = { const result = {
boatId: resource.$id, boatId: resource.$id,
start: buildISODate(blockDate, time[0]), start: new Date(blockDate + 'T' + time[0]).toISOString(),
end: buildISODate(blockDate, time[1]), end: new Date(blockDate + 'T' + time[1]).toISOString(),
}; };
return result; return result;
} }
export const useScheduleStore = defineStore('schedule', () => { export const useScheduleStore = defineStore('schedule', () => {
// TODO: Implement functions to dynamically pull this data. // TODO: Implement functions to dynamically pull this data.
const timeblocks = ref<Interval[]>([]); const intervals = ref<Interval[]>([]);
const timeblockTemplates = ref<IntervalTemplate[]>([]); const intervalTemplates = ref<IntervalTemplate[]>([]);
const getIntervals = (date: Timestamp, boat: Boat): Interval[] => { const getIntervals = (
return timeblocks.value.filter((block) => { date: Timestamp,
return ( boat: Boat
compareDate(parseDate(new Date(block.start)) as Timestamp, date) && ): ComputedRef<Interval[]> => {
block.boatId === boat.$id return computed(() =>
); intervals.value.filter((block) => {
}); return (
compareDate(parseDate(new Date(block.start)) as Timestamp, date) &&
block.boatId === boat.$id
);
})
);
}; };
const getIntervalsForDate = (date: string): Interval[] => { const getIntervalsForDate = (date: string): Interval[] => {
// TODO: This needs to actually make sure we have the dates we need, stay in sync, etc. // TODO: This needs to actually make sure we have the dates we need, stay in sync, etc.
return timeblocks.value.filter((b) => { return intervals.value.filter((b) => {
return compareDate( return compareDate(
parseDate(new Date(b.start)) as Timestamp, parseDate(new Date(b.start)) as Timestamp,
parsed(date) as Timestamp parsed(date) as Timestamp
@@ -105,7 +110,7 @@ export const useScheduleStore = defineStore('schedule', () => {
AppwriteIds.databaseId, AppwriteIds.databaseId,
AppwriteIds.collection.timeBlock AppwriteIds.collection.timeBlock
); );
timeblocks.value = response.documents as Interval[]; intervals.value = response.documents as Interval[];
} catch (error) { } catch (error) {
console.error('Failed to fetch timeblocks', error); console.error('Failed to fetch timeblocks', error);
} }
@@ -117,7 +122,7 @@ export const useScheduleStore = defineStore('schedule', () => {
AppwriteIds.databaseId, AppwriteIds.databaseId,
AppwriteIds.collection.timeBlockTemplate AppwriteIds.collection.timeBlockTemplate
); );
timeblockTemplates.value = response.documents.map( intervalTemplates.value = response.documents.map(
(d: Models.Document): IntervalTemplate => { (d: Models.Document): IntervalTemplate => {
return { return {
...d, ...d,
@@ -153,7 +158,7 @@ export const useScheduleStore = defineStore('schedule', () => {
ID.unique(), ID.unique(),
interval interval
); );
timeblocks.value.push(response as Interval); intervals.value.push(response as Interval);
} catch (e) { } catch (e) {
console.error('Error creating Interval: ' + e); console.error('Error creating Interval: ' + e);
} }
@@ -167,7 +172,8 @@ export const useScheduleStore = defineStore('schedule', () => {
interval.$id, interval.$id,
{ ...interval, $id: undefined } { ...interval, $id: undefined }
); );
timeblocks.value.push(response as Interval); intervals.value.push(response as Interval);
console.log(`Saved Interval: ${interval.$id}`);
} else { } else {
console.error('Update interval called without an ID'); console.error('Update interval called without an ID');
} }
@@ -182,7 +188,7 @@ export const useScheduleStore = defineStore('schedule', () => {
AppwriteIds.collection.timeBlock, AppwriteIds.collection.timeBlock,
id id
); );
timeblocks.value = timeblocks.value.filter((block) => block.$id !== id); intervals.value = intervals.value.filter((block) => block.$id !== id);
} catch (e) { } catch (e) {
console.error('Error deleting Interval: ' + e); console.error('Error deleting Interval: ' + e);
} }
@@ -195,7 +201,7 @@ export const useScheduleStore = defineStore('schedule', () => {
ID.unique(), ID.unique(),
{ name: template.name, timeTuple: template.timeTuples.flat(2) } { name: template.name, timeTuple: template.timeTuples.flat(2) }
); );
timeblockTemplates.value.push(response as IntervalTemplate); intervalTemplates.value.push(response as IntervalTemplate);
} catch (e) { } catch (e) {
console.error('Error updating IntervalTemplate: ' + e); console.error('Error updating IntervalTemplate: ' + e);
} }
@@ -207,7 +213,7 @@ export const useScheduleStore = defineStore('schedule', () => {
AppwriteIds.collection.timeBlockTemplate, AppwriteIds.collection.timeBlockTemplate,
id id
); );
timeblockTemplates.value = timeblockTemplates.value.filter( intervalTemplates.value = intervalTemplates.value.filter(
(template) => template.$id !== id (template) => template.$id !== id
); );
} catch (e) { } catch (e) {
@@ -228,7 +234,7 @@ export const useScheduleStore = defineStore('schedule', () => {
timeTuple: template.timeTuples.flat(2), timeTuple: template.timeTuples.flat(2),
} }
); );
timeblockTemplates.value = timeblockTemplates.value.map((b) => intervalTemplates.value = intervalTemplates.value.map((b) =>
b.$id !== id b.$id !== id
? b ? b
: ({ : ({
@@ -242,8 +248,8 @@ export const useScheduleStore = defineStore('schedule', () => {
}; };
return { return {
timeblocks, timeblocks: intervals,
timeblockTemplates, timeblockTemplates: intervalTemplates,
getIntervalsForDate, getIntervalsForDate,
getIntervals, getIntervals,
fetchIntervals, fetchIntervals,

View File

@@ -3,8 +3,8 @@ import { Models } from 'appwrite';
export type StatusTypes = 'tentative' | 'confirmed' | 'pending' | undefined; export type StatusTypes = 'tentative' | 'confirmed' | 'pending' | undefined;
export type Reservation = Partial<Models.Document> & { export type Reservation = Partial<Models.Document> & {
user: string; user: string;
start: string; // ISODate start: string;
end: string; //ISODate end: string;
resource: string; // Boat ID resource: string; // Boat ID
status?: StatusTypes; status?: StatusTypes;
}; };

View File

@@ -151,7 +151,6 @@ export const useTaskStore = defineStore('tasks', {
const result = state.tasks.filter((task) => const result = state.tasks.filter((task) =>
task.title.toLowerCase().includes(searchQuery.toLowerCase()) task.title.toLowerCase().includes(searchQuery.toLowerCase())
); );
console.log(result);
return result; return result;
}, },
}, },

View File

@@ -1,7 +1,3 @@
export function buildISODate(date: string, time: string | null): string {
return new Date(date + 'T' + time || '00:00').toISOString();
}
export function getNewId(): string { export function getNewId(): string {
return [...Array(20)] return [...Array(20)]
.map(() => Math.floor(Math.random() * 16).toString(16)) .map(() => Math.floor(Math.random() * 16).toString(16))