|
|
|
|
@@ -1,127 +1,78 @@
|
|
|
|
|
import { defineStore } from 'pinia';
|
|
|
|
|
import { ComputedRef, computed, ref } from 'vue';
|
|
|
|
|
import { Ref, computed, ref } from 'vue';
|
|
|
|
|
import { Boat } from './boat';
|
|
|
|
|
import {
|
|
|
|
|
Timestamp,
|
|
|
|
|
parseDate,
|
|
|
|
|
parsed,
|
|
|
|
|
compareDate,
|
|
|
|
|
} from '@quasar/quasar-ui-qcalendar';
|
|
|
|
|
import { Timestamp } from '@quasar/quasar-ui-qcalendar';
|
|
|
|
|
|
|
|
|
|
import { IntervalTemplate, TimeTuple, Interval } from './schedule.types';
|
|
|
|
|
import { IntervalTemplate, Interval, IntervalRecord } from './schedule.types';
|
|
|
|
|
import { AppwriteIds, databases } from 'src/boot/appwrite';
|
|
|
|
|
import { ID, Models } from 'appwrite';
|
|
|
|
|
|
|
|
|
|
export function arrayToTimeTuples(arr: string[]) {
|
|
|
|
|
const timeTuples: TimeTuple[] = [];
|
|
|
|
|
for (let i = 0; i < arr.length; i += 2) {
|
|
|
|
|
timeTuples.push([arr[i], arr[i + 1]]);
|
|
|
|
|
}
|
|
|
|
|
return timeTuples;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function timeTuplesOverlapped(tuples: TimeTuple[]): Interval[] {
|
|
|
|
|
return blocksOverlapped(
|
|
|
|
|
tuples.map((tuples) => {
|
|
|
|
|
return {
|
|
|
|
|
boatId: '',
|
|
|
|
|
start: '01/01/2001 ' + tuples[0],
|
|
|
|
|
end: '01/01/2001 ' + tuples[1],
|
|
|
|
|
};
|
|
|
|
|
})
|
|
|
|
|
).map((t) => {
|
|
|
|
|
return { ...t, start: t.start.split(' ')[1], end: t.end.split(' ')[1] };
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function blocksOverlapped(blocks: Interval[] | Interval[]): Interval[] {
|
|
|
|
|
return Array.from(
|
|
|
|
|
new Set(
|
|
|
|
|
blocks
|
|
|
|
|
.sort((a, b) => Date.parse(a.start) - Date.parse(b.start))
|
|
|
|
|
.reduce((acc: Interval[], block, i, arr) => {
|
|
|
|
|
if (i > 0 && block.start < arr[i - 1].end)
|
|
|
|
|
acc.push(arr[i - 1], block);
|
|
|
|
|
return acc;
|
|
|
|
|
}, [])
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function copyTimeTuples(tuples: TimeTuple[]): TimeTuple[] {
|
|
|
|
|
return tuples.map((t) => Object.assign([], t));
|
|
|
|
|
}
|
|
|
|
|
export function copyIntervalTemplate(
|
|
|
|
|
template: IntervalTemplate
|
|
|
|
|
): IntervalTemplate {
|
|
|
|
|
return {
|
|
|
|
|
...template,
|
|
|
|
|
timeTuples: copyTimeTuples(template.timeTuples || []),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function buildInterval(
|
|
|
|
|
resource: Boat,
|
|
|
|
|
time: TimeTuple,
|
|
|
|
|
blockDate: string
|
|
|
|
|
): Interval {
|
|
|
|
|
/* When the time zone offset is absent, date-only forms are interpreted
|
|
|
|
|
as a UTC time and date-time forms are interpreted as local time. */
|
|
|
|
|
const result = {
|
|
|
|
|
boatId: resource.$id,
|
|
|
|
|
start: new Date(blockDate + 'T' + time[0]).toISOString(),
|
|
|
|
|
end: new Date(blockDate + 'T' + time[1]).toISOString(),
|
|
|
|
|
};
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
import { ID, Models, Query } from 'appwrite';
|
|
|
|
|
import { arrayToTimeTuples } from 'src/utils/schedule';
|
|
|
|
|
|
|
|
|
|
export const useScheduleStore = defineStore('schedule', () => {
|
|
|
|
|
// TODO: Implement functions to dynamically pull this data.
|
|
|
|
|
const intervals = ref<Interval[]>([]);
|
|
|
|
|
const intervals = ref<Map<string, Interval>>(new Map());
|
|
|
|
|
const intervalDates = ref<IntervalRecord>({});
|
|
|
|
|
const intervalTemplates = ref<IntervalTemplate[]>([]);
|
|
|
|
|
|
|
|
|
|
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 getIntervals = (date: Timestamp | string, boat?: Boat): Interval[] => {
|
|
|
|
|
const searchDate = typeof date === 'string' ? date : date.date;
|
|
|
|
|
const dayStart = new Date(searchDate + 'T00:00');
|
|
|
|
|
const dayEnd = new Date(searchDate + 'T23:59');
|
|
|
|
|
if (!intervalDates.value[searchDate]) {
|
|
|
|
|
intervalDates.value[searchDate] = 'pending';
|
|
|
|
|
fetchIntervals(searchDate);
|
|
|
|
|
}
|
|
|
|
|
return computed(() => {
|
|
|
|
|
return Array.from(intervals.value.values()).filter((interval) => {
|
|
|
|
|
const intervalStart = new Date(interval.start);
|
|
|
|
|
const intervalEnd = new Date(interval.end);
|
|
|
|
|
|
|
|
|
|
const isWithinDay = intervalStart < dayEnd && intervalEnd > dayStart;
|
|
|
|
|
const matchesBoat = boat ? boat.$id === interval.boatId : true;
|
|
|
|
|
return isWithinDay && matchesBoat;
|
|
|
|
|
});
|
|
|
|
|
}).value;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const getIntervalsForDate = (date: string): Interval[] => {
|
|
|
|
|
// TODO: This needs to actually make sure we have the dates we need, stay in sync, etc.
|
|
|
|
|
|
|
|
|
|
return intervals.value.filter((b) => {
|
|
|
|
|
return compareDate(
|
|
|
|
|
parseDate(new Date(b.start)) as Timestamp,
|
|
|
|
|
parsed(date) as Timestamp
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
async function fetchIntervals() {
|
|
|
|
|
async function fetchIntervals(dateString: string) {
|
|
|
|
|
try {
|
|
|
|
|
const response = await databases.listDocuments(
|
|
|
|
|
AppwriteIds.databaseId,
|
|
|
|
|
AppwriteIds.collection.timeBlock
|
|
|
|
|
AppwriteIds.collection.interval,
|
|
|
|
|
[
|
|
|
|
|
Query.greaterThanEqual(
|
|
|
|
|
'end',
|
|
|
|
|
new Date(dateString + 'T00:00').toISOString()
|
|
|
|
|
),
|
|
|
|
|
Query.lessThanEqual(
|
|
|
|
|
'start',
|
|
|
|
|
new Date(dateString + 'T23:59').toISOString()
|
|
|
|
|
),
|
|
|
|
|
Query.limit(50), // We are asuming that we won't have more than 50 intervals per day.
|
|
|
|
|
]
|
|
|
|
|
);
|
|
|
|
|
intervals.value = response.documents as Interval[];
|
|
|
|
|
response.documents.forEach((d) =>
|
|
|
|
|
intervals.value.set(d.$id, d as Interval)
|
|
|
|
|
);
|
|
|
|
|
intervalDates.value[dateString] = 'loaded';
|
|
|
|
|
console.info(`Loaded ${response.documents.length} intervals from server`);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Failed to fetch timeblocks', error);
|
|
|
|
|
console.error('Failed to fetch intervals', error);
|
|
|
|
|
intervalDates.value[dateString] = 'error';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const getIntervalTemplates = (): Ref<IntervalTemplate[]> => {
|
|
|
|
|
// Should subscribe to get new intervaltemplates when they are created
|
|
|
|
|
if (!intervalTemplates.value) fetchIntervalTemplates();
|
|
|
|
|
return intervalTemplates;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
async function fetchIntervalTemplates() {
|
|
|
|
|
try {
|
|
|
|
|
const response = await databases.listDocuments(
|
|
|
|
|
AppwriteIds.databaseId,
|
|
|
|
|
AppwriteIds.collection.timeBlockTemplate
|
|
|
|
|
AppwriteIds.collection.intervalTemplate
|
|
|
|
|
);
|
|
|
|
|
intervalTemplates.value = response.documents.map(
|
|
|
|
|
(d: Models.Document): IntervalTemplate => {
|
|
|
|
|
@@ -140,11 +91,11 @@ export const useScheduleStore = defineStore('schedule', () => {
|
|
|
|
|
try {
|
|
|
|
|
const response = await databases.createDocument(
|
|
|
|
|
AppwriteIds.databaseId,
|
|
|
|
|
AppwriteIds.collection.timeBlock,
|
|
|
|
|
AppwriteIds.collection.interval,
|
|
|
|
|
ID.unique(),
|
|
|
|
|
interval
|
|
|
|
|
);
|
|
|
|
|
intervals.value.push(response as Interval);
|
|
|
|
|
intervals.value.set(response.$id, response as Interval);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error('Error creating Interval: ' + e);
|
|
|
|
|
}
|
|
|
|
|
@@ -154,12 +105,12 @@ export const useScheduleStore = defineStore('schedule', () => {
|
|
|
|
|
if (interval.$id) {
|
|
|
|
|
const response = await databases.updateDocument(
|
|
|
|
|
AppwriteIds.databaseId,
|
|
|
|
|
AppwriteIds.collection.timeBlock,
|
|
|
|
|
AppwriteIds.collection.interval,
|
|
|
|
|
interval.$id,
|
|
|
|
|
{ ...interval, $id: undefined }
|
|
|
|
|
);
|
|
|
|
|
intervals.value.push(response as Interval);
|
|
|
|
|
console.log(`Saved Interval: ${interval.$id}`);
|
|
|
|
|
intervals.value.set(response.$id, response as Interval);
|
|
|
|
|
console.info(`Saved Interval: ${interval.$id}`);
|
|
|
|
|
} else {
|
|
|
|
|
console.error('Update interval called without an ID');
|
|
|
|
|
}
|
|
|
|
|
@@ -171,10 +122,11 @@ export const useScheduleStore = defineStore('schedule', () => {
|
|
|
|
|
try {
|
|
|
|
|
await databases.deleteDocument(
|
|
|
|
|
AppwriteIds.databaseId,
|
|
|
|
|
AppwriteIds.collection.timeBlock,
|
|
|
|
|
AppwriteIds.collection.interval,
|
|
|
|
|
id
|
|
|
|
|
);
|
|
|
|
|
intervals.value = intervals.value.filter((block) => block.$id !== id);
|
|
|
|
|
intervals.value.delete(id);
|
|
|
|
|
console.info(`Deleted interval: ${id}`);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error('Error deleting Interval: ' + e);
|
|
|
|
|
}
|
|
|
|
|
@@ -183,7 +135,7 @@ export const useScheduleStore = defineStore('schedule', () => {
|
|
|
|
|
try {
|
|
|
|
|
const response = await databases.createDocument(
|
|
|
|
|
AppwriteIds.databaseId,
|
|
|
|
|
AppwriteIds.collection.timeBlockTemplate,
|
|
|
|
|
AppwriteIds.collection.intervalTemplate,
|
|
|
|
|
ID.unique(),
|
|
|
|
|
{ name: template.name, timeTuple: template.timeTuples.flat(2) }
|
|
|
|
|
);
|
|
|
|
|
@@ -196,7 +148,7 @@ export const useScheduleStore = defineStore('schedule', () => {
|
|
|
|
|
try {
|
|
|
|
|
await databases.deleteDocument(
|
|
|
|
|
AppwriteIds.databaseId,
|
|
|
|
|
AppwriteIds.collection.timeBlockTemplate,
|
|
|
|
|
AppwriteIds.collection.intervalTemplate,
|
|
|
|
|
id
|
|
|
|
|
);
|
|
|
|
|
intervalTemplates.value = intervalTemplates.value.filter(
|
|
|
|
|
@@ -213,7 +165,7 @@ export const useScheduleStore = defineStore('schedule', () => {
|
|
|
|
|
try {
|
|
|
|
|
const response = await databases.updateDocument(
|
|
|
|
|
AppwriteIds.databaseId,
|
|
|
|
|
AppwriteIds.collection.timeBlockTemplate,
|
|
|
|
|
AppwriteIds.collection.intervalTemplate,
|
|
|
|
|
id,
|
|
|
|
|
{
|
|
|
|
|
name: template.name,
|
|
|
|
|
@@ -234,10 +186,8 @@ export const useScheduleStore = defineStore('schedule', () => {
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
timeblocks: intervals,
|
|
|
|
|
timeblockTemplates: intervalTemplates,
|
|
|
|
|
getIntervalsForDate,
|
|
|
|
|
getIntervals,
|
|
|
|
|
getIntervalTemplates,
|
|
|
|
|
fetchIntervals,
|
|
|
|
|
fetchIntervalTemplates,
|
|
|
|
|
createInterval,
|
|
|
|
|
|