37 Commits

Author SHA1 Message Date
25ed6df62a Update quasar
All checks were successful
Build BAB Application Deployment Artifact / build (push) Successful in 2m11s
2024-03-07 21:49:38 -05:00
2f86700fb7 Change Number
All checks were successful
Build BAB Application Deployment Artifact / build (push) Successful in 2m33s
2024-02-13 14:56:41 -05:00
e7a79736b7 Change icons to blue
All checks were successful
Build BAB Application Deployment Artifact / build (push) Successful in 3m3s
2024-01-16 14:19:38 -05:00
2d585d499e Final build working
All checks were successful
Build BAB Application Deployment Artifact / build (push) Successful in 2m50s
2024-01-01 18:56:12 -05:00
284d5ffcb4 Move env file creation to the right place
All checks were successful
Build BAB Application Deployment Artifact / build (push) Successful in 3m2s
2024-01-01 18:48:58 -05:00
27a476ae00 Test
Some checks failed
Build BAB Application Deployment Artifact / build (push) Has been cancelled
2024-01-01 18:47:11 -05:00
ee7f79550c Fix name of .env file
All checks were successful
Build BAB Application Deployment Artifact / build (push) Successful in 3m1s
2024-01-01 18:42:57 -05:00
2ef801905b Update envfile
All checks were successful
Build BAB Application Deployment Artifact / build (push) Successful in 3m3s
2024-01-01 18:30:54 -05:00
752421c9fc Add the env file, so app builds with correct API info
All checks were successful
Build BAB Application Deployment Artifact / build (push) Successful in 3m3s
2024-01-01 17:39:11 -05:00
ce169f6a61 reorder install
All checks were successful
Build BAB Application Deployment Artifact / build (push) Successful in 2m56s
2024-01-01 13:42:03 -05:00
622b9fc82d Install dependencies with yarn
All checks were successful
Build BAB Application Deployment Artifact / build (push) Successful in 2m58s
2024-01-01 13:19:10 -05:00
275f23c421 Remove npm package-lock.json
Some checks failed
Build BAB Application Deployment Artifact / build (push) Failing after 59s
2024-01-01 13:12:48 -05:00
88ed4caf5b Update all yarn packages
Some checks failed
Build BAB Application Deployment Artifact / build (push) Failing after 2m2s
2024-01-01 13:10:30 -05:00
346e395e15 Build tar, as all the dates are messed up with zip
All checks were successful
Build BAB Application Deployment Artifact / build (push) Successful in 2m23s
2023-12-31 15:32:09 -05:00
f30848803b Update Boat selection component
All checks were successful
Build BAB Application Deployment Artifact / build (push) Successful in 2m33s
2023-12-31 15:04:53 -05:00
96dab93483 Fix URL Path
All checks were successful
Build BAB Application Deployment Artifact / build (push) Successful in 2m29s
2023-12-29 23:58:04 -05:00
a6abee1ddf Enable verbosity for debugging
All checks were successful
Build BAB Application Deployment Artifact / build (push) Successful in 2m29s
2023-12-29 12:45:19 -05:00
b20f2bffd6 Remove Secret
All checks were successful
Build BAB Application Deployment Artifact / build (push) Successful in 2m29s
2023-12-29 12:33:36 -05:00
f6689cbc5c Change url from secret to var
All checks were successful
Build BAB Application Deployment Artifact / build (push) Successful in 2m37s
2023-12-29 12:19:39 -05:00
8383605115 Disable SSL verification
Some checks failed
Build BAB Application Deployment Artifact / build (push) Failing after 2m31s
2023-12-29 12:14:20 -05:00
f69614d5c7 Fix URL again
Some checks failed
Build BAB Application Deployment Artifact / build (push) Failing after 2m40s
2023-12-29 12:06:24 -05:00
f7902011cc Fix action url
Some checks failed
Build BAB Application Deployment Artifact / build (push) Failing after 5s
2023-12-29 12:04:21 -05:00
e86876ba69 Update Actions
Some checks failed
Build BAB Application Deployment Artifact / build (push) Failing after 5s
2023-12-29 11:29:31 -05:00
cd6f2e3ba2 Updates to selection component
Some checks failed
Build BAB Application Deployment Artifact / build (push) Failing after 2m27s
2023-12-29 09:22:28 -05:00
66e2169f45 Adapting to time blocks for bookings
Some checks failed
Build BAB Application Deployment Artifact / build (push) Failing after 2m11s
2023-12-23 11:39:54 -05:00
489cc2646b Try v4 of the upload-artifact action
Some checks failed
Build BAB Application Deployment Artifact / build (push) Failing after 2m15s
2023-12-20 14:23:42 -05:00
295f1f7449 Don't bother tar/gz, as it's adding an extra, unnecessary step
All checks were successful
Build BAB Application Deployment Artifact / build (push) Successful in 2m20s
2023-12-20 13:59:50 -05:00
33a1bc24f6 Test run number addition to build
All checks were successful
Build BAB Application Deployment Artifact / build (push) Successful in 2m21s
2023-12-20 12:27:27 -05:00
d18780bb21 Begin implementation of timeblocks. Update workflow to build on devel branch
All checks were successful
Build BAB Application Deployment Artifact / build (push) Successful in 2m21s
2023-12-20 10:48:51 -05:00
ef569ac3b1 Merge minor edits from development.
All checks were successful
Build BAB Application Deployment Artifact / build (push) Successful in 2m30s
2023-12-18 20:50:51 -05:00
9390b7035c Change interval to 1h. Create StatusTypes 2023-12-18 20:44:01 -05:00
ac1730401a Add a shortened displayName for boats for a better mobile experience 2023-12-18 20:23:17 -05:00
bc41b1a7a1 Add bordered logo
All checks were successful
Build BAB Application Deployment Artifact / build (push) Successful in 3m42s
2023-12-18 18:10:05 -05:00
ea566d4a42 Add docs folder and design of users
All checks were successful
Build BAB Application Deployment Artifact / build (push) Successful in 2m31s
2023-12-18 14:26:03 -05:00
573e327a0f Move hard-coded API settings to .env file 2023-12-03 08:51:01 -05:00
831e81e892 Update package.json 2023-12-03 08:19:57 -05:00
39a6ab5fcc New workflow steps to use version
All checks were successful
Build BAB Application Deployment Artifact / build (push) Successful in 3m9s
2023-12-02 23:19:02 -05:00
14 changed files with 1292 additions and 919 deletions

View File

@@ -1,9 +1,10 @@
name: Build BAB Application Deployment Artifact name: Build BAB Application Deployment Artifact
run-name: ${{ gitea.actor }} is building an artifact 🚀 run-name: ${{ gitea.actor }} is building a BAB App artifact 🚀
on: on:
push: push:
branches: branches:
- main - main
- devel
jobs: jobs:
build: build:
@@ -15,16 +16,35 @@ jobs:
uses: actions/setup-node@v2 uses: actions/setup-node@v2
with: with:
node-version: '20.x' node-version: '20.x'
- name: Install dependencies
run: npm install
- name: Install yarn - name: Install yarn
run: npm install --global yarn run: npm install --global yarn
- name: Install yarn dependencies
run: yarn install
- name: Install Quasar CLI - name: Install Quasar CLI
run: npm install -g @quasar/cli run: yarn global add @quasar/cli
- name: Create env file
run: |
echo "${{ vars.ENV_FILE }}" > .env.local
- name: Show env file
run: |
/bin/cat .env.local
- name: Build Project - name: Build Project
run: quasar build -m pwa run: quasar build -m pwa
# - name: Archive Production Artifact - name: Get Version Number
# uses: actions/upload-artifact@v2 id: get_version
# with: run: echo "::set-output name=VERSION::$(node -p "require('./package.json').version")"
# name: build-artifact - name: Tarfile
# path: dist/pwa run: |
cd dist/pwa
tar czf ../../build-${{ steps.get_version.outputs.VERSION }}.tar.gz .
- name: Upload Artifact
uses: actions/upload-artifact@v3
with:
name: build-artifact-${{ steps.get_version.outputs.VERSION }}.${{ gitea.run_number }}
path: build-${{ steps.get_version.outputs.VERSION }}.tar.gz
- name: Trigger Ansible Deploy Playbook
uses: https://github.com/distributhor/workflow-webhook@v3
with:
webhook_url: ${{ vars.WEBHOOK_URL }}
verbose: true
data: '{ "artifact_url": "${{ gitea.server_url }}/${{ gitea.repository }}/actions/runs/${{ gitea.run_id}}/artifacts/build-artifact-${{ steps.get_version.outputs.VERSION }}.${{ gitea.run_number }}" }'

View File

@@ -0,0 +1,40 @@
# Users, Roles and Permissions
This is the design document for https://gitea.toal.ca/oys/bab-app/issues/11
## Backend Concepts
Utilizing the AppWrite backend provides us with some basic concepts we can use:
### Users, Groups, and Labels
#### Teams
Teams are AppWrite groups of users. Teams can be assigned roles, which can be assigned permissions. Teams "contain" users. A team has more permissions to manage it's members than labels, which are assigned / removed, rather than 'invited / left'.
#### Labels
Labels are AppWrite tags for users. Users have Labels as attributes. Like teams, labels can be used for Role / Permission mapping.
### Permissions
https://appwrite.io/docs/advanced/platform/permissions
Permissions are fine-grained access control for users and objects. They follow standard "CRUD" patterns.
## BAB Concepts
For teams, there will, to start, be the following:
- `staff` : Individuals with authority / responsibilities
- `maintenance` : Staff responsible for maintenance (eg: Boatswain)
- `admin`: Administrators of the program / application
- `school` : Members of the Sailing School (Instructors & Students)
- `student` role : A student in the school
- `instructor` role: An instructor in the school
- `bab` : Members of the BAB program
- `skipper` role: A member who has passed skipper certification
The following are the initial labels:
- TBD

View File

@@ -16,21 +16,22 @@
"@quasar/extras": "^1.16.4", "@quasar/extras": "^1.16.4",
"appwrite": "^13.0.0", "appwrite": "^13.0.0",
"pinia": "^2.1.7", "pinia": "^2.1.7",
"quasar": "^2.6.0", "vue": "3",
"vue": "^3.0.0", "vue-router": "4"
"vue-router": "^4.0.0"
}, },
"devDependencies": { "devDependencies": {
"@quasar/app-vite": "^1.3.0", "@quasar/app-vite": "^1.7.4",
"@quasar/quasar-app-extension-qcalendar": "^4.0.0-beta.15", "@quasar/quasar-app-extension-qcalendar": "^4.0.0-beta.15",
"@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",
"autoprefixer": "^10.4.2", "autoprefixer": "^10.4.2",
"dotenv": "^16.3.1",
"eslint": "^8.10.0", "eslint": "^8.10.0",
"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.14.6",
"typescript": "^4.5.4", "typescript": "^4.5.4",
"workbox-build": "^7.0.0", "workbox-build": "^7.0.0",
"workbox-cacheable-response": "^7.0.0", "workbox-cacheable-response": "^7.0.0",
@@ -38,10 +39,11 @@
"workbox-expiration": "^7.0.0", "workbox-expiration": "^7.0.0",
"workbox-precaching": "^7.0.0", "workbox-precaching": "^7.0.0",
"workbox-routing": "^7.0.0", "workbox-routing": "^7.0.0",
"workbox-strategies": "^7.0.0" "workbox-strategies": "^7.0.0",
"yarn": "^1.22.21"
}, },
"engines": { "engines": {
"node": "^18 || ^16 || ^14.19", "node": "^20 || ^18 || ^16 || ^14.19",
"npm": ">= 6.13.4", "npm": ">= 6.13.4",
"yarn": ">= 1.21.1" "yarn": ">= 1.21.1"
} }

View File

@@ -48,6 +48,7 @@ module.exports = configure(function (/* ctx */) {
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#build // Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#build
build: { build: {
env: require('dotenv').config({ path: '.env.local' }).parsed,
target: { target: {
browser: ['es2019', 'edge88', 'firefox78', 'chrome87', 'safari13.1'], browser: ['es2019', 'edge88', 'firefox78', 'chrome87', 'safari13.1'],
node: 'node16', node: 'node16',

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

View File

@@ -14,8 +14,8 @@ const client = new Client();
// Private self-hosted appwrite // Private self-hosted appwrite
client client
.setEndpoint('https://apidev.bab.toal.ca/v1') .setEndpoint(process.env.APPWRITE_API_ENDPOINT)
.setProject('655a7116479b4d5a815f'); .setProject(process.env.APPWRITE_API_PROJECT);
//TODO //TODO
const appDatabaseId = ''; const appDatabaseId = '';

View File

@@ -1,3 +1,4 @@
<!-- This has been abandoned for now. Going to block-based booking. Will probably need the schedule viewer functionality at some point in the future, though -->
<template> <template>
<q-card-section> <q-card-section>
<div class="text-caption text-justify"> <div class="text-caption text-justify">
@@ -55,11 +56,13 @@
v-model="selectedDate" v-model="selectedDate"
:model-resources="boatStore.boats" :model-resources="boatStore.boats"
resource-key="id" resource-key="id"
resource-label="name" resource-label="displayName"
:interval-start="12" resource-width="32"
:interval-count="36" :interval-start="6"
:interval-minutes="30" :interval-count="18"
:interval-minutes="60"
cell-width="48" cell-width="48"
style="--calendar-resources-width: 48px"
resource-min-height="40" resource-min-height="40"
animated animated
bordered bordered
@@ -79,8 +82,8 @@
</template> </template>
<template #resource-label="{ scope: { resource } }"> <template #resource-label="{ scope: { resource } }">
<div class="col-12"> <div class="col-12 .col-md-auto">
{{ resource.name }} {{ resource.displayName }}
<q-icon v-if="resource.defects" name="warning" color="warning" /> <q-icon v-if="resource.defects" name="warning" color="warning" />
</div> </div>
</template> </template>
@@ -98,7 +101,6 @@
><template v-slot:append><q-icon name="timelapse" /></template></q-select ><template v-slot:append><q-icon name="timelapse" /></template></q-select
></q-card-section> ></q-card-section>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue'; import { ref } from 'vue';
import { import {
@@ -114,6 +116,17 @@ 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';
type EventData = {
event: object;
scope: {
timestamp: object;
columnindex: number;
activeDate: boolean;
droppable: boolean;
};
};
const durations = [1, 1.5, 2, 2.5, 3, 3.5, 4]; const durations = [1, 1.5, 2, 2.5, 3, 3.5, 4];
@@ -179,7 +192,7 @@ function getStyle(event: {
left: number; left: number;
width: number; width: number;
title: string; title: string;
status: 'tentative' | 'confirmed' | 'pending'; status: StatusTypes;
}) { }) {
return { return {
position: 'absolute', position: 'absolute',
@@ -200,14 +213,16 @@ function onPrev() {
function onNext() { function onNext() {
calendar.value.next(); calendar.value.next();
} }
function onClickDate(data) {
return; function onClickDate(data: EventData) {
return data;
} }
function onClickTime(data) {
function onClickTime(data: EventData) {
// TODO: Add a duration picker, here. // TODO: Add a duration picker, here.
emit('onClickTime', data); emit('onClickTime', data);
} }
function onUpdateDuration(value) { function onUpdateDuration(value: EventData) {
emit('onUpdateDuration', value); emit('onUpdateDuration', value);
} }
// eslint-disable-next-line @typescript-eslint/no-empty-function // eslint-disable-next-line @typescript-eslint/no-empty-function

View File

@@ -0,0 +1,193 @@
<template>
<q-card-section style="max-width: 320px">
<div class="text-caption">
Use the calendar to pick a date. Select an available boat and timeslot
below.
</div>
<div
style="
width: 100%;
max-width: 320px;
display: flex;
justify-content: center;
"
>
<div
style="
width: 50%;
max-width: 350px;
display: flex;
justify-content: space-between;
"
>
<span
class="q-button"
style="cursor: pointer; user-select: none"
@click="onPrev"
>&lt;</span
>
{{ formattedMonth }}
<span
class="q-button"
style="cursor: pointer; user-select: none"
@click="onNext"
>&gt;</span
>
</div>
</div>
<div
style="
display: flex;
justify-content: center;
align-items: center;
flex-wrap: nowrap;
"
>
<div style="display: flex; width: 100%">
<q-calendar-month
ref="calendar"
v-model="selectedDate"
:disabled-before="disabledBefore"
:disabled-days="disabledDays()"
animated
bordered
mini-mode
date-type="rounded"
@click-date="onClickDate"
@change="onChange"
/>
</div></div
></q-card-section>
<q-card-section style="max-width: 320px">
<div v-for="boat in boatStore.boats" :key="boat.name">
<q-item-label header>{{ boat.name }}</q-item-label>
<q-item>
<q-item-section>
<q-option-group
:options="boatoptions(boat)"
type="radio"
v-model="selectedBoatTime"
>
<template v-slot:label="opt">
<div class="row items-center">
{{ opt.label }} &nbsp;
<span class="text-caption" v-if="opt.disable"
>Reserved by {{ opt.user }}</span
>
</div></template
>
</q-option-group>
</q-item-section>
</q-item>
</div>
</q-card-section>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import {
today,
parseTimestamp,
addToDate,
Timestamp,
} from '@quasar/quasar-ui-qcalendar';
import { Boat, useBoatStore } from 'src/stores/boat';
import { useScheduleStore, Timeblock } from 'src/stores/schedule';
import { computed } from 'vue';
import { date } from 'quasar';
type EventData = {
event: object;
scope: {
timestamp: object;
columnindex: number;
activeDate: boolean;
droppable: boolean;
};
};
const calendar = ref();
const boatStore = useBoatStore();
const scheduleStore = useScheduleStore();
const selectedDate = ref(today());
const selectedBoatTime = ref();
const formattedMonth = computed(() => {
const date = new Date(selectedDate.value);
return monthFormatter()?.format(date);
});
const disabledBefore = computed(() => {
const todayTs = parseTimestamp(today()) as Timestamp;
return addToDate(todayTs, { day: -1 }).date;
});
function monthFormatter() {
try {
return new Intl.DateTimeFormat('en-CA' || undefined, {
month: 'long',
timeZone: 'UTC',
});
} catch (e) {
//
}
}
const disabledDays = () => {
// Placeholder. This should actually compute days when boats aren't available.
const days = [];
const todayTs = parseTimestamp(today()) as Timestamp;
days.push(addToDate(todayTs, { day: 2 }).date);
return days;
};
const boatoptions = (boat: Boat) => {
const options = useScheduleStore()
.getTimeblocksForDate(date.extractDate(selectedDate.value, 'YYYY-MM-DD'))
.map((x: Timeblock) => {
const conflicts = getConflicts(x, boat);
return {
label: x.start.time + ' to ' + x.end.time,
value: boat.id + ':' + x.start.time,
disable: conflicts.length > 0,
user: conflicts[0]?.user,
boat: boat,
timeblock: x,
};
});
return options;
};
const emit = defineEmits(['onClickTime', 'onUpdateDuration']);
function onPrev() {
calendar.value.prev();
}
function onNext() {
calendar.value.next();
}
function onClickDate(data: EventData) {
return data;
}
function onChange(data: EventData) {
return data;
}
const getConflicts = (timeblock: Timeblock, boat: Boat) => {
const start = date.buildDate({
hour: timeblock.start.hour,
minute: timeblock.start.minute,
second: 0,
millisecond: 0,
});
const end = date.buildDate({
hour: timeblock.end.hour,
minute: timeblock.end.minute,
second: 0,
millisecond: 0,
});
return scheduleStore.getConflictingReservations(boat, start, end);
};
</script>

View File

@@ -17,7 +17,7 @@
<q-avatar icon="numbers" /> <q-avatar icon="numbers" />
</q-item-section> </q-item-section>
<q-item-section> <q-item-section>
12345 123456
<q-item-label caption>Member ID</q-item-label> <q-item-label caption>Member ID</q-item-label>
</q-item-section> </q-item-section>
</q-item> </q-item>
@@ -25,13 +25,13 @@
<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="primary" text-color="white" <q-chip square icon="verified" color="blue" text-color="white"
>J/27</q-chip >J/27</q-chip
> >
<q-chip square icon="verified" color="green" text-color="white" <q-chip square icon="verified" color="blue" text-color="white"
>Capri25</q-chip >Capri25</q-chip
> >
<q-chip square icon="verified" color="grey-8" text-color="white" <q-chip square icon="verified" color="blue" text-color="white"
>Night</q-chip >Night</q-chip
> >
</q-item-section> </q-item-section>

View File

@@ -21,14 +21,8 @@
:caption="bookingSummary" :caption="bookingSummary"
> >
<q-separator /> <q-separator />
<resource-schedule-viewer-component <boat-selection />
@on-click-time="onClickTime"
@on-update-duration="
(value) => {
bookingForm.duration = value;
}
"
/>
<q-banner <q-banner
rounded rounded
class="bg-warning text-grey-10" class="bg-warning text-grey-10"
@@ -78,7 +72,7 @@ import { reactive, ref, computed, watch } from 'vue';
import { useAuthStore } from 'src/stores/auth'; import { useAuthStore } from 'src/stores/auth';
import { Boat, useBoatStore } from 'src/stores/boat'; import { Boat, useBoatStore } from 'src/stores/boat';
import { Dialog, date } from 'quasar'; import { Dialog, date } from 'quasar';
import ResourceScheduleViewerComponent from 'src/components/ResourceScheduleViewerComponent.vue'; import BoatSelection from 'src/components/scheduling/BoatSelection.vue';
import { makeDateTime } from '@quasar/quasar-ui-qcalendar'; import { makeDateTime } from '@quasar/quasar-ui-qcalendar';
import { useScheduleStore, Reservation } from 'src/stores/schedule'; import { useScheduleStore, Reservation } from 'src/stores/schedule';
@@ -113,7 +107,7 @@ watch(bookingForm, (b, a) => {
status: 'tentative', status: 'tentative',
}; };
//TODO: Turn this into a validator. //TODO: Turn this into a validator.
scheduleStore.isOverlapped(newRes) scheduleStore.isReservationOverlapped(newRes)
? Dialog.create({ message: 'This booking overlaps another!' }) ? Dialog.create({ message: 'This booking overlaps another!' })
: scheduleStore.addOrCreateReservation(newRes); : scheduleStore.addOrCreateReservation(newRes);
}); });

View File

@@ -5,6 +5,7 @@ import { defineStore } from 'pinia';
export interface Boat { export interface Boat {
id: number; id: number;
name: string; name: string;
displayName?: string;
class?: string; class?: string;
year?: number; year?: number;
imgsrc?: string; imgsrc?: string;
@@ -27,6 +28,7 @@ const getSampleData = () => [
{ {
id: 1, id: 1,
name: 'ProjectX', name: 'ProjectX',
displayName: 'PX',
class: 'J/27', class: 'J/27',
year: 1981, year: 1981,
imgsrc: '/tmpimg/j27.png', imgsrc: '/tmpimg/j27.png',
@@ -52,6 +54,7 @@ and rough engine performance.`,
{ {
id: 2, id: 2,
name: 'Take5', name: 'Take5',
displayName: 'T5',
class: 'J/27', class: 'J/27',
year: 1985, year: 1985,
imgsrc: '/tmpimg/j27.png', imgsrc: '/tmpimg/j27.png',
@@ -60,6 +63,7 @@ and rough engine performance.`,
{ {
id: 3, id: 3,
name: 'WeeBeestie', name: 'WeeBeestie',
displayName: 'WB',
class: 'Capri 25', class: 'Capri 25',
year: 1989, year: 1989,
imgsrc: '/tmpimg/capri25.png', imgsrc: '/tmpimg/capri25.png',

View File

@@ -3,24 +3,55 @@ import { ref } from 'vue';
import { Boat, useBoatStore } from './boat'; import { Boat, useBoatStore } from './boat';
import { date } from 'quasar'; import { date } from 'quasar';
import { DateOptions } from 'quasar'; import { DateOptions } from 'quasar';
import {
Timestamp,
parseTimestamp,
TimestampArray,
} from '@quasar/quasar-ui-qcalendar';
import { timeStamp } from 'console';
export interface Reservation { export type StatusTypes = 'tentative' | 'confirmed' | 'pending' | undefined;
export type Reservation = {
id: number; id: number;
user: string; user: string;
start: Date; start: Date;
end: Date; end: Date;
resource: Boat; resource: Boat;
reservationDate: Date; reservationDate: Date;
status?: string; status?: StatusTypes;
} };
function getSampleData(): Reservation[] { export type Timeblock = {
start: Timestamp;
end: Timestamp;
};
const sampleBlocks = [
{
start: { time: '09:00', hour: 9, minute: 0, hasDay: false, hasTime: true },
end: { time: '12:00', hour: 12, minute: 0, hasDay: false, hasTime: true },
},
{
start: { time: '12:00', hour: 12, minute: 0, hasDay: false, hasTime: true },
end: { time: '15:00', hour: 15, minute: 0, hasDay: false, hasTime: true },
},
{
start: { time: '15:00', hour: 15, minute: 0, hasDay: false, hasTime: true },
end: { time: '18:00', hour: 18, minute: 0, hasDay: false, hasTime: true },
},
{
start: { time: '18:00', hour: 18, minute: 0, hasDay: false, hasTime: true },
end: { time: '21:00', hour: 21, minute: 0, hasDay: false, hasTime: true },
},
] as Timeblock[];
function getSampleReservations(): Reservation[] {
const sampleData = [ const sampleData = [
{ {
id: 1, id: 1,
user: 'John Smith', user: 'John Smith',
start: '12:00', start: '12:00',
end: '14:00', end: '15:00',
boat: 1, boat: 1,
status: 'confirmed', status: 'confirmed',
}, },
@@ -28,31 +59,31 @@ function getSampleData(): Reservation[] {
id: 2, id: 2,
user: 'Bob Barker', user: 'Bob Barker',
start: '18:00', start: '18:00',
end: '20:00', end: '21:00',
boat: 1, boat: 1,
status: 'confirmed', status: 'confirmed',
}, },
{ {
id: 3, id: 3,
user: 'Peter Parker', user: 'Peter Parker',
start: '8:00', start: '9:00',
end: '10:00', end: '12:00',
boat: 2, boat: 2,
status: 'tentative', status: 'tentative',
}, },
{ {
id: 4, id: 4,
user: 'Vince McMahon', user: 'Vince McMahon',
start: '13:00', start: '15:00',
end: '17:00', end: '18:00',
boat: 2, boat: 2,
status: 'pending', status: 'pending',
}, },
{ {
id: 5, id: 5,
user: 'Heather Graham', user: 'Heather Graham',
start: '06:00', start: '09:00',
end: '09:00', end: '12:00',
boat: 3, boat: 3,
status: 'confirmed', status: 'confirmed',
}, },
@@ -60,7 +91,7 @@ function getSampleData(): Reservation[] {
id: 6, id: 6,
user: 'Lawrence Fishburne', user: 'Lawrence Fishburne',
start: '18:00', start: '18:00',
end: '20:00', end: '21:00',
boat: 3, boat: 3,
}, },
]; ];
@@ -70,7 +101,12 @@ function getSampleData(): Reservation[] {
return x.split(':'); return x.split(':');
}; };
const makeOpts = (x: string[]): DateOptions => { const makeOpts = (x: string[]): DateOptions => {
return { hour: parseInt(x[0]), minute: parseInt(x[1]) }; return {
hour: parseInt(x[0]),
minute: parseInt(x[1]),
seconds: 0,
milliseconds: 0,
};
}; };
return sampleData.map((entry): Reservation => { return sampleData.map((entry): Reservation => {
@@ -82,13 +118,18 @@ function getSampleData(): Reservation[] {
end: date.adjustDate(now, makeOpts(splitTime(entry.end))), end: date.adjustDate(now, makeOpts(splitTime(entry.end))),
resource: boat, resource: boat,
reservationDate: now, reservationDate: now,
status: entry.status, status: entry.status as StatusTypes,
}; };
}); });
} }
export const useScheduleStore = defineStore('schedule', () => { export const useScheduleStore = defineStore('schedule', () => {
const reservations = ref<Reservation[]>(getSampleData()); // TODO: Implement functions to dynamically pull this data.
const reservations = ref<Reservation[]>(getSampleReservations());
const timeblocks = sampleBlocks;
const getTimeblocksForDate = (date: Date): Timeblock[] => timeblocks;
const getBoatReservations = ( const getBoatReservations = (
boat: number | string, boat: number | string,
curDate: Date curDate: Date
@@ -105,15 +146,30 @@ export const useScheduleStore = defineStore('schedule', () => {
}); });
}; };
const isOverlapped = (res: Reservation) => { const getConflictingReservations = (
const lapped = reservations.value.filter( resource: Boat,
start: Date,
end: Date
): Reservation[] => {
const overlapped = reservations.value.filter(
(entry: Reservation) => (entry: Reservation) =>
entry.id != res.id && entry.resource.id == resource.id &&
entry.resource == res.resource && entry.start < end &&
((entry.start <= res.start && entry.end > res.start) || entry.end > start
(entry.end >= res.end && entry.start <= res.end))
); );
return lapped.length > 0; return overlapped;
};
const isResourceTimeOverlapped = (
resource: Boat,
start: Date,
end: Date
): boolean => {
return getConflictingReservations(resource, start, end).length > 0;
};
const isReservationOverlapped = (res: Reservation): boolean => {
return isResourceTimeOverlapped(res.resource, res.start, res.end);
}; };
const getNewId = () => { const getNewId = () => {
@@ -133,8 +189,11 @@ export const useScheduleStore = defineStore('schedule', () => {
return { return {
reservations, reservations,
getBoatReservations, getBoatReservations,
getConflictingReservations,
getTimeblocksForDate,
getNewId, getNewId,
addOrCreateReservation, addOrCreateReservation,
isOverlapped, isReservationOverlapped,
isResourceTimeOverlapped,
}; };
}); });

1749
yarn.lock

File diff suppressed because it is too large Load Diff