Many changes to try to improve reliability
This commit is contained in:
20
docs/time.md
Normal file
20
docs/time.md
Normal 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.
|
||||
@@ -107,7 +107,6 @@ import {
|
||||
QCalendarResource,
|
||||
TimestampOrNull,
|
||||
today,
|
||||
parseDate,
|
||||
parseTimestamp,
|
||||
addToDate,
|
||||
Timestamp,
|
||||
@@ -172,8 +171,8 @@ function monthFormatter() {
|
||||
}
|
||||
|
||||
function getEvents(scope: ResourceIntervalScope) {
|
||||
const resourceEvents = reservationStore.getBoatReservations(
|
||||
parseDate(date.extractDate(selectedDate.value, 'YYYY-MM-DD')) as Timestamp,
|
||||
const resourceEvents = reservationStore.getReservationsByDate(
|
||||
selectedDate.value,
|
||||
scope.resource.$id
|
||||
);
|
||||
|
||||
|
||||
@@ -70,6 +70,7 @@ import {
|
||||
parseTimestamp,
|
||||
parseDate,
|
||||
addToDate,
|
||||
getDate,
|
||||
} from '@quasar/quasar-ui-qcalendar';
|
||||
import CalendarHeaderComponent from './CalendarHeaderComponent.vue';
|
||||
|
||||
@@ -189,13 +190,13 @@ function getBoatBlocks(scope: DayBodyScope): Interval[] {
|
||||
function getBoatReservations(scope: DayBodyScope): Reservation[] {
|
||||
const boat = boats.value[scope.columnIndex];
|
||||
return boat
|
||||
? reservationStore.getBoatReservations(scope.timestamp, boat.$id)
|
||||
? reservationStore.getReservationsByDate(getDate(scope.timestamp), boat.$id)
|
||||
: [];
|
||||
}
|
||||
|
||||
// function changeEvent({ start }: { start: string }) {
|
||||
// const newBlocks = scheduleStore.getIntervalsForDate(start);
|
||||
// const reservations = scheduleStore.getBoatReservations(
|
||||
// const reservations = scheduleStore.getReservationsByDate(
|
||||
// parsed(start) as Timestamp
|
||||
// );
|
||||
// boats.value.map((boat) => {
|
||||
@@ -268,4 +269,7 @@ const disabledBefore = computed(() => {
|
||||
font-size: 0.8em
|
||||
.q-calendar-day__day.q-current-day
|
||||
padding: 1px
|
||||
.q-calendar-day__head--days__column
|
||||
background: $primary
|
||||
color: white
|
||||
</style>
|
||||
|
||||
@@ -56,7 +56,12 @@ import { useReservationStore } from 'src/stores/reservation';
|
||||
import { Reservation } from 'src/stores/schedule.types';
|
||||
import { ref } from 'vue';
|
||||
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 { date } from 'quasar';
|
||||
import { Timestamp } from '@quasar/quasar-ui-qcalendar';
|
||||
@@ -87,7 +92,7 @@ function slotStyle(
|
||||
}
|
||||
|
||||
function reservationEvents(timestamp: Timestamp) {
|
||||
return reservationStore.getBoatReservations(timestamp);
|
||||
return reservationStore.getReservationsByDate(getDate(timestamp));
|
||||
}
|
||||
function onMoved(data: Event) {
|
||||
console.log('onMoved', data);
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
>
|
||||
<template #day="{ scope }">
|
||||
<div
|
||||
v-if="getIntervals(scope.timestamp, scope.resource)"
|
||||
v-if="filteredIntervals(scope.timestamp, scope.resource).length"
|
||||
style="
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
@@ -33,21 +33,22 @@
|
||||
"
|
||||
>
|
||||
<template
|
||||
v-for="block in getIntervals(
|
||||
v-for="block in sortedIntervals(
|
||||
scope.timestamp,
|
||||
scope.resource
|
||||
).sort((a, b) => Date.parse(a.start) - Date.parse(b.start))"
|
||||
)"
|
||||
:key="block.id"
|
||||
>
|
||||
<q-chip class="cursor-pointer">
|
||||
{{ date.formatDate(block.start, 'HH:mm') }} -
|
||||
{{ date.formatDate(block.end, 'HH:mm') }}
|
||||
<q-popup-edit
|
||||
<!-- <q-popup-edit
|
||||
:model-value="block"
|
||||
v-slot="scope"
|
||||
buttons
|
||||
@save="updateInterval(block)"
|
||||
@save="saveInterval"
|
||||
>
|
||||
TODO: Why isn't this saving?
|
||||
<q-input
|
||||
:model-value="date.formatDate(scope.value.start, 'HH:mm')"
|
||||
dense
|
||||
@@ -56,13 +57,14 @@
|
||||
label="start"
|
||||
@keyup.enter="scope.set"
|
||||
@update:model-value="
|
||||
(t) =>
|
||||
(block.start = buildISODate(
|
||||
date.formatDate(scope.value.start, 'YYYY-MM-DD'),t as string
|
||||
))
|
||||
(t) => {
|
||||
block.start = new Date(
|
||||
scope.value.start.split('T')[0] + 'T' + t
|
||||
).toISOString();
|
||||
}
|
||||
"
|
||||
/>
|
||||
<!-- TODO: Clean this up -->
|
||||
TODO: Clean this up
|
||||
<q-input
|
||||
:model-value="date.formatDate(scope.value.end, 'HH:mm')"
|
||||
dense
|
||||
@@ -71,12 +73,12 @@
|
||||
@keyup.enter="scope.set"
|
||||
@update:model-value="
|
||||
(t) =>
|
||||
(block.end = buildISODate(
|
||||
date.formatDate(scope.value.end, 'YYYY-MM-DD'),t as string
|
||||
))
|
||||
(block.end = new Date(
|
||||
scope.value.end.split('T')[0] + 'T' + t
|
||||
).toISOString())
|
||||
"
|
||||
/>
|
||||
</q-popup-edit> </q-chip
|
||||
</q-popup-edit>--> </q-chip
|
||||
><q-btn
|
||||
size="xs"
|
||||
icon="delete"
|
||||
@@ -89,7 +91,7 @@
|
||||
</q-calendar-scheduler>
|
||||
</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-item>
|
||||
<q-item-section>
|
||||
@@ -125,8 +127,12 @@
|
||||
<div class="text-h6">Warning!</div>
|
||||
</q-card-section>
|
||||
<q-card-section class="q-pt-none">
|
||||
This will overwrite existing blocks!
|
||||
{{ overlapped }}
|
||||
Conflicting times! Please delete overlapped items!
|
||||
<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-actions align="right">
|
||||
<q-btn flat label="OK" color="primary" v-close-popup />
|
||||
@@ -147,7 +153,6 @@ import {
|
||||
useScheduleStore,
|
||||
} from 'src/stores/schedule';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { buildISODate } from 'src/utils/misc';
|
||||
import type {
|
||||
Interval,
|
||||
IntervalTemplate,
|
||||
@@ -160,19 +165,17 @@ import { storeToRefs } from 'pinia';
|
||||
|
||||
const selectedDate = ref(today());
|
||||
const { fetchBoats } = useBoatStore();
|
||||
const { getIntervals, fetchIntervals, updateInterval, fetchIntervalTemplates } =
|
||||
useScheduleStore();
|
||||
const scheduleStore = useScheduleStore();
|
||||
const { boats } = storeToRefs(useBoatStore());
|
||||
const { timeblockTemplates } = storeToRefs(useScheduleStore());
|
||||
const calendar = ref();
|
||||
const overlapped = ref();
|
||||
const blankTemplate: IntervalTemplate = {
|
||||
const alert = ref(false);
|
||||
const newTemplate = ref<IntervalTemplate>({
|
||||
$id: '',
|
||||
name: 'NewTemplate',
|
||||
timeTuples: [['09:00', '12:00']],
|
||||
};
|
||||
const newTemplate = ref<IntervalTemplate>({ ...blankTemplate });
|
||||
const alert = ref(false);
|
||||
});
|
||||
|
||||
/* TODOS:
|
||||
* Need more validation:
|
||||
@@ -183,20 +186,37 @@ const alert = ref(false);
|
||||
|
||||
onMounted(async () => {
|
||||
await fetchBoats();
|
||||
await fetchIntervals();
|
||||
await fetchIntervalTemplates();
|
||||
await scheduleStore.fetchIntervals();
|
||||
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() {
|
||||
newTemplate.value = { ...blankTemplate };
|
||||
newTemplate.value = {
|
||||
$id: 'unsaved',
|
||||
name: 'NewTemplate',
|
||||
timeTuples: [['09:00', '12:00']],
|
||||
};
|
||||
}
|
||||
function createTemplate() {
|
||||
newTemplate.value.$id = 'unsaved';
|
||||
}
|
||||
function createIntervals(boat: Boat, templateId: string, date: string) {
|
||||
timeBlocksFromTemplate(boat, templateId, date)?.map((block) =>
|
||||
useScheduleStore().createInterval(block)
|
||||
);
|
||||
const intervals = timeBlocksFromTemplate(boat, templateId, date);
|
||||
intervals.forEach((interval) => scheduleStore.createInterval(interval));
|
||||
}
|
||||
|
||||
function getIntervals(date: Timestamp, boat: Boat) {
|
||||
return scheduleStore.getIntervals(date, boat);
|
||||
}
|
||||
|
||||
function timeBlocksFromTemplate(
|
||||
@@ -204,25 +224,28 @@ function timeBlocksFromTemplate(
|
||||
templateId: string,
|
||||
date: string
|
||||
): Interval[] {
|
||||
const timeBlock = timeblockTemplates.value.find((t) => t.$id === templateId);
|
||||
return (
|
||||
timeBlock?.timeTuples.map((tb: TimeTuple) =>
|
||||
buildInterval(boat, tb, date)
|
||||
) || []
|
||||
);
|
||||
const template = timeblockTemplates.value.find((t) => t.$id === templateId);
|
||||
return template
|
||||
? template.timeTuples.map((timeTuple: TimeTuple) =>
|
||||
buildInterval(boat, timeTuple, date)
|
||||
)
|
||||
: [];
|
||||
}
|
||||
|
||||
function deleteBlock(block: Interval) {
|
||||
if (block.$id) {
|
||||
useScheduleStore().deleteInterval(block.$id);
|
||||
scheduleStore.deleteInterval(block.$id);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function onDragEnter(e: DragEvent, type: string) {
|
||||
if (type === 'day' || type === 'head-day') {
|
||||
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');
|
||||
}
|
||||
}
|
||||
@@ -230,16 +253,18 @@ function onDragEnter(e: DragEvent, type: string) {
|
||||
function onDragOver(e: DragEvent, type: string) {
|
||||
if (type === 'day' || type === 'head-day') {
|
||||
e.preventDefault();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function onDragLeave(e: DragEvent, type: string) {
|
||||
if (type === 'day' || type === 'head-day') {
|
||||
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');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,34 +274,28 @@ function onDrop(
|
||||
type: string,
|
||||
scope: { resource: Boat; timestamp: Timestamp }
|
||||
) {
|
||||
if (e.target instanceof HTMLDivElement)
|
||||
e.target.classList.remove('bg-secondary');
|
||||
|
||||
if ((type === 'day' || type === 'head-day') && e.dataTransfer) {
|
||||
const templateId = e.dataTransfer.getData('ID');
|
||||
const date = scope.timestamp.date;
|
||||
if (type === 'head-day') {
|
||||
overlapped.value = boats.value.map((boat) =>
|
||||
const resource = scope.resource;
|
||||
const existingIntervals = getIntervals(scope.timestamp, resource).value;
|
||||
const boatsToApply = type === 'head-day' ? boats.value : [resource];
|
||||
overlapped.value = boatsToApply
|
||||
.map((boat) =>
|
||||
blocksOverlapped(
|
||||
getIntervals(scope.timestamp, boat).concat(
|
||||
existingIntervals.concat(
|
||||
timeBlocksFromTemplate(boat, templateId, date)
|
||||
)
|
||||
)
|
||||
);
|
||||
if (overlapped.value.length === 0) {
|
||||
boats.value.map((b) => createIntervals(b, templateId, date));
|
||||
} else {
|
||||
alert.value = true;
|
||||
}
|
||||
)
|
||||
.flat(1);
|
||||
if (overlapped.value.length === 0) {
|
||||
boatsToApply.map((b) => createIntervals(b, templateId, date));
|
||||
} else {
|
||||
overlapped.value = blocksOverlapped(
|
||||
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;
|
||||
}
|
||||
alert.value = true;
|
||||
}
|
||||
}
|
||||
if (e.target instanceof HTMLDivElement)
|
||||
|
||||
@@ -44,12 +44,16 @@ export const useAuthStore = defineStore('auth', () => {
|
||||
'/userinfo/' + id,
|
||||
ExecutionMethod.GET
|
||||
)
|
||||
.then(
|
||||
(res) => (userNames.value[id] = JSON.parse(res.responseBody).name)
|
||||
);
|
||||
.then((res) => {
|
||||
if (res.responseBody) {
|
||||
userNames.value[id] = JSON.parse(res.responseBody).name;
|
||||
} else {
|
||||
console.error(res, id);
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('Failed to get username. Error: ' + e);
|
||||
console.error('Failed to get username. Error: ' + e);
|
||||
}
|
||||
return userNames.value[id];
|
||||
}
|
||||
|
||||
@@ -2,17 +2,20 @@ import { defineStore } from 'pinia';
|
||||
import type { Reservation } from './schedule.types';
|
||||
import { ref } from 'vue';
|
||||
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', () => {
|
||||
const reservations = ref<Reservation[]>([]);
|
||||
const reservations = ref<Map<string, Reservation>>(new Map());
|
||||
const datesLoaded = ref<Record<string, boolean>>({});
|
||||
|
||||
const getConflictingReservations = (
|
||||
resource: string,
|
||||
start: Date,
|
||||
end: Date
|
||||
): Reservation[] => {
|
||||
const overlapped = reservations.value.filter(
|
||||
const overlapped = Array.from(reservations.value.values()).filter(
|
||||
(entry: Reservation) =>
|
||||
entry.resource == resource &&
|
||||
new Date(entry.start) < end &&
|
||||
@@ -37,46 +40,75 @@ export const useReservationStore = defineStore('reservation', () => {
|
||||
);
|
||||
};
|
||||
|
||||
const addOrCreateReservation = (reservation: Reservation) => {
|
||||
const index = reservations.value.findIndex(
|
||||
(res) => res.id == reservation.id
|
||||
);
|
||||
index != -1
|
||||
? (reservations.value[index] = reservation)
|
||||
: reservations.value.push(reservation);
|
||||
};
|
||||
async function fetchReservations() {
|
||||
function setDateLoaded(start: Date, end: Date, state: boolean) {
|
||||
let curDate = start;
|
||||
while (curDate < end) {
|
||||
datesLoaded.value[(parseDate(curDate) as Timestamp).date] = state;
|
||||
curDate = date.addToDate(curDate, { days: 1 });
|
||||
}
|
||||
}
|
||||
async function fetchReservationsForDateRange(start: string, end?: string) {
|
||||
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 {
|
||||
const response = await databases.listDocuments(
|
||||
AppwriteIds.databaseId,
|
||||
AppwriteIds.collection.reservation
|
||||
);
|
||||
reservations.value = response.documents as Reservation[];
|
||||
databases
|
||||
.listDocuments(
|
||||
AppwriteIds.databaseId,
|
||||
AppwriteIds.collection.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) {
|
||||
console.error('Failed to fetch timeblocks', error);
|
||||
console.error('Failed to fetch reservations', error);
|
||||
}
|
||||
}
|
||||
|
||||
const getBoatReservations = (
|
||||
searchDate: Timestamp,
|
||||
async function fetchReservations() {
|
||||
return;
|
||||
// fetchReservationsForDateRange(
|
||||
// today(),
|
||||
// date.addToDate(today(), { days: 7 }).toLocaleDateString()
|
||||
// );
|
||||
}
|
||||
|
||||
const getReservationsByDate = (
|
||||
searchDate: string,
|
||||
boat?: string
|
||||
): Reservation[] => {
|
||||
const result = reservations.value.filter((x) => {
|
||||
return (
|
||||
((parsed(x.start)?.date == searchDate.date ||
|
||||
parsed(x.end)?.date == searchDate.date) && // Part of reservation falls on day
|
||||
x.resource != undefined && // A boat is defined
|
||||
!boat) ||
|
||||
x.resource == boat // A specific boat has been passed, and matches
|
||||
);
|
||||
if (!datesLoaded.value[searchDate]) {
|
||||
fetchReservationsForDateRange(searchDate);
|
||||
}
|
||||
const result = Array.from(reservations.value.values()).filter((x) => {
|
||||
return new Date(x.start) < new Date(searchDate + 'T' + '23:59') &&
|
||||
new Date(x.end) > new Date(searchDate + 'T' + '00:00') && // Part of reservation falls on day
|
||||
boat
|
||||
? boat === x.boat
|
||||
: true; // A specific boat has been passed, and matches
|
||||
});
|
||||
console.log(result);
|
||||
return result;
|
||||
};
|
||||
|
||||
return {
|
||||
getBoatReservations,
|
||||
getReservationsByDate,
|
||||
fetchReservations,
|
||||
addOrCreateReservation,
|
||||
isReservationOverlapped,
|
||||
isResourceTimeOverlapped,
|
||||
getConflictingReservations,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { ref } from 'vue';
|
||||
import { ComputedRef, computed, ref } from 'vue';
|
||||
import { Boat } from './boat';
|
||||
import {
|
||||
Timestamp,
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
import { IntervalTemplate, TimeTuple, Interval } from './schedule.types';
|
||||
import { AppwriteIds, databases } from 'src/boot/appwrite';
|
||||
import { ID, Models } from 'appwrite';
|
||||
import { buildISODate } from 'src/utils/misc';
|
||||
|
||||
export function arrayToTimeTuples(arr: string[]) {
|
||||
const timeTuples: TimeTuple[] = [];
|
||||
@@ -70,28 +69,34 @@ export function buildInterval(
|
||||
as a UTC time and date-time forms are interpreted as local time. */
|
||||
const result = {
|
||||
boatId: resource.$id,
|
||||
start: buildISODate(blockDate, time[0]),
|
||||
end: buildISODate(blockDate, time[1]),
|
||||
start: new Date(blockDate + 'T' + time[0]).toISOString(),
|
||||
end: new Date(blockDate + 'T' + time[1]).toISOString(),
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
export const useScheduleStore = defineStore('schedule', () => {
|
||||
// TODO: Implement functions to dynamically pull this data.
|
||||
const timeblocks = ref<Interval[]>([]);
|
||||
const timeblockTemplates = ref<IntervalTemplate[]>([]);
|
||||
const intervals = ref<Interval[]>([]);
|
||||
const intervalTemplates = ref<IntervalTemplate[]>([]);
|
||||
|
||||
const getIntervals = (date: Timestamp, boat: Boat): Interval[] => {
|
||||
return timeblocks.value.filter((block) => {
|
||||
return (
|
||||
compareDate(parseDate(new Date(block.start)) as Timestamp, date) &&
|
||||
block.boatId === boat.$id
|
||||
);
|
||||
});
|
||||
const getIntervals = (
|
||||
date: Timestamp,
|
||||
boat: Boat
|
||||
): ComputedRef<Interval[]> => {
|
||||
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[] => {
|
||||
// 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(
|
||||
parseDate(new Date(b.start)) as Timestamp,
|
||||
parsed(date) as Timestamp
|
||||
@@ -105,7 +110,7 @@ export const useScheduleStore = defineStore('schedule', () => {
|
||||
AppwriteIds.databaseId,
|
||||
AppwriteIds.collection.timeBlock
|
||||
);
|
||||
timeblocks.value = response.documents as Interval[];
|
||||
intervals.value = response.documents as Interval[];
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch timeblocks', error);
|
||||
}
|
||||
@@ -117,7 +122,7 @@ export const useScheduleStore = defineStore('schedule', () => {
|
||||
AppwriteIds.databaseId,
|
||||
AppwriteIds.collection.timeBlockTemplate
|
||||
);
|
||||
timeblockTemplates.value = response.documents.map(
|
||||
intervalTemplates.value = response.documents.map(
|
||||
(d: Models.Document): IntervalTemplate => {
|
||||
return {
|
||||
...d,
|
||||
@@ -153,7 +158,7 @@ export const useScheduleStore = defineStore('schedule', () => {
|
||||
ID.unique(),
|
||||
interval
|
||||
);
|
||||
timeblocks.value.push(response as Interval);
|
||||
intervals.value.push(response as Interval);
|
||||
} catch (e) {
|
||||
console.error('Error creating Interval: ' + e);
|
||||
}
|
||||
@@ -167,7 +172,8 @@ export const useScheduleStore = defineStore('schedule', () => {
|
||||
interval.$id,
|
||||
{ ...interval, $id: undefined }
|
||||
);
|
||||
timeblocks.value.push(response as Interval);
|
||||
intervals.value.push(response as Interval);
|
||||
console.log(`Saved Interval: ${interval.$id}`);
|
||||
} else {
|
||||
console.error('Update interval called without an ID');
|
||||
}
|
||||
@@ -182,7 +188,7 @@ export const useScheduleStore = defineStore('schedule', () => {
|
||||
AppwriteIds.collection.timeBlock,
|
||||
id
|
||||
);
|
||||
timeblocks.value = timeblocks.value.filter((block) => block.$id !== id);
|
||||
intervals.value = intervals.value.filter((block) => block.$id !== id);
|
||||
} catch (e) {
|
||||
console.error('Error deleting Interval: ' + e);
|
||||
}
|
||||
@@ -195,7 +201,7 @@ export const useScheduleStore = defineStore('schedule', () => {
|
||||
ID.unique(),
|
||||
{ name: template.name, timeTuple: template.timeTuples.flat(2) }
|
||||
);
|
||||
timeblockTemplates.value.push(response as IntervalTemplate);
|
||||
intervalTemplates.value.push(response as IntervalTemplate);
|
||||
} catch (e) {
|
||||
console.error('Error updating IntervalTemplate: ' + e);
|
||||
}
|
||||
@@ -207,7 +213,7 @@ export const useScheduleStore = defineStore('schedule', () => {
|
||||
AppwriteIds.collection.timeBlockTemplate,
|
||||
id
|
||||
);
|
||||
timeblockTemplates.value = timeblockTemplates.value.filter(
|
||||
intervalTemplates.value = intervalTemplates.value.filter(
|
||||
(template) => template.$id !== id
|
||||
);
|
||||
} catch (e) {
|
||||
@@ -228,7 +234,7 @@ export const useScheduleStore = defineStore('schedule', () => {
|
||||
timeTuple: template.timeTuples.flat(2),
|
||||
}
|
||||
);
|
||||
timeblockTemplates.value = timeblockTemplates.value.map((b) =>
|
||||
intervalTemplates.value = intervalTemplates.value.map((b) =>
|
||||
b.$id !== id
|
||||
? b
|
||||
: ({
|
||||
@@ -242,8 +248,8 @@ export const useScheduleStore = defineStore('schedule', () => {
|
||||
};
|
||||
|
||||
return {
|
||||
timeblocks,
|
||||
timeblockTemplates,
|
||||
timeblocks: intervals,
|
||||
timeblockTemplates: intervalTemplates,
|
||||
getIntervalsForDate,
|
||||
getIntervals,
|
||||
fetchIntervals,
|
||||
|
||||
@@ -3,8 +3,8 @@ import { Models } from 'appwrite';
|
||||
export type StatusTypes = 'tentative' | 'confirmed' | 'pending' | undefined;
|
||||
export type Reservation = Partial<Models.Document> & {
|
||||
user: string;
|
||||
start: string; // ISODate
|
||||
end: string; //ISODate
|
||||
start: string;
|
||||
end: string;
|
||||
resource: string; // Boat ID
|
||||
status?: StatusTypes;
|
||||
};
|
||||
|
||||
@@ -151,7 +151,6 @@ export const useTaskStore = defineStore('tasks', {
|
||||
const result = state.tasks.filter((task) =>
|
||||
task.title.toLowerCase().includes(searchQuery.toLowerCase())
|
||||
);
|
||||
console.log(result);
|
||||
return result;
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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 {
|
||||
return [...Array(20)]
|
||||
.map(() => Math.floor(Math.random() * 16).toString(16))
|
||||
|
||||
Reference in New Issue
Block a user