Compare commits

8 Commits

Author SHA1 Message Date
ea785887a1 Sorted out reactivity with storeToRefs
Some checks failed
Build BAB Application Deployment Artifact / build (push) Failing after 2m1s
2024-05-08 23:43:18 -04:00
b860e1d977 Add some checks 2024-05-08 17:23:23 -04:00
274d0193f7 Some timeblock stuff working 2024-05-08 13:32:10 -04:00
033993b1b8 Upgrade Quasar 2024-05-06 19:22:28 -04:00
2872fb867e Started work on Schedule Management 2024-05-06 17:22:11 -04:00
8e73650462 Clean up all kinds of typescript issues 2024-05-05 15:58:58 -04:00
634cff507c Converted some schedule to use backend 2024-05-04 23:17:05 -04:00
fa4d83e42d Cleanup linting messages. Also, break some things 2024-05-04 12:08:16 -04:00
33 changed files with 1126 additions and 417 deletions

4
appwrite.json Normal file
View File

@@ -0,0 +1,4 @@
{
"projectId": "65ede55a213134f2b688",
"projectName": ""
}

View File

@@ -14,14 +14,14 @@
}, },
"dependencies": { "dependencies": {
"@quasar/extras": "^1.16.11", "@quasar/extras": "^1.16.11",
"appwrite": "^13.0.0", "appwrite": "^14.0.1",
"pinia": "^2.1.7", "pinia": "^2.1.7",
"vue": "3", "vue": "3",
"vue-router": "4" "vue-router": "4"
}, },
"devDependencies": { "devDependencies": {
"@quasar/app-vite": "^1.7.4", "@quasar/app-vite": "^1.9.1",
"@quasar/quasar-app-extension-qcalendar": "^4.0.0-beta.15", "@quasar/quasar-app-extension-qcalendar": "^4.0.0-beta.16",
"@types/node": "^12.20.21", "@types/node": "^12.20.21",
"@typescript-eslint/eslint-plugin": "^5.10.0", "@typescript-eslint/eslint-plugin": "^5.10.0",
"@typescript-eslint/parser": "^5.10.0", "@typescript-eslint/parser": "^5.10.0",
@@ -31,8 +31,10 @@
"eslint-config-prettier": "^8.1.0", "eslint-config-prettier": "^8.1.0",
"eslint-plugin-vue": "^9.0.0", "eslint-plugin-vue": "^9.0.0",
"prettier": "^2.5.1", "prettier": "^2.5.1",
"quasar": "^2.15.2", "quasar": "^2.16.0",
"typescript": "^4.5.4", "typescript": "~5.3.0",
"vite-plugin-checker": "^0.6.4",
"vue-tsc": "^1.8.22",
"workbox-build": "^7.0.0", "workbox-build": "^7.0.0",
"workbox-cacheable-response": "^7.0.0", "workbox-cacheable-response": "^7.0.0",
"workbox-core": "^7.0.0", "workbox-core": "^7.0.0",

View File

@@ -9,7 +9,6 @@
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js // https://v2.quasar.dev/quasar-cli-vite/quasar-config-js
const { configure } = require('quasar/wrappers'); const { configure } = require('quasar/wrappers');
const path = require('path');
module.exports = configure(function (/* ctx */) { module.exports = configure(function (/* ctx */) {
return { return {
@@ -73,9 +72,20 @@ module.exports = configure(function (/* ctx */) {
// extendViteConf (viteConf) {}, // extendViteConf (viteConf) {},
// viteVuePluginOptions: {}, // viteVuePluginOptions: {},
// vitePlugins: [ vitePlugins: [
// [ 'package-name', { ..options.. } ] [
// ] 'vite-plugin-checker',
{
vueTsc: {
tsconfigPath: 'tsconfig.vue-tsc.json',
},
eslint: {
lintCommand: 'eslint "./**/*.{js,ts,mjs,cjs,vue}"',
},
},
{ server: false },
],
],
}, },
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#devServer // Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#devServer
@@ -104,7 +114,7 @@ module.exports = configure(function (/* ctx */) {
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#framework // https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#framework
framework: { framework: {
config: { config: {
autoImportComponentCase: 'combined', // or 'kebab' (default) or 'combined' autoImportComponentCase: 'kebab', // or 'kebab' (default) or 'combined'
}, },
// iconSet: 'material-icons', // Quasar icon set // iconSet: 'material-icons', // Quasar icon set

View File

@@ -2,10 +2,20 @@
<router-view /> <router-view />
</template> </template>
<script lang="ts"> <script setup lang="ts">
import { defineComponent } from 'vue'; import { defineComponent, onMounted } from 'vue';
import { useScheduleStore } from './stores/schedule';
import { useBoatStore } from './stores/boat';
import { useAuthStore } from './stores/auth';
export default defineComponent({ defineComponent({
name: 'OYS Borrow-a-Boat', name: 'OYS Borrow-a-Boat',
}); });
onMounted(async () => {
await useAuthStore().init();
await useScheduleStore().fetchTimeBlockTemplates();
await useScheduleStore().fetchTimeBlocks();
await useBoatStore().fetchBoats();
});
</script> </script>

View File

@@ -13,9 +13,10 @@ const client = new Client();
// const appDatabaseId = '654ac5044d1c446feb71'; // const appDatabaseId = '654ac5044d1c446feb71';
// Private self-hosted appwrite // Private self-hosted appwrite
client if (process.env.APPWRITE_API_ENDPOINT && process.env.APPWRITE_API_PROJECT)
.setEndpoint(process.env.APPWRITE_API_ENDPOINT) client
.setProject(process.env.APPWRITE_API_PROJECT); .setEndpoint(process.env.APPWRITE_API_ENDPOINT)
.setProject(process.env.APPWRITE_API_PROJECT);
//TODO move this to config file //TODO move this to config file
const AppwriteIds = { const AppwriteIds = {
@@ -24,7 +25,9 @@ const AppwriteIds = {
task: '65ee1cd5b550023fae4f', task: '65ee1cd5b550023fae4f',
taskTags: '65ee21d72d5c8007c34c', taskTags: '65ee21d72d5c8007c34c',
skillTags: '66072582a74d94a4bd01', skillTags: '66072582a74d94a4bd01',
boats: '66341910003e287cd71c', boat: '66341910003e287cd71c',
timeBlock: '66361869002883fb4c4b',
timeBlockTemplate: '66361f480007fdd639af',
}, },
}; };

View File

@@ -1,64 +0,0 @@
<template>
<div>
<p>{{ title }}</p>
<ul>
<li v-for="todo in todos" :key="todo.id" @click="increment">
{{ todo.id }} - {{ todo.content }}
</li>
</ul>
<p>Count: {{ todoCount }} / {{ meta.totalCount }}</p>
<p>Active: {{ active ? 'yes' : 'no' }}</p>
<p>Clicks on todos: {{ clickCount }}</p>
</div>
</template>
<script lang="ts">
import {
defineComponent,
PropType,
computed,
ref,
toRef,
Ref,
} from 'vue';
import { Todo, Meta } from './models';
function useClickCount() {
const clickCount = ref(0);
function increment() {
clickCount.value += 1
return clickCount.value;
}
return { clickCount, increment };
}
function useDisplayTodo(todos: Ref<Todo[]>) {
const todoCount = computed(() => todos.value.length);
return { todoCount };
}
export default defineComponent({
name: 'ExampleComponent',
props: {
title: {
type: String,
required: true
},
todos: {
type: Array as PropType<Todo[]>,
default: () => []
},
meta: {
type: Object as PropType<Meta>,
required: true
},
active: {
type: Boolean
}
},
setup (props) {
return { ...useClickCount(), ...useDisplayTodo(toRef(props, 'todos')) };
},
});
</script>

View File

@@ -116,7 +116,7 @@ import { Boat, useBoatStore } from 'src/stores/boat';
import { useScheduleStore } from 'src/stores/schedule'; import { useScheduleStore } from 'src/stores/schedule';
import { date } from 'quasar'; import { date } from 'quasar';
import { computed } from 'vue'; import { computed } from 'vue';
import type { StatusTypes } from 'src/stores/schedule'; import type { StatusTypes } from 'src/stores/schedule.types';
interface EventData { interface EventData {
event: object; event: object;
@@ -172,7 +172,7 @@ function monthFormatter() {
function getEvents(scope: ResourceIntervalScope) { function getEvents(scope: ResourceIntervalScope) {
const resourceEvents = scheduleStore.getBoatReservations( const resourceEvents = scheduleStore.getBoatReservations(
date.extractDate(selectedDate.value, 'YYYY-MM-DD'), parseDate(date.extractDate(selectedDate.value, 'YYYY-MM-DD')) as Timestamp,
scope.resource.$id scope.resource.$id
); );

View File

@@ -1,23 +1,26 @@
<template> <template>
<q-card v-for="boat in boats" :key="boat.id" flat class="mobile-card"> <div v-if="boats">
<q-card-section> <q-card v-for="boat in boats" :key="boat.id" flat class="mobile-card">
<q-img :src="boat.imgSrc" :fit="'scale-down'"> <q-card-section>
<div class="row absolute-top"> <q-img :src="boat.imgSrc" :fit="'scale-down'">
<div class="col text-h6 text-left">{{ boat.name }}</div> <div class="row absolute-top">
<div class="col text-right">{{ boat.class }}</div> <div class="col text-h6 text-left">{{ boat.name }}</div>
</div> <div class="col text-right">{{ boat.class }}</div>
</q-img> </div>
</q-card-section> </q-img>
</q-card-section>
<q-separator /> <q-separator />
<q-card-actions align="evenly"> <q-card-actions align="evenly">
<q-btn flat>Info</q-btn> <q-btn flat>Info</q-btn>
<q-btn flat>Book</q-btn> <q-btn flat>Book</q-btn>
<q-btn flat>Check-Out</q-btn> <q-btn flat>Check-Out</q-btn>
<q-btn flat>Check-In</q-btn> <q-btn flat>Check-In</q-btn>
</q-card-actions> </q-card-actions>
</q-card> </q-card>
</div>
<div v-else><q-card>Sorry, no boats to show you!</q-card></div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">

View File

@@ -0,0 +1,19 @@
<template>
<div class="row justify-center">
<div class="q-pa-md q-gutter-sm row">
<q-btn no-caps class="button" style="margin: 2px" @click="$emit('today')">
Today
</q-btn>
<q-btn no-caps class="button" style="margin: 2px" @click="$emit('prev')">
&lt; Prev
</q-btn>
<q-btn no-caps class="button" style="margin: 2px" @click="$emit('next')">
Next &gt;
</q-btn>
</div>
</div>
</template>
<script setup lang="ts">
defineEmits(['today', 'prev', 'next']);
</script>

View File

@@ -0,0 +1,129 @@
<template>
<q-card>
<q-card-section horizontal>
<div>
<q-input
label="Template name"
filled
dense
v-model="template.name"
v-if="editable"
/>
<q-list dense>
<q-item v-for="item in template.timeTuples" :key="item[0]">
<q-input
dense
:filled="editable"
v-model="item[0]"
type="time"
label="Start"
:readonly="!editable"
/>
<q-input
:filled="editable"
dense
v-model="item[1]"
type="time"
label="End"
:readonly="!editable"
/> </q-item></q-list
><q-btn
v-if="editable"
dense
color="primary"
size="sm"
label="Add interval"
@click="template.timeTuples.push(['00:00', '00:00'])"
/>
</div>
<q-card-actions align="right" vertical>
<q-btn
v-if="!editable"
color="primary"
icon="edit"
label="Edit"
@click="editable = !editable"
/>
<q-btn
v-if="editable"
color="primary"
icon="save"
label="Save"
@click="saveTemplate($event, template)"
/>
<q-btn
v-if="editable"
color="secondary"
icon="cancel"
label="Cancel"
@click="revert"
/>
<q-btn
color="negative"
icon="delete"
label="Delete"
@click="deleteTemplate($event, template)"
/>
</q-card-actions>
</q-card-section>
</q-card>
<q-dialog v-model="alert">
<q-card>
<q-card-section>
<div class="text-h6">Overlapped blocks!</div>
</q-card-section>
<q-card-section class="q-pt-none">
<q-chip
square
icon="schedule"
v-for="item in overlapped"
:key="item.start"
>
{{ item.start }}-{{ item.end }}</q-chip
>
</q-card-section>
<q-card-actions align="right">
<q-btn flat label="OK" color="primary" v-close-popup />
</q-card-actions> </q-card
></q-dialog>
</template>
<script setup lang="ts">
import { timeTuplesOverlapped, useScheduleStore } from 'src/stores/schedule';
import { TimeBlockTemplate } from 'src/stores/schedule.types';
import { ref } from 'vue';
const editable = ref(false);
const alert = ref(false);
const overlapped = ref();
const scheduleStore = useScheduleStore();
const template = defineModel<TimeBlockTemplate>({ required: true });
const revert = () => {
editable.value = false;
};
const deleteTemplate = (
event: Event,
template: TimeBlockTemplate | undefined
) => {
if (template?.$id) scheduleStore.deleteTimeBlockTemplate(template.$id);
};
// const edit = () => {
// // TODO: Make it so that editing the template does not affect the store. Need to be able to "cancel" editing, and revert to original data
// };
const saveTemplate = (evt: Event, template: TimeBlockTemplate | undefined) => {
if (!template) return false;
overlapped.value = timeTuplesOverlapped(template.timeTuples);
if (overlapped.value.length > 0) {
alert.value = true;
} else {
if (template.$id) {
console.log(template.$id);
scheduleStore.updateTimeBlockTemplate(template, template.$id);
} else {
scheduleStore.createTimeBlockTemplate(template);
}
editable.value = false;
}
};
</script>

View File

@@ -14,36 +14,32 @@
interval-start="06:00" interval-start="06:00"
:short-interval-label="true" :short-interval-label="true"
v-model="selectedDate" v-model="selectedDate"
:column-count="boatData.length" :column-count="boats.length"
@change="changeEvent"
v-touch-swipe.left.right="handleSwipe" v-touch-swipe.left.right="handleSwipe"
> >
<template #head-day="{ scope }"> <template #head-day="{ scope }">
<div style="text-align: center; font-weight: 800"> <div style="text-align: center; font-weight: 800">
{{ boatData[scope.columnIndex].displayName }} {{ getBoatDisplayName(scope) }}
</div> </div>
</template> </template>
<template #day-body="{ scope }"> <template #day-body="{ scope }">
<div <div v-for="block in getBoatBlocks(scope)" :key="block.$id">
v-for="block in boatData[scope.columnIndex].blocks"
:key="block.id"
>
<div <div
class="timeblock" class="timeblock"
:class="selectedBlock?.id === block.id ? 'selected' : ''" :class="selectedBlock?.$id === block.$id ? 'selected' : ''"
:style=" :style="
blockStyles(block, scope.timeStartPos, scope.timeDurationHeight) blockStyles(block, scope.timeStartPos, scope.timeDurationHeight)
" "
:id="block.id" :id="block.id"
@click="selectBlock($event, scope, block)" @click="selectBlock($event, scope, block)"
> >
{{ boatData[scope.columnIndex].name }}<br /> {{ boats[scope.columnIndex].name }}<br />
{{ selectedBlock?.id === block.id ? 'Selected' : 'Available' }} {{ selectedBlock?.$id === block.$id ? 'Selected' : 'Available' }}
</div> </div>
</div> </div>
<div <!-- <div
v-for="r in boatData[scope.columnIndex].reservations" v-for="r in boats[scope.columnIndex].reservations"
:key="r.id" :key="r.id"
> >
<div <div
@@ -58,7 +54,7 @@
> >
{{ r.user }} {{ r.user }}
</div> </div>
</div> </div> -->
</template> </template>
</QCalendarDay> </QCalendarDay>
</div> </div>
@@ -71,61 +67,58 @@ import {
Timestamp, Timestamp,
diffTimestamp, diffTimestamp,
today, today,
parsed,
parseTimestamp, parseTimestamp,
parseDate, parseDate,
addToDate, addToDate,
makeDateTime,
} from '@quasar/quasar-ui-qcalendar'; } from '@quasar/quasar-ui-qcalendar';
import CalendarHeaderComponent from './CalendarHeaderComponent.vue'; import CalendarHeaderComponent from './CalendarHeaderComponent.vue';
import { ref, computed } from 'vue'; import { ref, computed } from 'vue';
import { Boat, useBoatStore } from 'src/stores/boat'; import { useBoatStore } from 'src/stores/boat';
import { useScheduleStore } from 'src/stores/schedule'; import { useScheduleStore } from 'src/stores/schedule';
import { Reservation, Timeblock } from 'src/stores/schedule.types'; import { TimeBlock } from 'src/stores/schedule.types';
import { date } from 'quasar'; import { storeToRefs } from 'pinia';
interface BoatData extends Boat {
blocks?: Timeblock[];
reservations?: Reservation[];
}
const scheduleStore = useScheduleStore(); const scheduleStore = useScheduleStore();
const boatStore = useBoatStore(); const { boats } = storeToRefs(useBoatStore());
const selectedBlock = defineModel<Timeblock | null>(); const selectedBlock = defineModel<TimeBlock | null>();
const selectedDate = ref(today()); const selectedDate = ref(today());
const boatData = ref<BoatData[]>(boatStore.boats);
const calendar = ref<QCalendarDay | null>(null); const calendar = ref<QCalendarDay | null>(null);
function handleSwipe({ ...event }) { function handleSwipe({ ...event }) {
event.direction === 'right' ? calendar.value?.prev() : calendar.value?.next(); event.direction === 'right' ? calendar.value?.prev() : calendar.value?.next();
} }
function reservationStyles( // function reservationStyles(
reservation: Reservation, // reservation: Reservation,
// timeStartPos: (t: string) => string,
// timeDurationHeight: (d: number) => string
// ) {
// return genericBlockStyle(
// parseDate(reservation.start) as Timestamp,
// parseDate(reservation.end) as Timestamp,
// timeStartPos,
// timeDurationHeight
// );
// }
function blockStyles(
block: TimeBlock,
timeStartPos: (t: string) => string, timeStartPos: (t: string) => string,
timeDurationHeight: (d: number) => string timeDurationHeight: (d: number) => string
) { ) {
return genericBlockStyle( return genericBlockStyle(
parseDate(reservation.start) as Timestamp, parseDate(new Date(block.start)) as Timestamp,
parseDate(reservation.end) as Timestamp, parseDate(new Date(block.end)) as Timestamp,
timeStartPos, timeStartPos,
timeDurationHeight timeDurationHeight
); );
} }
function blockStyles( function getBoatDisplayName(scope: DayBodyScope) {
block: Timeblock, return boats && boats.value[scope.columnIndex]
timeStartPos: (t: string) => string, ? boats.value[scope.columnIndex].displayName
timeDurationHeight: (d: number) => string : '';
) {
return genericBlockStyle(
parsed(block.start) as Timestamp,
parsed(block.end) as Timestamp,
timeStartPos,
timeDurationHeight
);
} }
function genericBlockStyle( function genericBlockStyle(
@@ -161,42 +154,61 @@ interface DayBodyScope {
timestamp: Timestamp; timestamp: Timestamp;
} }
function selectBlock(event: MouseEvent, scope: DayBodyScope, block: Timeblock) { function selectBlock(event: MouseEvent, scope: DayBodyScope, block: TimeBlock) {
// TODO: Disable blocks before today with updateDisabled and/or comparison // TODO: Disable blocks before today with updateDisabled and/or comparison
selectedBlock.value === block selectedBlock.value === block
? (selectedBlock.value = null) ? (selectedBlock.value = null)
: (selectedBlock.value = block); : (selectedBlock.value = block);
} }
function changeEvent({ start }: { start: string }) { interface BoatBlocks {
const newBlocks = scheduleStore.getTimeblocksForDate(start); [key: string]: TimeBlock[];
const reservations = scheduleStore.getBoatReservations(
parsed(start) as Timestamp
);
boatData.value.map((boat) => {
boat.reservations = reservations.filter(
(reservation) => reservation.resource === boat
);
boat.blocks = newBlocks.filter(
(block) =>
block.boatId === boat.$id &&
boat.reservations?.filter(
(r) =>
r.start <
date.addToDate(makeDateTime(parsed(block.end) as Timestamp), {
hours: 4,
}) &&
r.end >
date.addToDate(makeDateTime(parsed(block.start) as Timestamp), {
hours: 4,
})
).length == 0
);
});
setTimeout(() => calendar.value?.scrollToTime('09:00'), 100); // Should figure out why we need this setTimeout...
} }
const boatBlocks = computed((): BoatBlocks => {
return scheduleStore
.getTimeBlocksForDate(selectedDate.value)
.reduce((result, tb) => {
if (!result[tb.boatId]) result[tb.boatId] = [];
result[tb.boatId].push(tb);
return result;
}, <BoatBlocks>{});
});
function getBoatBlocks(scope: DayBodyScope): TimeBlock[] {
return boats.value[scope.columnIndex]
? boatBlocks.value[boats.value[scope.columnIndex].$id]
: [];
}
// function changeEvent({ start }: { start: string }) {
// const newBlocks = scheduleStore.getTimeBlocksForDate(start);
// const reservations = scheduleStore.getBoatReservations(
// parsed(start) as Timestamp
// );
// boats.value.map((boat) => {
// boat.reservations = reservations.filter(
// (reservation) => reservation.resource === boat
// );
// boat.blocks = newBlocks.filter(
// (block) =>
// block.boatId === boat.$id &&
// boat.reservations?.filter(
// (r: Reservation) =>
// r.start <
// date.addToDate(makeDateTime(parsed(block.end) as Timestamp), {
// hours: 4,
// }) &&
// r.end >
// date.addToDate(makeDateTime(parsed(block.start) as Timestamp), {
// hours: 4,
// })
// ).length == 0
// );
// });
// setTimeout(() => calendar.value?.scrollToTime('09:00'), 100); // Should figure out why we need this setTimeout...
// }
const disabledBefore = computed(() => { const disabledBefore = computed(() => {
const todayTs = parseTimestamp(today()) as Timestamp; const todayTs = parseTimestamp(today()) as Timestamp;
return addToDate(todayTs, { day: -1 }).date; return addToDate(todayTs, { day: -1 }).date;

View File

@@ -57,7 +57,7 @@ import { ref, reactive, computed } from 'vue';
const selectedDate = defineModel<string>(); const selectedDate = defineModel<string>();
const weekdays = reactive([0, 1, 2, 3, 4, 5, 6]), const weekdays = reactive([1, 2, 3, 4, 5, 6, 0]),
locale = ref('en-CA'), locale = ref('en-CA'),
monthFormatter = monthFormatterFunc(), monthFormatter = monthFormatterFunc(),
dayFormatter = dayFormatterFunc(), dayFormatter = dayFormatterFunc(),
@@ -124,8 +124,14 @@ function dayClass(day: Timestamp) {
} }
function monthFormatterFunc() { function monthFormatterFunc() {
const longOptions = { timeZone: 'UTC', month: 'long' }; const longOptions: Intl.DateTimeFormatOptions = {
const shortOptions = { timeZone: 'UTC', month: 'short' }; timeZone: 'UTC',
month: 'long',
};
const shortOptions: Intl.DateTimeFormatOptions = {
timeZone: 'UTC',
month: 'short',
};
return createNativeLocaleFormatter(locale.value, (_tms, short) => return createNativeLocaleFormatter(locale.value, (_tms, short) =>
short ? shortOptions : longOptions short ? shortOptions : longOptions
@@ -133,17 +139,28 @@ function monthFormatterFunc() {
} }
function weekdayFormatterFunc() { function weekdayFormatterFunc() {
const longOptions = { timeZone: 'UTC', weekday: 'long' }; const longOptions: Intl.DateTimeFormatOptions = {
const shortOptions = { timeZone: 'UTC', weekday: 'short' }; timeZone: 'UTC',
weekday: 'long',
};
const shortOptions: Intl.DateTimeFormatOptions = {
timeZone: 'UTC',
weekday: 'short',
};
return createNativeLocaleFormatter(locale.value, (_tms, short) => return createNativeLocaleFormatter(locale.value, (_tms, short) =>
short ? shortOptions : longOptions short ? shortOptions : longOptions
); );
} }
function dayFormatterFunc() { function dayFormatterFunc() {
const longOptions = { timeZone: 'UTC', day: '2-digit' }; const longOptions: Intl.DateTimeFormatOptions = {
const shortOptions = { timeZone: 'UTC', day: 'numeric' }; timeZone: 'UTC',
day: '2-digit',
};
const shortOptions: Intl.DateTimeFormatOptions = {
timeZone: 'UTC',
day: 'numeric',
};
return createNativeLocaleFormatter(locale.value, (_tms, short) => return createNativeLocaleFormatter(locale.value, (_tms, short) =>
short ? shortOptions : longOptions short ? shortOptions : longOptions

View File

@@ -20,5 +20,5 @@
import { defineProps } from 'vue'; import { defineProps } from 'vue';
import type { Task } from 'src/stores/task'; import type { Task } from 'src/stores/task';
const props = defineProps<{ task: Task }>(); defineProps<{ task: Task }>();
</script> </script>

View File

@@ -162,7 +162,7 @@ import { useRouter } from 'vue-router';
import { useTaskStore, TASKSTATUS } from 'src/stores/task'; import { useTaskStore, TASKSTATUS } from 'src/stores/task';
import type { TaskTag, SkillTag, Task } from 'src/stores/task'; import type { TaskTag, SkillTag, Task } from 'src/stores/task';
import { date } from 'quasar'; import { date } from 'quasar';
import { Boat, useBoatStore } from 'src/stores/boat'; import { useBoatStore } from 'src/stores/boat';
const props = defineProps<{ taskId?: string }>(); const props = defineProps<{ taskId?: string }>();
const taskStore = useTaskStore(); const taskStore = useTaskStore();
@@ -187,7 +187,7 @@ const targetTask = taskId && taskStore.tasks.find((t) => t.$id === taskId);
const modifiedTask = reactive(targetTask ? targetTask : defaultTask); const modifiedTask = reactive(targetTask ? targetTask : defaultTask);
let tasks = taskStore.tasks; let tasks = taskStore.tasks;
const boatList = ref<Boat[]>(useBoatStore().boats); const boatList = useBoatStore().boats;
const skillTagOptions = ref<SkillTag[]>(taskStore.skillTags); const skillTagOptions = ref<SkillTag[]>(taskStore.skillTags);
const taskTagOptions = ref<TaskTag[]>(taskStore.taskTags); const taskTagOptions = ref<TaskTag[]>(taskStore.taskTags);

View File

@@ -9,9 +9,8 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { defineProps } from 'vue';
import type { Task } from 'src/stores/task'; import type { Task } from 'src/stores/task';
import TaskCardComponent from './TaskCardComponent.vue'; import TaskCardComponent from './TaskCardComponent.vue';
const props = defineProps<{ tasks: Task[] }>(); defineProps<{ tasks: Task[] }>();
</script> </script>

View File

@@ -133,7 +133,7 @@
<q-separator /> <q-separator />
<q-list dense> <q-list dense>
<q-item <q-item
v-for="col in props.cols.filter((col) => col.name !== 'desc')" v-for="col in props.cols.filter((col:Boat) => col.name !== 'desc')"
:key="col.name" :key="col.name"
> >
<q-item-section> <q-item-section>
@@ -215,10 +215,8 @@
import { computed, defineProps, ref } from 'vue'; import { computed, defineProps, ref } from 'vue';
import { useTaskStore, Task, SkillTag, TaskTag } from 'src/stores/task'; import { useTaskStore, Task, SkillTag, TaskTag } from 'src/stores/task';
import { QTableProps, date, useQuasar } from 'quasar'; import { QTableProps, date, useQuasar } from 'quasar';
import { useBoatStore } from 'src/stores/boat'; import { Boat, useBoatStore } from 'src/stores/boat';
import { useRouter } from 'vue-router';
const router = useRouter();
const selected = ref([]); const selected = ref([]);
const loading = ref(false); // Placeholder const loading = ref(false); // Placeholder
const fabShow = ref(false); const fabShow = ref(false);
@@ -301,44 +299,51 @@ const columns = <QTableProps['columns']>[
{ name: 'actions', align: 'center', label: 'Actions', field: '$id' }, { name: 'actions', align: 'center', label: 'Actions', field: '$id' },
]; ];
const props = defineProps<{ tasks: Task[] }>(); defineProps<{ tasks: Task[] }>();
const taskStore = useTaskStore(); const taskStore = useTaskStore();
const $q = useQuasar(); const $q = useQuasar();
const searchFilter = ref({ interface SearchObject {
title: string;
skillTags: SkillTag[];
taskTags: TaskTag[];
}
const searchFilter = ref<SearchObject>({
title: '', title: '',
skillTags: <SkillTag[]>[], skillTags: [],
taskTags: <TaskTag[]>[], taskTags: [],
}); });
const skillTagOptions = ref<SkillTag[]>(taskStore.skillTags); const skillTagOptions = ref<SkillTag[]>(taskStore.skillTags);
const taskTagOptions = ref<TaskTag[]>(taskStore.taskTags); const taskTagOptions = ref<TaskTag[]>(taskStore.taskTags);
function onRowClick(evt: Event, row: Task) { // function onRowClick(evt: Event, row: Task) {
router.push({ name: 'edit-task', params: { id: row.$id } }); // router.push({ name: 'edit-task', params: { id: row.$id } });
} // }
// TODO: Implement server side search // TODO: Implement server side search
const filterRows = computed( const filterRows = computed(
() => (rows: readonly Task[], terms: any, cols: any, cellValueFn: any) => { () => (rows: readonly Task[], terms: SearchObject) => {
let result = rows; return rows
result = rows.filter((row) => .filter((row) =>
terms.title terms.title
? row.title.toLowerCase().includes(terms.title.toLowerCase()) ? row.title.toLowerCase().includes(terms.title.toLowerCase())
: true : true
); )
result = result.filter((row) => .filter((row) =>
terms.skillTags && terms.skillTags.length > 0 terms.skillTags && terms.skillTags.length > 0
? row.required_skills.some((req_skill) => ? row.required_skills.some((req_skill) =>
terms.skillTags.map((t) => t.$id).includes(req_skill) terms.skillTags.map((t) => t.$id).includes(req_skill)
) )
: true : true
); )
result = result.filter((row) => .filter((row) =>
terms.taskTags && terms.taskTags.length > 0 terms.taskTags && terms.taskTags.length > 0
? row.tags.some((tag) => terms.taskTags.map((t) => t.$id).includes(tag)) ? row.tags.some((tag) =>
: true terms.taskTags.map((t) => t.$id).includes(tag)
); )
return result; : true
);
} }
); );

View File

@@ -11,5 +11,7 @@ import { ref } from 'vue';
import { useBoatStore } from 'src/stores/boat'; import { useBoatStore } from 'src/stores/boat';
import ToolbarComponent from 'src/components/ToolbarComponent.vue'; import ToolbarComponent from 'src/components/ToolbarComponent.vue';
const boatStore = useBoatStore();
boatStore.fetchBoats();
const boats = ref(useBoatStore().boats); const boats = ref(useBoatStore().boats);
</script> </script>

View File

@@ -8,32 +8,25 @@
<q-avatar icon="person" /> <q-avatar icon="person" />
</q-item-section> </q-item-section>
<q-item-section> <q-item-section>
Ricky Gervais {{ authStore.currentUser?.name }}
<q-item-label caption>Name</q-item-label> <q-item-label caption>Name</q-item-label>
</q-item-section> </q-item-section>
</q-item> </q-item>
<q-item>
<q-item-section avatar>
<q-avatar icon="numbers" />
</q-item-section>
<q-item-section>
123456
<q-item-label caption>Member ID</q-item-label>
</q-item-section>
</q-item>
<q-separator /> <q-separator />
<q-item> <q-item>
<q-item-section> <q-item-section>
<q-item-label overline>Certifications</q-item-label> <q-item-label overline>Certifications</q-item-label>
<q-chip square icon="verified" color="green" text-color="white" <div>
>J/27</q-chip <q-chip square icon="verified" color="green" text-color="white"
> >J/27</q-chip
<q-chip square icon="verified" color="blue" text-color="white" >
>Capri25</q-chip <q-chip square icon="verified" color="blue" text-color="white"
> >Capri25</q-chip
<q-chip square icon="verified" color="red" text-color="white" >
>Night</q-chip <q-chip square icon="verified" color="grey-9" text-color="white"
> >Night</q-chip
>
</div>
</q-item-section> </q-item-section>
</q-item> </q-item>
</q-list> </q-list>
@@ -42,4 +35,7 @@
<script setup lang="ts"> <script setup lang="ts">
import ToolbarComponent from 'src/components/ToolbarComponent.vue'; import ToolbarComponent from 'src/components/ToolbarComponent.vue';
import { useAuthStore } from 'src/stores/auth';
const authStore = useAuthStore();
</script> </script>

View File

@@ -30,7 +30,7 @@
rounded rounded
class="bg-warning text-grey-10" class="bg-warning text-grey-10"
style="max-width: 95vw; margin: auto" style="max-width: 95vw; margin: auto"
v-if="bookingForm.boat?.defects" v-if="bookingForm.boat && bookingForm.boat.defects.length > 0"
> >
<template v-slot:avatar> <template v-slot:avatar>
<q-icon name="warning" color="grey-10" /> <q-icon name="warning" color="grey-10" />
@@ -63,7 +63,7 @@
><q-banner v-if="bookingForm.boat" ><q-banner v-if="bookingForm.boat"
>Passengers: >Passengers:
{{ bookingForm.members.length + bookingForm.guests.length }} / {{ bookingForm.members.length + bookingForm.guests.length }} /
{{ bookingForm.boat.booking?.maxPassengers }}</q-banner {{ bookingForm.boat.maxPassengers }}</q-banner
> >
<q-item <q-item
class="q-my-sm" class="q-my-sm"
@@ -102,7 +102,7 @@ import { useAuthStore } from 'src/stores/auth';
import { Boat, useBoatStore } from 'src/stores/boat'; import { Boat, useBoatStore } from 'src/stores/boat';
import { date } from 'quasar'; import { date } from 'quasar';
import { useScheduleStore } from 'src/stores/schedule'; import { useScheduleStore } from 'src/stores/schedule';
import { Timeblock } from 'src/stores/schedule.types'; import { TimeBlock } from 'src/stores/schedule.types';
import BoatScheduleTableComponent from 'src/components/scheduling/boat/BoatScheduleTableComponent.vue'; import BoatScheduleTableComponent from 'src/components/scheduling/boat/BoatScheduleTableComponent.vue';
interface BookingForm { interface BookingForm {
@@ -119,7 +119,7 @@ const auth = useAuthStore();
const dateFormat = 'MMM D, YYYY h:mm A'; const dateFormat = 'MMM D, YYYY h:mm A';
const resourceView = ref(true); const resourceView = ref(true);
const scheduleStore = useScheduleStore(); const scheduleStore = useScheduleStore();
const timeblock = ref<Timeblock>(); const timeblock = ref<TimeBlock>();
const bookingForm = ref<BookingForm>({ const bookingForm = ref<BookingForm>({
bookingId: scheduleStore.getNewId(), bookingId: scheduleStore.getNewId(),
name: auth.currentUser?.name, name: auth.currentUser?.name,
@@ -131,7 +131,6 @@ const bookingForm = ref<BookingForm>({
}); });
watch(timeblock, (tb_new) => { watch(timeblock, (tb_new) => {
console.log('Hi');
bookingForm.value.boat = useBoatStore().boats.find( bookingForm.value.boat = useBoatStore().boats.find(
(b) => b.$id === tb_new?.boatId (b) => b.$id === tb_new?.boatId
); );

View File

@@ -50,25 +50,17 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { Reservation, useScheduleStore } from 'src/stores/schedule'; import { useScheduleStore } from 'src/stores/schedule';
import { Reservation } from 'src/stores/schedule.types';
import { ref } from 'vue'; import { ref } from 'vue';
const scheduleStore = useScheduleStore(); const scheduleStore = useScheduleStore();
import { import { TimestampOrNull, parseDate, today } from '@quasar/quasar-ui-qcalendar';
TimestampOrNull,
makeDateTime,
makeDate,
parseDate,
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';
const selectedDate = ref(today()); const selectedDate = ref(today());
// Use ref to get a reference to the QCalendarDay component
const calendarRef = ref(QCalendarDay);
// Method declarations // Method declarations
function slotStyle( function slotStyle(
@@ -93,32 +85,22 @@ function slotStyle(
function reservationEvents(timestamp: Timestamp) { function reservationEvents(timestamp: Timestamp) {
return scheduleStore.getBoatReservations(timestamp); return scheduleStore.getBoatReservations(timestamp);
} }
function onMoved(data: Event) {
function onToday() {
calendarRef.value.moveToToday();
}
function onPrev() {
calendarRef.value.prev();
}
function onNext() {
calendarRef.value.next();
}
function onMoved(data) {
console.log('onMoved', data); console.log('onMoved', data);
} }
function onChange(data) { function onChange(data: Event) {
console.log('onChange', data); console.log('onChange', data);
} }
function onClickDate(data) { function onClickDate(data: Event) {
console.log('onClickDate', data); console.log('onClickDate', data);
} }
function onClickTime(data) { function onClickTime(data: Event) {
console.log('onClickTime', data); console.log('onClickTime', data);
} }
function onClickInterval(data) { function onClickInterval(data: Event) {
console.log('onClickInterval', data); console.log('onClickInterval', data);
} }
function onClickHeadDay(data) { function onClickHeadDay(data: Event) {
console.log('onClickHeadDay', data); console.log('onClickHeadDay', data);
} }
</script> </script>

View File

@@ -0,0 +1,188 @@
<template>
<div class="fit row wrap justify-start items-start content-start">
<div class="col-9 q-pa-md">
<div class="scheduler">
<NavigationBar @next="onNext" @today="onToday" @prev="onPrev" />
<q-calendar-scheduler
ref="calendar"
v-model="selectedDate"
v-model:model-resources="boats"
resource-key="$id"
resource-label="name"
view="week"
:weekdays="[1, 2, 3, 4, 5, 6, 0]"
hoverable
animated
bordered
:drag-enter-func="onDragEnter"
:drag-over-func="onDragOver"
:drag-leave-func="onDragLeave"
:drop-func="onDrop"
:day-min-height="50"
:day-height="0"
>
<template #day="{ scope }">
<div
v-if="getTimeBlocks(scope.timestamp, scope.resource)"
style="
display: flex;
flex: 1 0 auto;
flex-wrap: wrap;
justify-content: space-evenly;
align-items: center;
font-size: 12px;
"
>
<template
v-for="event in getTimeBlocks(scope.timestamp, scope.resource)"
:key="event.id"
>
<q-chip clickable square icon="schedule">
{{ date.formatDate(event.start, 'HH:mm') }} -
{{ date.formatDate(event.end, 'HH:mm') }}</q-chip
>
</template>
</div>
</template>
</q-calendar-scheduler>
</div>
</div>
<div class="col-3 q-pa-md">
<q-list padding bordered class="rounded-borders">
<q-item>
<q-item-section>
<q-item-label overline>Availability Templates</q-item-label>
<q-item-label caption
>Drag and drop a template to a boat / date to create booking
availability</q-item-label
>
</q-item-section>
</q-item>
<q-card-actions align="right">
<q-btn label="Add Template" color="primary" @click="addTemplate" />
</q-card-actions>
<q-separator spaced />
<q-expansion-item
v-for="template in timeblockTemplates"
:key="template.$id"
dense
dense-toggle
expand-separator
:label="template.name"
style="font-size: 0.8em"
draggable="true"
@dragstart="onDragStart($event, template)"
>
<TimeBlockTemplateComponent
:model-value="template"
/> </q-expansion-item
></q-list>
</div>
</div>
</template>
<script setup lang="ts">
import {
QCalendarScheduler,
Timestamp,
today,
} from '@quasar/quasar-ui-qcalendar';
import { Boat, useBoatStore } from 'src/stores/boat';
import { buildTimeBlock, useScheduleStore } from 'src/stores/schedule';
import { onMounted, ref } from 'vue';
import type { TimeBlockTemplate, TimeTuple } from 'src/stores/schedule.types';
import { date } from 'quasar';
import TimeBlockTemplateComponent from 'src/components/scheduling/TimeBlockTemplateComponent.vue';
import NavigationBar from 'src/components/scheduling/NavigationBar.vue';
import { storeToRefs } from 'pinia';
const selectedDate = ref(today());
const { fetchBoats } = useBoatStore();
const { getTimeBlocks, fetchTimeBlocks, fetchTimeBlockTemplates } =
useScheduleStore();
const { boats } = storeToRefs(useBoatStore());
const { timeblockTemplates } = storeToRefs(useScheduleStore());
const calendar = ref();
onMounted(async () => {
await fetchBoats();
await fetchTimeBlocks();
await fetchTimeBlockTemplates();
});
function addTemplate() {
timeblockTemplates.value.push({ name: 'New Template', timeTuples: [] });
}
function createTimeBlock(boat: Boat, templateId: string, date: string) {
const timeBlock = timeblockTemplates.value.find((t) => t.$id === templateId);
timeBlock?.timeTuples.map((tb: TimeTuple) =>
useScheduleStore().createTimeBlock(buildTimeBlock(boat, tb, date))
);
}
function onDragStart(e: DragEvent, template: TimeBlockTemplate) {
if (e.dataTransfer) {
console.log('Drag start: ', e);
e.dataTransfer.dropEffect = 'copy';
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('ID', template.$id || '');
}
}
function onDragEnter(e: DragEvent, type: string) {
console.log('onDragEnter', e, type);
if (type === 'day' || type === 'head-day') {
e.preventDefault();
if (e.target instanceof HTMLDivElement)
e.target.classList.add('bg-secondary');
}
}
function onDragOver(e: DragEvent, type: string) {
console.log('onDragOver');
if (type === 'day' || type === 'head-day') {
e.preventDefault();
return true;
}
}
function onDragLeave(e: DragEvent, type: string) {
console.log('onDragLeave');
if (type === 'day' || type === 'head-day') {
e.preventDefault();
if (e.target instanceof HTMLDivElement)
e.target.classList.remove('bg-secondary');
console.log(e.target);
return false;
}
}
function onDrop(
e: DragEvent,
type: string,
scope: { resource: Boat; timestamp: Timestamp }
) {
console.log('onDrop', e, type, scope);
if ((type === 'day' || type === 'head-day') && e.dataTransfer) {
const templateId = e.dataTransfer.getData('ID');
const date = scope.timestamp.date;
if (type === 'head-day') {
boats.value.map((r) => createTimeBlock(r, templateId, date));
} else {
createTimeBlock(scope.resource, templateId, date);
}
}
if (e.target instanceof HTMLDivElement)
e.target.classList.remove('bg-secondary');
return false;
}
function onToday() {
calendar.value.moveToToday();
}
function onPrev() {
calendar.value.prev();
}
function onNext() {
calendar.value.next();
}
</script>

View File

@@ -3,7 +3,7 @@
<q-item v-for="link in navlinks" :key="link.label"> <q-item v-for="link in navlinks" :key="link.label">
<q-btn <q-btn
:icon="link.icon" :icon="link.icon"
color="primary" :color="link.color"
size="1.25em" size="1.25em"
:to="link.to" :to="link.to"
:label="link.label" :label="link.label"
@@ -21,7 +21,19 @@ const navlinks = [
icon: 'more_time', icon: 'more_time',
to: '/schedule/book', to: '/schedule/book',
label: 'Create a Reservation', label: 'Create a Reservation',
color: 'primary',
},
{
icon: 'calendar_month',
to: '/schedule/view',
label: 'View Schedule',
color: 'primary',
},
{
icon: 'edit_calendar',
to: '/schedule/manage',
label: 'Manage Calendar',
color: 'accent',
}, },
{ icon: 'calendar_month', to: '/schedule/view', label: 'View Schedule' },
]; ];
</script> </script>

View File

@@ -38,9 +38,6 @@ export default route(function (/* { store, ssrContext } */) {
Router.beforeEach((to) => { Router.beforeEach((to) => {
const auth = useAuthStore(); const auth = useAuthStore();
if (!auth.ready) {
return false;
}
if (auth.currentUser) { if (auth.currentUser) {
return to.meta.accountRoute ? { name: 'index' } : true; return to.meta.accountRoute ? { name: 'index' } : true;
} else { } else {

View File

@@ -40,6 +40,12 @@ const routes: RouteRecordRaw[] = [
component: () => import('src/pages/schedule/BoatScheduleView.vue'), component: () => import('src/pages/schedule/BoatScheduleView.vue'),
name: 'boat-schedule', name: 'boat-schedule',
}, },
{
path: 'manage',
component: () => import('src/pages/schedule/ManageCalendar.vue'),
name: 'manage-schedule',
meta: { requiresScheduleAdmin: true },
},
], ],
}, },
{ {
@@ -96,6 +102,7 @@ const routes: RouteRecordRaw[] = [
{ {
path: '/admin', path: '/admin',
component: () => import('layouts/AdminLayout.vue'), component: () => import('layouts/AdminLayout.vue'),
meta: { requiresAdmin: true },
children: [ children: [
{ {
path: '/user', path: '/user',

View File

@@ -1,11 +1,10 @@
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { ID, account } from 'boot/appwrite'; import { ID, account } from 'boot/appwrite';
import type { Models } from 'appwrite'; import { OAuthProvider, type Models } from 'appwrite';
import { ref } from 'vue'; import { ref } from 'vue';
export const useAuthStore = defineStore('auth', () => { export const useAuthStore = defineStore('auth', () => {
const currentUser = ref<Models.User<Models.Preferences> | null>(null); const currentUser = ref<Models.User<Models.Preferences> | null>(null);
const ready = ref(false);
async function init() { async function init() {
try { try {
@@ -13,7 +12,6 @@ export const useAuthStore = defineStore('auth', () => {
} catch { } catch {
currentUser.value = null; currentUser.value = null;
} }
ready.value = true;
} }
async function register(email: string, password: string) { async function register(email: string, password: string) {
@@ -21,12 +19,12 @@ export const useAuthStore = defineStore('auth', () => {
return await login(email, password); return await login(email, password);
} }
async function login(email: string, password: string) { async function login(email: string, password: string) {
await account.createEmailSession(email, password); await account.createEmailPasswordSession(email, password);
currentUser.value = await account.get(); currentUser.value = await account.get();
} }
async function googleLogin() { async function googleLogin() {
account.createOAuth2Session( account.createOAuth2Session(
'google', OAuthProvider.Google,
'https://bab.toal.ca/', 'https://bab.toal.ca/',
'https://bab.toal.ca/#/login' 'https://bab.toal.ca/#/login'
); );
@@ -37,5 +35,5 @@ export const useAuthStore = defineStore('auth', () => {
return account.deleteSession('current').then((currentUser.value = null)); return account.deleteSession('current').then((currentUser.value = null));
} }
return { currentUser, register, login, googleLogin, logout, init, ready }; return { currentUser, register, login, googleLogin, logout, init };
}); });

View File

@@ -1,8 +1,11 @@
import { Models } from 'appwrite';
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { AppwriteIds, databases } from 'src/boot/appwrite';
import { ref } from 'vue';
// const boatSource = null; // const boatSource = null;
export interface Boat { export interface Boat extends Models.Document {
$id: string; $id: string;
name: string; name: string;
displayName?: string; displayName?: string;
@@ -10,10 +13,10 @@ export interface Boat {
year?: number; year?: number;
imgSrc?: string; imgSrc?: string;
iconSrc?: string; iconSrc?: string;
bookingAvailable?: boolean; bookingAvailable: boolean;
requiredCerts: string[]; requiredCerts: string[];
maxPassengers: number; maxPassengers: number;
defects?: { defects: {
type: string; type: string;
severity: string; severity: string;
description: string; description: string;
@@ -21,81 +24,20 @@ export interface Boat {
}[]; }[];
} }
const getSampleData = () => [ export const useBoatStore = defineStore('boat', () => {
{ const boats = ref<Boat[]>([]);
$id: '1',
name: 'ProjectX',
displayName: 'PX',
class: 'J/27',
year: 1981,
imgSrc: '/tmpimg/j27.png',
iconSrc: '/tmpimg/projectx_avatar256.png',
bookingAvailable: true,
maxPassengers: 8,
requiredCerts: [],
defects: [
{
type: 'engine',
severity: 'moderate',
description: 'Fuel line leaks at engine fitting.',
detail: `The gasket in the end of the fuel hose is damaged, and does not properly seal.
This will cause fuel to leak, and will allow air into the fuel chamber, causing a lean mixture,
and rough engine performance.`,
},
{
type: 'rigging',
severity: 'moderate',
description: 'Tiller extension is broken.',
detail:
'The tiller extension swivel is broken, and will not attach to the tiller.',
},
],
},
{
$id: '2',
name: 'Take5',
displayName: 'T5',
class: 'J/27',
year: 1985,
imgSrc: '/tmpimg/j27.png',
iconsrc: '/tmpimg/take5_avatar32.png',
bookingAvailable: true,
maxPassengers: 8,
requiredCerts: [],
},
{
$id: '3',
name: 'WeeBeestie',
displayName: 'WB',
class: 'Capri 25',
year: 1989,
imgSrc: '/tmpimg/capri25.png',
bookingAvailable: true,
maxPassengers: 6,
requiredCerts: [],
},
{
$id: '4',
name: 'Just My Imagination',
displayName: 'JMI',
class: 'Sirius 28',
year: 1989,
imgSrc: '/tmpimg/JMI.jpg',
bookingAvailable: true,
maxPassengers: 8,
requiredCerts: [],
},
];
export const useBoatStore = defineStore('boat', { async function fetchBoats() {
state: () => ({ try {
boats: getSampleData(), const response = await databases.listDocuments(
}), AppwriteIds.databaseId,
AppwriteIds.collection.boat
);
boats.value = response.documents as Boat[];
} catch (error) {
console.error('Failed to fetch boats', error);
}
}
getters: {}, return { boats, fetchBoats };
actions: {
// update () {
// }
},
}); });

View File

@@ -0,0 +1,65 @@
export const getSampleData = () => [
{
$id: '1',
name: 'ProjectX',
displayName: 'PX',
class: 'J/27',
year: 1981,
imgSrc: '/tmpimg/j27.png',
iconSrc: '/tmpimg/projectx_avatar256.png',
bookingAvailable: true,
maxPassengers: 8,
requiredCerts: [],
defects: [
{
type: 'engine',
severity: 'moderate',
description: 'Fuel line leaks at engine fitting.',
detail: `The gasket in the end of the fuel hose is damaged, and does not properly seal.
This will cause fuel to leak, and will allow air into the fuel chamber, causing a lean mixture,
and rough engine performance.`,
},
{
type: 'rigging',
severity: 'moderate',
description: 'Tiller extension is broken.',
detail:
'The tiller extension swivel is broken, and will not attach to the tiller.',
},
],
},
{
$id: '2',
name: 'Take5',
displayName: 'T5',
class: 'J/27',
year: 1985,
imgSrc: '/tmpimg/j27.png',
iconsrc: '/tmpimg/take5_avatar32.png',
bookingAvailable: true,
maxPassengers: 8,
requiredCerts: [],
},
{
$id: '3',
name: 'WeeBeestie',
displayName: 'WB',
class: 'Capri 25',
year: 1989,
imgSrc: '/tmpimg/capri25.png',
bookingAvailable: true,
maxPassengers: 6,
requiredCerts: [],
},
{
$id: '4',
name: 'Just My Imagination',
displayName: 'JMI',
class: 'Sirius 28',
year: 1989,
imgSrc: '/tmpimg/JMI.jpg',
bookingAvailable: true,
maxPassengers: 8,
requiredCerts: [],
},
];

View File

@@ -11,13 +11,14 @@ import type {
StatusTypes, StatusTypes,
Reservation, Reservation,
TimeBlockTemplate, TimeBlockTemplate,
Timeblock, TimeBlock,
TimeTuple,
} from '../schedule.types'; } from '../schedule.types';
export const templateA: TimeBlockTemplate = { export const templateA: TimeBlockTemplate = {
id: '1', id: '1',
name: 'WeekdayBlocks', name: 'WeekdayBlocks',
blocks: [ timeTuples: [
['08:00', '12:00'], ['08:00', '12:00'],
['12:00', '16:00'], ['12:00', '16:00'],
['17:00', '21:00'], ['17:00', '21:00'],
@@ -27,7 +28,7 @@ export const templateA: TimeBlockTemplate = {
export const templateB: TimeBlockTemplate = { export const templateB: TimeBlockTemplate = {
id: '2', id: '2',
name: 'WeekendBlocks', name: 'WeekendBlocks',
blocks: [ timeTuples: [
['07:00', '10:00'], ['07:00', '10:00'],
['10:00', '13:00'], ['10:00', '13:00'],
['13:00', '16:00'], ['13:00', '16:00'],
@@ -35,20 +36,20 @@ export const templateB: TimeBlockTemplate = {
], ],
}; };
export function getSampleTimeBlocks(): Timeblock[] { export function getSampleTimeBlocks(): TimeBlock[] {
// Hard-code 30 days worth of blocks, for now. Make them random templates // Hard-code 30 days worth of blocks, for now. Make them random templates
const boats = useBoatStore().boats; const boats = useBoatStore().boats;
const result: Timeblock[] = []; const result: TimeBlock[] = [];
const tsToday: Timestamp = parseTimestamp(today()) as Timestamp; const tsToday: Timestamp = parseTimestamp(today()) as Timestamp;
for (let i = 0; i <= 30; i++) { for (let i = 0; i <= 30; i++) {
const template = templateB; const template = templateB;
result.push( result.push(
...boats ...boats
.map((b): Timeblock[] => { .map((b): TimeBlock[] => {
return template.blocks.map((t): Timeblock => { return template.blocks.map((t: TimeTuple): TimeBlock => {
return { return {
id: 'id' + Math.random().toString(32).slice(2), $id: 'id' + Math.random().toString(32).slice(2),
boatId: b.$id, boatId: b.$id,
start: addToDate(tsToday, { day: i }).date + ' ' + t[0], start: addToDate(tsToday, { day: i }).date + ' ' + t[0],
end: addToDate(tsToday, { day: i }).date + ' ' + t[1], end: addToDate(tsToday, { day: i }).date + ' ' + t[1],

View File

@@ -8,21 +8,92 @@ import {
compareDate, compareDate,
} from '@quasar/quasar-ui-qcalendar'; } from '@quasar/quasar-ui-qcalendar';
import { Reservation, Timeblock } from './schedule.types';
import { import {
getSampleReservations, Reservation,
getSampleTimeBlocks, TimeBlockTemplate,
} from './sampledata/schedule'; TimeTuple,
TimeBlock,
} from './schedule.types';
import { AppwriteIds, databases } from 'src/boot/appwrite';
import { ID, Models } from 'appwrite';
export type Interval = {
start: string;
end: string;
};
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 {
start: '01/01/2001 ' + tuples[0],
end: '01/01/2001 ' + tuples[1],
};
})
).map((t) => {
return { start: t.start.split(' ')[1], end: t.end.split(' ')[1] };
});
}
export function blocksOverlapped(blocks: TimeBlock[] | 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 buildTimeBlock(
resource: Boat,
time: TimeTuple,
blockDate: string
): TimeBlock {
/* 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;
}
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 reservations = ref<Reservation[]>(getSampleReservations()); const reservations = ref<Reservation[]>([]);
const timeblocks = ref<Timeblock[]>(getSampleTimeBlocks()); const timeblocks = ref<TimeBlock[]>([]);
const timeblockTemplates = ref<TimeBlockTemplate[]>([]);
const getTimeblocksForDate = (date: string): Timeblock[] => { const getTimeBlocks = (date: Timestamp, boat: Boat): TimeBlock[] => {
return timeblocks.value.filter((b) => return timeblocks.value.filter((block) => {
compareDate(parsed(b.start) as Timestamp, parsed(date) as Timestamp) return (
); compareDate(parseDate(new Date(block.start)) as Timestamp, date) &&
block.boatId === boat.$id
);
});
};
const getTimeBlocksForDate = (date: string): TimeBlock[] => {
// TODO: This needs to actually make sure we have the dates we need, stay in sync, etc.
return timeblocks.value.filter((b) => {
return compareDate(
parseDate(new Date(b.start)) as Timestamp,
parsed(date) as Timestamp
);
});
}; };
const getBoatReservations = ( const getBoatReservations = (
@@ -40,7 +111,37 @@ export const useScheduleStore = defineStore('schedule', () => {
}); });
}; };
// const getConflicts = (timeblock: Timeblock, boat: Boat) => { async function fetchTimeBlocks() {
try {
const response = await databases.listDocuments(
AppwriteIds.databaseId,
AppwriteIds.collection.timeBlock
);
timeblocks.value = response.documents as TimeBlock[];
} catch (error) {
console.error('Failed to fetch timeblocks', error);
}
}
async function fetchTimeBlockTemplates() {
try {
const response = await databases.listDocuments(
AppwriteIds.databaseId,
AppwriteIds.collection.timeBlockTemplate
);
timeblockTemplates.value = response.documents.map(
(d: Models.Document): TimeBlockTemplate => {
return {
...d,
timeTuples: arrayToTimeTuples(d.timeTuple),
} as TimeBlockTemplate;
}
);
} catch (error) {
console.error('Failed to fetch timeblock templates', error);
}
}
// const getConflicts = (timeblock: TimeBlock, boat: Boat) => {
// const start = date.buildDate({ // const start = date.buildDate({
// hour: timeblock.start.hour, // hour: timeblock.start.hour,
// minute: timeblock.start.minute, // minute: timeblock.start.minute,
@@ -55,6 +156,7 @@ export const useScheduleStore = defineStore('schedule', () => {
// }); // });
// return scheduleStore.getConflictingReservations(boat, start, end); // return scheduleStore.getConflictingReservations(boat, start, end);
// }; // };
const getConflictingReservations = ( const getConflictingReservations = (
resource: Boat, resource: Boat,
start: Date, start: Date,
@@ -98,13 +200,85 @@ export const useScheduleStore = defineStore('schedule', () => {
: reservations.value.push(reservation); : reservations.value.push(reservation);
}; };
const createTimeBlock = async (block: TimeBlock) => {
try {
const response = await databases.createDocument(
AppwriteIds.databaseId,
AppwriteIds.collection.timeBlock,
ID.unique(),
block
);
timeblocks.value.push(response as TimeBlock);
} catch (e) {
console.error('Error creating TimeBlock: ' + e);
}
};
const createTimeBlockTemplate = async (template: TimeBlockTemplate) => {
try {
const response = await databases.createDocument(
AppwriteIds.databaseId,
AppwriteIds.collection.timeBlockTemplate,
ID.unique(),
{ name: template.name, timeTuple: template.timeTuples.flat(2) }
);
timeblockTemplates.value.push(response as TimeBlockTemplate);
} catch (e) {
console.error('Error updating TimeBlockTemplate: ' + e);
}
};
const deleteTimeBlockTemplate = async (id: string) => {
try {
await databases.deleteDocument(
AppwriteIds.databaseId,
AppwriteIds.collection.timeBlockTemplate,
id
);
timeblockTemplates.value = timeblockTemplates.value.filter(
(template) => template.$id !== id
);
} catch (e) {
console.error('Error deleting TimeBlockTemplate: ' + e);
}
};
const updateTimeBlockTemplate = async (
template: TimeBlockTemplate,
id: string
) => {
try {
const response = await databases.updateDocument(
AppwriteIds.databaseId,
AppwriteIds.collection.timeBlockTemplate,
id,
{
name: template.name,
timeTuple: template.timeTuples.flat(2),
}
);
timeblockTemplates.value = timeblockTemplates.value.map((b) =>
b.$id !== id ? b : (response as TimeBlockTemplate)
);
} catch (e) {
console.error('Error updating TimeBlockTemplate: ' + e);
}
};
return { return {
reservations, reservations,
timeblocks,
timeblockTemplates,
getBoatReservations, getBoatReservations,
getConflictingReservations, getConflictingReservations,
getTimeblocksForDate, getTimeBlocksForDate,
getTimeBlocks,
fetchTimeBlocks,
fetchTimeBlockTemplates,
getNewId, getNewId,
addOrCreateReservation, addOrCreateReservation,
createTimeBlock,
createTimeBlockTemplate,
deleteTimeBlockTemplate,
updateTimeBlockTemplate,
isReservationOverlapped, isReservationOverlapped,
isResourceTimeOverlapped, isResourceTimeOverlapped,
}; };

View File

@@ -1,3 +1,4 @@
import { Models } from 'appwrite';
import type { Boat } from './boat'; import type { Boat } from './boat';
export type StatusTypes = 'tentative' | 'confirmed' | 'pending' | undefined; export type StatusTypes = 'tentative' | 'confirmed' | 'pending' | undefined;
@@ -16,17 +17,15 @@ export interface Reservation {
e.g.: Should there be any qcalendar stuff in this store? Or should we have just JS Date e.g.: Should there be any qcalendar stuff in this store? Or should we have just JS Date
objects in here? */ objects in here? */
export type timeTuple = [start: string, end: string]; export type TimeTuple = [start: string, end: string];
export interface Timeblock { export type TimeBlock = Partial<Models.Document> & {
id: string;
boatId: string; boatId: string;
start: string; start: string;
end: string; end: string;
selected?: false; selected?: false;
} };
export interface TimeBlockTemplate { export type TimeBlockTemplate = Partial<Models.Document> & {
id: string;
name: string; name: string;
blocks: timeTuple[]; timeTuples: TimeTuple[];
} };

View File

@@ -86,7 +86,7 @@ export const useTaskStore = defineStore('tasks', {
return; return;
} }
try { try {
const response = await databases.deleteDocument( await databases.deleteDocument(
AppwriteIds.databaseId, AppwriteIds.databaseId,
AppwriteIds.collection.task, AppwriteIds.collection.task,
docId docId

6
tsconfig.vue-tsc.json Normal file
View File

@@ -0,0 +1,6 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"skipLibCheck": true
}
}

260
yarn.lock
View File

@@ -19,7 +19,7 @@
jsonpointer "^5.0.0" jsonpointer "^5.0.0"
leven "^3.1.0" leven "^3.1.0"
"@babel/code-frame@^7.23.5", "@babel/code-frame@^7.24.1", "@babel/code-frame@^7.24.2": "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.23.5", "@babel/code-frame@^7.24.1", "@babel/code-frame@^7.24.2":
version "7.24.2" version "7.24.2"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.2.tgz#718b4b19841809a58b29b68cde80bc5e1aa6d9ae" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.2.tgz#718b4b19841809a58b29b68cde80bc5e1aa6d9ae"
integrity sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ== integrity sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==
@@ -1077,13 +1077,13 @@
"@nodelib/fs.scandir" "2.1.5" "@nodelib/fs.scandir" "2.1.5"
fastq "^1.6.0" fastq "^1.6.0"
"@quasar/app-vite@^1.7.4": "@quasar/app-vite@^1.9.1":
version "1.8.5" version "1.9.1"
resolved "https://registry.yarnpkg.com/@quasar/app-vite/-/app-vite-1.8.5.tgz#f4345be5f22c9a5309ec98b40b5ca56e2e376f90" resolved "https://registry.yarnpkg.com/@quasar/app-vite/-/app-vite-1.9.1.tgz#eb5a5e3fbc4bccf866c5555513df1fd986cb497d"
integrity sha512-OB5nU9qKIl3p7Ton9fLWkSQTv1I/7slfQl8izhZPPJZRY755Jn4Kz1exYUoEgJJ4cLSaUI/cpnVOL59pw53NEg== integrity sha512-IC50irZQ3kPhyhdjG15+GRav4KOMN82uesApIg91HlxdMrLNw4FJrFbwVsRgJfFjS1dT1h2qK3bhYICb8goECg==
dependencies: dependencies:
"@quasar/render-ssr-error" "^1.0.3" "@quasar/render-ssr-error" "^1.0.3"
"@quasar/vite-plugin" "^1.3.3" "@quasar/vite-plugin" "^1.7.0"
"@rollup/pluginutils" "^4.1.2" "@rollup/pluginutils" "^4.1.2"
"@types/chrome" "^0.0.208" "@types/chrome" "^0.0.208"
"@types/compression" "^1.7.2" "@types/compression" "^1.7.2"
@@ -1122,14 +1122,14 @@
resolved "https://registry.yarnpkg.com/@quasar/extras/-/extras-1.16.11.tgz#84b1efb9097a6e58c3ebfdd5da83ac658056a35c" resolved "https://registry.yarnpkg.com/@quasar/extras/-/extras-1.16.11.tgz#84b1efb9097a6e58c3ebfdd5da83ac658056a35c"
integrity sha512-sbTBHOA+Hi7ah0P6qSm+xfRXqwJ94ct3NKA3Lkq3iNPYuHD7VXbSWtP2eA7Cu9Fd0WjVoPbngf6yFGg46U3IfQ== integrity sha512-sbTBHOA+Hi7ah0P6qSm+xfRXqwJ94ct3NKA3Lkq3iNPYuHD7VXbSWtP2eA7Cu9Fd0WjVoPbngf6yFGg46U3IfQ==
"@quasar/quasar-app-extension-qcalendar@^4.0.0-beta.15": "@quasar/quasar-app-extension-qcalendar@^4.0.0-beta.16":
version "4.0.0-beta.15" version "4.0.0-beta.16"
resolved "https://registry.yarnpkg.com/@quasar/quasar-app-extension-qcalendar/-/quasar-app-extension-qcalendar-4.0.0-beta.15.tgz#1e85626a104c3a33083b7237f50ccf5f9048926a" resolved "https://registry.yarnpkg.com/@quasar/quasar-app-extension-qcalendar/-/quasar-app-extension-qcalendar-4.0.0-beta.16.tgz#5b0bdfb04db59cf6017892ff91563dd9759c7bb5"
integrity sha512-i6hQkcP70LXLfVMPZMKQjSg3681gjZmASV3vq6ULzc0LhtBiPneLdVNNtH2itkWxAmaUj+1heQDI5Pa0F7VKLQ== integrity sha512-Rj3KKjPFrE13cswlZAPcqdqi1YH9CeHMpWIw8xsNqdLhCoaRhMGbRas9fvHFLJOXpnsDaVwWINNgN/bBUyn99w==
dependencies: dependencies:
"@quasar/quasar-ui-qcalendar" "^4.0.0-beta.15" "@quasar/quasar-ui-qcalendar" "^4.0.0-beta.16"
"@quasar/quasar-ui-qcalendar@^4.0.0-beta.15": "@quasar/quasar-ui-qcalendar@^4.0.0-beta.16":
version "4.0.0-beta.16" version "4.0.0-beta.16"
resolved "https://registry.yarnpkg.com/@quasar/quasar-ui-qcalendar/-/quasar-ui-qcalendar-4.0.0-beta.16.tgz#90dca0962f1fe1068361f387893df6c5da7522e2" resolved "https://registry.yarnpkg.com/@quasar/quasar-ui-qcalendar/-/quasar-ui-qcalendar-4.0.0-beta.16.tgz#90dca0962f1fe1068361f387893df6c5da7522e2"
integrity sha512-KVbFJD1HQp91tiklv+6XsG7bq8FKK6mhhnoVzmjgoyhUAEb9csfbDPbpegy1/FzXy3o0wITe6mmRZ8nbaiMEZg== integrity sha512-KVbFJD1HQp91tiklv+6XsG7bq8FKK6mhhnoVzmjgoyhUAEb9csfbDPbpegy1/FzXy3o0wITe6mmRZ8nbaiMEZg==
@@ -1141,10 +1141,10 @@
dependencies: dependencies:
stack-trace "^1.0.0-pre2" stack-trace "^1.0.0-pre2"
"@quasar/vite-plugin@^1.3.3": "@quasar/vite-plugin@^1.7.0":
version "1.6.0" version "1.7.0"
resolved "https://registry.yarnpkg.com/@quasar/vite-plugin/-/vite-plugin-1.6.0.tgz#3b8f82656b14782fafe66b30dfac0775b87ab9dd" resolved "https://registry.yarnpkg.com/@quasar/vite-plugin/-/vite-plugin-1.7.0.tgz#8873391ed7f69677948180f6eb14aa0821747478"
integrity sha512-LmbV76G1CwWZbrEQhqyZpkRQTJyO3xpW55aXY1zWN+JhyUeG77CcMCEWteBVnJ6I6ehUPFDC9ONd2+WlwH6rNQ== integrity sha512-ia4w1n4DuPYm92MQLPNpMqLJID1WGGRyVGxkVeg8V+V25Vh3p9QBo++iuXR4sW/bCmzzx66Ko6VStsr1zp90GQ==
"@rollup/plugin-babel@^5.2.0": "@rollup/plugin-babel@^5.2.0":
version "5.3.1" version "5.3.1"
@@ -1464,6 +1464,28 @@
resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-2.3.4.tgz#966a6279060eb2d9d1a02ea1a331af071afdcf9e" resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-2.3.4.tgz#966a6279060eb2d9d1a02ea1a331af071afdcf9e"
integrity sha512-IfFNbtkbIm36O9KB8QodlwwYvTEsJb4Lll4c2IwB3VHc2gie2mSPtSzL0eYay7X2jd/2WX02FjSGTWR6OPr/zg== integrity sha512-IfFNbtkbIm36O9KB8QodlwwYvTEsJb4Lll4c2IwB3VHc2gie2mSPtSzL0eYay7X2jd/2WX02FjSGTWR6OPr/zg==
"@volar/language-core@1.11.1", "@volar/language-core@~1.11.1":
version "1.11.1"
resolved "https://registry.yarnpkg.com/@volar/language-core/-/language-core-1.11.1.tgz#ecdf12ea8dc35fb8549e517991abcbf449a5ad4f"
integrity sha512-dOcNn3i9GgZAcJt43wuaEykSluAuOkQgzni1cuxLxTV0nJKanQztp7FxyswdRILaKH+P2XZMPRp2S4MV/pElCw==
dependencies:
"@volar/source-map" "1.11.1"
"@volar/source-map@1.11.1", "@volar/source-map@~1.11.1":
version "1.11.1"
resolved "https://registry.yarnpkg.com/@volar/source-map/-/source-map-1.11.1.tgz#535b0328d9e2b7a91dff846cab4058e191f4452f"
integrity sha512-hJnOnwZ4+WT5iupLRnuzbULZ42L7BWWPMmruzwtLhJfpDVoZLjNBxHDi2sY2bgZXCKlpU5XcsMFoYrsQmPhfZg==
dependencies:
muggle-string "^0.3.1"
"@volar/typescript@~1.11.1":
version "1.11.1"
resolved "https://registry.yarnpkg.com/@volar/typescript/-/typescript-1.11.1.tgz#ba86c6f326d88e249c7f5cfe4b765be3946fd627"
integrity sha512-iU+t2mas/4lYierSnoFOeRFQUhAEMgsFuQxoxvwn5EdQopw43j+J27a4lt9LMInx1gLJBC6qL14WYGlgymaSMQ==
dependencies:
"@volar/language-core" "1.11.1"
path-browserify "^1.0.1"
"@vue/compiler-core@3.4.25": "@vue/compiler-core@3.4.25":
version "3.4.25" version "3.4.25"
resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.4.25.tgz#691f59ee5014f6f2a2488fd4465f892e1e82f729" resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.4.25.tgz#691f59ee5014f6f2a2488fd4465f892e1e82f729"
@@ -1475,6 +1497,17 @@
estree-walker "^2.0.2" estree-walker "^2.0.2"
source-map-js "^1.2.0" source-map-js "^1.2.0"
"@vue/compiler-core@3.4.26":
version "3.4.26"
resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.4.26.tgz#d507886520e83a6f8339ed55ed0b2b5d84b44b73"
integrity sha512-N9Vil6Hvw7NaiyFUFBPXrAyETIGlQ8KcFMkyk6hW1Cl6NvoqvP+Y8p1Eqvx+UdqsnrnI9+HMUEJegzia3mhXmQ==
dependencies:
"@babel/parser" "^7.24.4"
"@vue/shared" "3.4.26"
entities "^4.5.0"
estree-walker "^2.0.2"
source-map-js "^1.2.0"
"@vue/compiler-dom@3.4.25": "@vue/compiler-dom@3.4.25":
version "3.4.25" version "3.4.25"
resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.4.25.tgz#b367e0c84e11d9e9f70beabdd6f6b2277fde375f" resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.4.25.tgz#b367e0c84e11d9e9f70beabdd6f6b2277fde375f"
@@ -1483,6 +1516,14 @@
"@vue/compiler-core" "3.4.25" "@vue/compiler-core" "3.4.25"
"@vue/shared" "3.4.25" "@vue/shared" "3.4.25"
"@vue/compiler-dom@^3.3.0":
version "3.4.26"
resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.4.26.tgz#acc7b788b48152d087d4bb9e655b795e3dbec554"
integrity sha512-4CWbR5vR9fMg23YqFOhr6t6WB1Fjt62d6xdFPyj8pxrYub7d+OgZaObMsoxaF9yBUHPMiPFK303v61PwAuGvZA==
dependencies:
"@vue/compiler-core" "3.4.26"
"@vue/shared" "3.4.26"
"@vue/compiler-sfc@3.4.25": "@vue/compiler-sfc@3.4.25":
version "3.4.25" version "3.4.25"
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.4.25.tgz#ceab148f81571c8b251e8a8b75a9972addf1db8b" resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.4.25.tgz#ceab148f81571c8b251e8a8b75a9972addf1db8b"
@@ -1511,6 +1552,21 @@
resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.6.1.tgz#7c14346383751d9f6ad4bea0963245b30220ef83" resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.6.1.tgz#7c14346383751d9f6ad4bea0963245b30220ef83"
integrity sha512-LgPscpE3Vs0x96PzSSB4IGVSZXZBZHpfxs+ZA1d+VEPwHdOXowy/Y2CsvCAIFrf+ssVU1pD1jidj505EpUnfbA== integrity sha512-LgPscpE3Vs0x96PzSSB4IGVSZXZBZHpfxs+ZA1d+VEPwHdOXowy/Y2CsvCAIFrf+ssVU1pD1jidj505EpUnfbA==
"@vue/language-core@1.8.27":
version "1.8.27"
resolved "https://registry.yarnpkg.com/@vue/language-core/-/language-core-1.8.27.tgz#2ca6892cb524e024a44e554e4c55d7a23e72263f"
integrity sha512-L8Kc27VdQserNaCUNiSFdDl9LWT24ly8Hpwf1ECy3aFb9m6bDhBGQYOujDm21N7EW3moKIOKEanQwe1q5BK+mA==
dependencies:
"@volar/language-core" "~1.11.1"
"@volar/source-map" "~1.11.1"
"@vue/compiler-dom" "^3.3.0"
"@vue/shared" "^3.3.0"
computeds "^0.0.1"
minimatch "^9.0.3"
muggle-string "^0.3.1"
path-browserify "^1.0.1"
vue-template-compiler "^2.7.14"
"@vue/reactivity@3.4.25": "@vue/reactivity@3.4.25":
version "3.4.25" version "3.4.25"
resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.4.25.tgz#74983b146e06ce3341d15382669350125375d36f" resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.4.25.tgz#74983b146e06ce3341d15382669350125375d36f"
@@ -1548,6 +1604,11 @@
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.4.25.tgz#243ba8543e7401751e0ca319f75a80f153edd273" resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.4.25.tgz#243ba8543e7401751e0ca319f75a80f153edd273"
integrity sha512-k0yappJ77g2+KNrIaF0FFnzwLvUBLUYr8VOwz+/6vLsmItFp51AcxLL7Ey3iPd7BIRyWPOcqUjMnm7OkahXllA== integrity sha512-k0yappJ77g2+KNrIaF0FFnzwLvUBLUYr8VOwz+/6vLsmItFp51AcxLL7Ey3iPd7BIRyWPOcqUjMnm7OkahXllA==
"@vue/shared@3.4.26", "@vue/shared@^3.3.0":
version "3.4.26"
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.4.26.tgz#f17854fb1faf889854aed4b23b60e86a8cab6403"
integrity sha512-Fg4zwR0GNnjzodMt3KRy2AWGMKQXByl56+4HjN87soxLNU9P5xcJkstAlIeEF3cU6UYOzmJl1tV0dVPGIljCnQ==
accepts@~1.3.5, accepts@~1.3.8: accepts@~1.3.5, accepts@~1.3.8:
version "1.3.8" version "1.3.8"
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e"
@@ -1586,7 +1647,7 @@ ajv@^8.0.1, ajv@^8.6.0:
require-from-string "^2.0.2" require-from-string "^2.0.2"
uri-js "^4.2.2" uri-js "^4.2.2"
ansi-escapes@^4.2.1: ansi-escapes@^4.2.1, ansi-escapes@^4.3.0:
version "4.3.2" version "4.3.2"
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e"
integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==
@@ -1620,10 +1681,10 @@ anymatch@~3.1.2:
normalize-path "^3.0.0" normalize-path "^3.0.0"
picomatch "^2.0.4" picomatch "^2.0.4"
appwrite@^13.0.0: appwrite@^14.0.1:
version "13.0.2" version "14.0.1"
resolved "https://registry.yarnpkg.com/appwrite/-/appwrite-13.0.2.tgz#225f38225a012bb7dc2a70ea777fae363f9188fa" resolved "https://registry.yarnpkg.com/appwrite/-/appwrite-14.0.1.tgz#8a7e653597b370f0b9472c007e29ca0be8af182a"
integrity sha512-ISkUXO8pojDWGx5XqknCwwikgAQye4Ni4FL+Ns8Hg42rXeyehLlmvHGjFOmpS+odT6nsWYUaXzVjV4SZuDorog== integrity sha512-ORlvfqVif/2K3qKGgGiGfMP33Zwm+xxB1fIC4Lm3sojOkDd8u8YvgKQO0Meq5UXb8Dc0Rl66Z7qlGBAfRQ04bA==
dependencies: dependencies:
cross-fetch "3.1.5" cross-fetch "3.1.5"
isomorphic-form-data "2.0.0" isomorphic-form-data "2.0.0"
@@ -1936,7 +1997,7 @@ chardet@^0.7.0:
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
"chokidar@>=3.0.0 <4.0.0", chokidar@^3.5.3: "chokidar@>=3.0.0 <4.0.0", chokidar@^3.5.1, chokidar@^3.5.3:
version "3.6.0" version "3.6.0"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b"
integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==
@@ -2044,6 +2105,11 @@ commander@^2.20.0:
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
commander@^8.0.0:
version "8.3.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66"
integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==
common-tags@^1.8.0: common-tags@^1.8.0:
version "1.8.2" version "1.8.2"
resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.2.tgz#94ebb3c076d26032745fd54face7f688ef5ac9c6" resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.2.tgz#94ebb3c076d26032745fd54face7f688ef5ac9c6"
@@ -2079,6 +2145,11 @@ compression@^1.7.4:
safe-buffer "5.1.2" safe-buffer "5.1.2"
vary "~1.1.2" vary "~1.1.2"
computeds@^0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/computeds/-/computeds-0.0.1.tgz#215b08a4ba3e08a11ff6eee5d6d8d7166a97ce2e"
integrity sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==
concat-map@0.0.1: concat-map@0.0.1:
version "0.0.1" version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
@@ -2194,6 +2265,11 @@ data-view-byte-offset@^1.0.0:
es-errors "^1.3.0" es-errors "^1.3.0"
is-data-view "^1.0.1" is-data-view "^1.0.1"
de-indent@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"
integrity sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==
debug@2.6.9: debug@2.6.9:
version "2.6.9" version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
@@ -2904,7 +2980,7 @@ fast-glob@3.2.12:
merge2 "^1.3.0" merge2 "^1.3.0"
micromatch "^4.0.4" micromatch "^4.0.4"
fast-glob@^3.2.9: fast-glob@^3.2.7, fast-glob@^3.2.9:
version "3.3.2" version "3.3.2"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129"
integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==
@@ -3240,6 +3316,11 @@ hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2:
dependencies: dependencies:
function-bind "^1.1.2" function-bind "^1.1.2"
he@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
html-minifier-terser@^7.2.0: html-minifier-terser@^7.2.0:
version "7.2.0" version "7.2.0"
resolved "https://registry.yarnpkg.com/html-minifier-terser/-/html-minifier-terser-7.2.0.tgz#18752e23a2f0ed4b0f550f217bb41693e975b942" resolved "https://registry.yarnpkg.com/html-minifier-terser/-/html-minifier-terser-7.2.0.tgz#18752e23a2f0ed4b0f550f217bb41693e975b942"
@@ -3846,7 +3927,7 @@ mimic-fn@^2.1.0:
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
version "3.1.2" version "3.1.2"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
@@ -3860,6 +3941,13 @@ minimatch@^5.0.1, minimatch@^5.1.0:
dependencies: dependencies:
brace-expansion "^2.0.1" brace-expansion "^2.0.1"
minimatch@^9.0.3:
version "9.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.4.tgz#8e49c731d1749cbec05050ee5145147b32496a51"
integrity sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==
dependencies:
brace-expansion "^2.0.1"
minimist@^1.2.6: minimist@^1.2.6:
version "1.2.8" version "1.2.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
@@ -3880,6 +3968,11 @@ ms@2.1.3:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
muggle-string@^0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/muggle-string/-/muggle-string-0.3.1.tgz#e524312eb1728c63dd0b2ac49e3282e6ed85963a"
integrity sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==
mute-stream@0.0.8: mute-stream@0.0.8:
version "0.0.8" version "0.0.8"
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d"
@@ -3935,6 +4028,13 @@ normalize-range@^0.1.2:
resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942"
integrity sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA== integrity sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==
npm-run-path@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea"
integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==
dependencies:
path-key "^3.0.0"
nth-check@^2.1.1: nth-check@^2.1.1:
version "2.1.1" version "2.1.1"
resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d"
@@ -4071,6 +4171,11 @@ pascal-case@^3.1.2:
no-case "^3.0.4" no-case "^3.0.4"
tslib "^2.0.3" tslib "^2.0.3"
path-browserify@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd"
integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==
path-exists@^4.0.0: path-exists@^4.0.0:
version "4.0.0" version "4.0.0"
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
@@ -4081,7 +4186,7 @@ path-is-absolute@^1.0.0:
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==
path-key@^3.1.0: path-key@^3.0.0, path-key@^3.1.0:
version "3.1.1" version "3.1.1"
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
@@ -4186,10 +4291,10 @@ qs@6.11.0:
dependencies: dependencies:
side-channel "^1.0.4" side-channel "^1.0.4"
quasar@^2.15.2: quasar@^2.16.0:
version "2.15.4" version "2.16.0"
resolved "https://registry.yarnpkg.com/quasar/-/quasar-2.15.4.tgz#764bd886671f98d75f682b1df917adaf7dc4a849" resolved "https://registry.yarnpkg.com/quasar/-/quasar-2.16.0.tgz#c168a3a135fb67c39bd1e8e5fa82880a7dd2a412"
integrity sha512-6Rtj0KrsVA0IV9zMZ6R7U7hOpwLS/6E06hsISVHRPn21KEm3XAwHdvy9xWz5kwqWraHRlcisFSDu/KPL4VQK1w== integrity sha512-j0MSuGuIAOQdtg/zEn/7jMIZjqS00Kp4t4h/0+HCqEkf6mxtwOJoaC7s0rIC+6AVYIErCTiXrp7Hmkt32Hom1w==
queue-microtask@^1.2.2: queue-microtask@^1.2.2:
version "1.2.3" version "1.2.3"
@@ -4457,7 +4562,7 @@ semver@^6.3.1:
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
semver@^7.3.5, semver@^7.3.6, semver@^7.3.7, semver@^7.6.0: semver@^7.3.4, semver@^7.3.5, semver@^7.3.6, semver@^7.3.7, semver@^7.5.0, semver@^7.5.4, semver@^7.6.0:
version "7.6.0" version "7.6.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d" resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d"
integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg== integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==
@@ -4796,6 +4901,11 @@ through@^2.3.6:
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==
tiny-invariant@^1.1.0:
version "1.3.3"
resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.3.tgz#46680b7a873a0d5d10005995eb90a70d74d60127"
integrity sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==
tmp@^0.0.33: tmp@^0.0.33:
version "0.0.33" version "0.0.33"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
@@ -4923,10 +5033,10 @@ typed-array-length@^1.0.6:
is-typed-array "^1.1.13" is-typed-array "^1.1.13"
possible-typed-array-names "^1.0.0" possible-typed-array-names "^1.0.0"
typescript@^4.5.4: typescript@~5.3.0:
version "4.9.5" version "5.3.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.3.tgz#b3ce6ba258e72e6305ba66f5c9b452aaee3ffe37"
integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== integrity sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==
unbox-primitive@^1.0.2: unbox-primitive@^1.0.2:
version "1.0.2" version "1.0.2"
@@ -5018,6 +5128,27 @@ vary@~1.1.2:
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==
vite-plugin-checker@^0.6.4:
version "0.6.4"
resolved "https://registry.yarnpkg.com/vite-plugin-checker/-/vite-plugin-checker-0.6.4.tgz#aca186ab605aa15bd2c5dd9cc6d7c8fdcbe214ec"
integrity sha512-2zKHH5oxr+ye43nReRbC2fny1nyARwhxdm0uNYp/ERy4YvU9iZpNOsueoi/luXw5gnpqRSvjcEPxXbS153O2wA==
dependencies:
"@babel/code-frame" "^7.12.13"
ansi-escapes "^4.3.0"
chalk "^4.1.1"
chokidar "^3.5.1"
commander "^8.0.0"
fast-glob "^3.2.7"
fs-extra "^11.1.0"
npm-run-path "^4.0.1"
semver "^7.5.0"
strip-ansi "^6.0.0"
tiny-invariant "^1.1.0"
vscode-languageclient "^7.0.0"
vscode-languageserver "^7.0.0"
vscode-languageserver-textdocument "^1.0.1"
vscode-uri "^3.0.2"
vite@^2.9.13: vite@^2.9.13:
version "2.9.18" version "2.9.18"
resolved "https://registry.yarnpkg.com/vite/-/vite-2.9.18.tgz#74e2a83b29da81e602dac4c293312cc575f091c7" resolved "https://registry.yarnpkg.com/vite/-/vite-2.9.18.tgz#74e2a83b29da81e602dac4c293312cc575f091c7"
@@ -5030,6 +5161,50 @@ vite@^2.9.13:
optionalDependencies: optionalDependencies:
fsevents "~2.3.2" fsevents "~2.3.2"
vscode-jsonrpc@6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz#108bdb09b4400705176b957ceca9e0880e9b6d4e"
integrity sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg==
vscode-languageclient@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-7.0.0.tgz#b505c22c21ffcf96e167799757fca07a6bad0fb2"
integrity sha512-P9AXdAPlsCgslpP9pRxYPqkNYV7Xq8300/aZDpO35j1fJm/ncize8iGswzYlcvFw5DQUx4eVk+KvfXdL0rehNg==
dependencies:
minimatch "^3.0.4"
semver "^7.3.4"
vscode-languageserver-protocol "3.16.0"
vscode-languageserver-protocol@3.16.0:
version "3.16.0"
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0.tgz#34135b61a9091db972188a07d337406a3cdbe821"
integrity sha512-sdeUoAawceQdgIfTI+sdcwkiK2KU+2cbEYA0agzM2uqaUy2UpnnGHtWTHVEtS0ES4zHU0eMFRGN+oQgDxlD66A==
dependencies:
vscode-jsonrpc "6.0.0"
vscode-languageserver-types "3.16.0"
vscode-languageserver-textdocument@^1.0.1:
version "1.0.11"
resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.11.tgz#0822a000e7d4dc083312580d7575fe9e3ba2e2bf"
integrity sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA==
vscode-languageserver-types@3.16.0:
version "3.16.0"
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz#ecf393fc121ec6974b2da3efb3155644c514e247"
integrity sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA==
vscode-languageserver@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-7.0.0.tgz#49b068c87cfcca93a356969d20f5d9bdd501c6b0"
integrity sha512-60HTx5ID+fLRcgdHfmz0LDZAXYEV68fzwG0JWwEPBode9NuMYTIxuYXPg4ngO8i8+Ou0lM7y6GzaYWbiDL0drw==
dependencies:
vscode-languageserver-protocol "3.16.0"
vscode-uri@^3.0.2:
version "3.0.8"
resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.0.8.tgz#1770938d3e72588659a172d0fd4642780083ff9f"
integrity sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==
vue-demi@>=0.14.5: vue-demi@>=0.14.5:
version "0.14.7" version "0.14.7"
resolved "https://registry.yarnpkg.com/vue-demi/-/vue-demi-0.14.7.tgz#8317536b3ef74c5b09f268f7782e70194567d8f2" resolved "https://registry.yarnpkg.com/vue-demi/-/vue-demi-0.14.7.tgz#8317536b3ef74c5b09f268f7782e70194567d8f2"
@@ -5055,6 +5230,23 @@ vue-router@4:
dependencies: dependencies:
"@vue/devtools-api" "^6.5.1" "@vue/devtools-api" "^6.5.1"
vue-template-compiler@^2.7.14:
version "2.7.16"
resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz#c81b2d47753264c77ac03b9966a46637482bb03b"
integrity sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==
dependencies:
de-indent "^1.0.2"
he "^1.2.0"
vue-tsc@^1.8.22:
version "1.8.27"
resolved "https://registry.yarnpkg.com/vue-tsc/-/vue-tsc-1.8.27.tgz#feb2bb1eef9be28017bb9e95e2bbd1ebdd48481c"
integrity sha512-WesKCAZCRAbmmhuGl3+VrdWItEvfoFIPXOvUJkjULi+x+6G/Dy69yO3TBRJDr9eUlmsNAwVmxsNZxvHKzbkKdg==
dependencies:
"@volar/typescript" "~1.11.1"
"@vue/language-core" "1.8.27"
semver "^7.5.4"
vue@3: vue@3:
version "3.4.25" version "3.4.25"
resolved "https://registry.yarnpkg.com/vue/-/vue-3.4.25.tgz#e59d4ed36389647b52ff2fd7aa84bb6691f4205b" resolved "https://registry.yarnpkg.com/vue/-/vue-3.4.25.tgz#e59d4ed36389647b52ff2fd7aa84bb6691f4205b"