Compare commits
2 Commits
4faff7cc8c
...
2f68877ce6
| Author | SHA1 | Date | |
|---|---|---|---|
|
2f68877ce6
|
|||
|
0de9991a49
|
@@ -1,15 +0,0 @@
|
|||||||
<template>
|
|
||||||
<q-banner :class="$q.dark.isActive ? 'bg-grey-9' : 'bg-grey-3'">
|
|
||||||
Use the calendar to pick a date. Select an available boat and timeslot
|
|
||||||
below.
|
|
||||||
</q-banner>
|
|
||||||
<BoatScheduleTableComponent v-model="reservation" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref } from 'vue';
|
|
||||||
import BoatScheduleTableComponent from './boat/BoatScheduleTableComponent.vue';
|
|
||||||
import { Reservation } from 'src/stores/schedule.types';
|
|
||||||
|
|
||||||
const reservation = ref<Reservation | null>(null);
|
|
||||||
</script>
|
|
||||||
|
|||||||
@@ -38,7 +38,25 @@
|
|||||||
:id="block.id"
|
:id="block.id"
|
||||||
@click="selectBlock($event, scope, block)"
|
@click="selectBlock($event, scope, block)"
|
||||||
>
|
>
|
||||||
Available
|
{{ boatData[scope.columnIndex].name }}<br />
|
||||||
|
{{ selectedBlock?.id === block.id ? 'Selected' : 'Available' }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-for="r in boatData[scope.columnIndex].reservations"
|
||||||
|
:key="r.id"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="reservation"
|
||||||
|
:style="
|
||||||
|
reservationStyles(
|
||||||
|
r,
|
||||||
|
scope.timeStartPos,
|
||||||
|
scope.timeDurationHeight
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ r.user }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -55,6 +73,7 @@ import {
|
|||||||
today,
|
today,
|
||||||
parsed,
|
parsed,
|
||||||
parseTimestamp,
|
parseTimestamp,
|
||||||
|
parseDate,
|
||||||
addToDate,
|
addToDate,
|
||||||
} from '@quasar/quasar-ui-qcalendar';
|
} from '@quasar/quasar-ui-qcalendar';
|
||||||
import CalendarHeaderComponent from './CalendarHeaderComponent.vue';
|
import CalendarHeaderComponent from './CalendarHeaderComponent.vue';
|
||||||
@@ -66,44 +85,67 @@ import { Reservation, Timeblock } from 'src/stores/schedule.types';
|
|||||||
|
|
||||||
interface BoatData extends Boat {
|
interface BoatData extends Boat {
|
||||||
blocks?: Timeblock[];
|
blocks?: Timeblock[];
|
||||||
|
reservations?: Reservation[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const scheduleStore = useScheduleStore();
|
const scheduleStore = useScheduleStore();
|
||||||
const boatStore = useBoatStore();
|
const boatStore = useBoatStore();
|
||||||
const selectedBlock = ref<Timeblock | null>(null);
|
const selectedBlock = ref<Timeblock | null>(null);
|
||||||
const selectedDate = ref(today());
|
const selectedDate = ref(today());
|
||||||
const reservation = ref<Reservation | null>(null);
|
|
||||||
|
|
||||||
const boatData = ref<BoatData[]>(boatStore.boats);
|
const boatData = ref<BoatData[]>(boatStore.boats);
|
||||||
|
|
||||||
const calendar = ref<QCalendarDay | null>(null);
|
const calendar = ref<QCalendarDay | null>(null);
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
updateBoatTime: [block: Timeblock];
|
||||||
|
}>();
|
||||||
|
|
||||||
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(
|
||||||
|
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(
|
function blockStyles(
|
||||||
block: Timeblock,
|
block: Timeblock,
|
||||||
timeStartPos: (t: string) => string,
|
timeStartPos: (t: string) => string,
|
||||||
timeDurationHeight: (d: number) => string
|
timeDurationHeight: (d: number) => string
|
||||||
|
) {
|
||||||
|
return genericBlockStyle(
|
||||||
|
parsed(block.start) as Timestamp,
|
||||||
|
parsed(block.end) as Timestamp,
|
||||||
|
timeStartPos,
|
||||||
|
timeDurationHeight
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function genericBlockStyle(
|
||||||
|
start: Timestamp,
|
||||||
|
end: Timestamp,
|
||||||
|
timeStartPos: (t: string) => string,
|
||||||
|
timeDurationHeight: (d: number) => string
|
||||||
) {
|
) {
|
||||||
const s = {
|
const s = {
|
||||||
top: '',
|
top: '',
|
||||||
height: '',
|
height: '',
|
||||||
opacity: '',
|
opacity: '',
|
||||||
};
|
};
|
||||||
if (block && timeStartPos && timeDurationHeight) {
|
if (timeStartPos && timeDurationHeight) {
|
||||||
s.top = timeStartPos(parsed(block.start)?.time || '00:00') + 'px';
|
s.top = timeStartPos(start.time) + 'px';
|
||||||
s.height =
|
s.height =
|
||||||
parseInt(
|
parseInt(
|
||||||
timeDurationHeight(
|
timeDurationHeight(diffTimestamp(start, end, false) / 1000 / 60)
|
||||||
diffTimestamp(
|
|
||||||
parsed(block.start) as Timestamp,
|
|
||||||
parsed(block.end) as Timestamp,
|
|
||||||
false
|
|
||||||
) /
|
|
||||||
1000 /
|
|
||||||
60
|
|
||||||
)
|
|
||||||
) -
|
) -
|
||||||
1 +
|
1 +
|
||||||
'px';
|
'px';
|
||||||
@@ -124,14 +166,22 @@ interface DayBodyScope {
|
|||||||
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;
|
||||||
|
emit('updateBoatTime', block);
|
||||||
}
|
}
|
||||||
|
|
||||||
function changeEvent({ start }: { start: string }) {
|
function changeEvent({ start }: { start: string }) {
|
||||||
const newBlocks = scheduleStore.getTimeblocksForDate(start);
|
const newBlocks = scheduleStore.getTimeblocksForDate(start);
|
||||||
|
const reservations = scheduleStore.getBoatReservations(
|
||||||
|
parsed(start) as Timestamp
|
||||||
|
);
|
||||||
boatData.value.map((b) => {
|
boatData.value.map((b) => {
|
||||||
return (b.blocks = newBlocks.filter((block) => block.boatId === b.$id));
|
b.blocks = newBlocks.filter((block) => block.boatId === b.$id);
|
||||||
|
b.reservations = reservations.filter(
|
||||||
|
(reservation) => reservation.resource === b
|
||||||
|
); // TODO: search by id, not item.
|
||||||
});
|
});
|
||||||
setTimeout(() => calendar.value?.scrollToTime('09:00'), 10); // Should figure out why we need this setTimeout...
|
|
||||||
|
setTimeout(() => calendar.value?.scrollToTime('09:00'), 100); // Should figure out why we need this setTimeout...
|
||||||
}
|
}
|
||||||
|
|
||||||
const disabledBefore = computed(() => {
|
const disabledBefore = computed(() => {
|
||||||
@@ -144,12 +194,26 @@ const disabledBefore = computed(() => {
|
|||||||
.boat-schedule-table-component
|
.boat-schedule-table-component
|
||||||
display: flex
|
display: flex
|
||||||
max-height: 60vh
|
max-height: 60vh
|
||||||
|
.reservation
|
||||||
|
display: flex
|
||||||
|
position: absolute
|
||||||
|
justify-content: center
|
||||||
|
align-items: center
|
||||||
|
width: 100%
|
||||||
|
opacity: 1
|
||||||
|
margin: 0px
|
||||||
|
text-overflow: ellipsis
|
||||||
|
font-size: 0.8em
|
||||||
|
cursor: pointer
|
||||||
|
background: $accent
|
||||||
|
color: white
|
||||||
|
border: 1px solid black
|
||||||
.timeblock
|
.timeblock
|
||||||
display: flex
|
display: flex
|
||||||
position: absolute
|
position: absolute
|
||||||
justify-content: center
|
justify-content: center
|
||||||
align-items: center
|
align-items: center
|
||||||
width: 99%
|
width: 100%
|
||||||
opacity: 0.5
|
opacity: 0.5
|
||||||
margin: 0px
|
margin: 0px
|
||||||
text-overflow: ellipsis
|
text-overflow: ellipsis
|
||||||
|
|||||||
@@ -20,7 +20,11 @@
|
|||||||
:caption="bookingSummary"
|
:caption="bookingSummary"
|
||||||
>
|
>
|
||||||
<q-separator />
|
<q-separator />
|
||||||
<boat-selection />
|
<q-banner :class="$q.dark.isActive ? 'bg-grey-9' : 'bg-grey-3'">
|
||||||
|
Use the calendar to pick a date. Select an available boat and
|
||||||
|
timeslot below.
|
||||||
|
</q-banner>
|
||||||
|
<BoatScheduleTableComponent @updateBoatTime="updateBoat" />
|
||||||
|
|
||||||
<q-banner
|
<q-banner
|
||||||
rounded
|
rounded
|
||||||
@@ -69,14 +73,14 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { reactive, ref, computed, watch } from 'vue';
|
import { reactive, ref, computed, watch } from 'vue';
|
||||||
import { useAuthStore } from 'src/stores/auth';
|
import { useAuthStore } from 'src/stores/auth';
|
||||||
import { Boat } from 'src/stores/boat';
|
import { Boat, useBoatStore } from 'src/stores/boat';
|
||||||
import { Dialog, date } from 'quasar';
|
import { Dialog, date } from 'quasar';
|
||||||
import BoatSelection from 'src/components/scheduling/BoatSelection.vue';
|
import { useScheduleStore } from 'src/stores/schedule';
|
||||||
import { makeDateTime } from '@quasar/quasar-ui-qcalendar';
|
import { Reservation, Timeblock } from 'src/stores/schedule.types';
|
||||||
import { useScheduleStore, Reservation } from 'src/stores/schedule';
|
import BoatScheduleTableComponent from 'src/components/scheduling/boat/BoatScheduleTableComponent.vue';
|
||||||
|
|
||||||
const auth = useAuthStore();
|
const auth = useAuthStore();
|
||||||
const dateFormat = 'ddd 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 bookingForm = reactive({
|
const bookingForm = reactive({
|
||||||
@@ -84,15 +88,7 @@ const bookingForm = reactive({
|
|||||||
name: auth.currentUser?.name,
|
name: auth.currentUser?.name,
|
||||||
boat: <Boat | undefined>undefined,
|
boat: <Boat | undefined>undefined,
|
||||||
startDate: date.formatDate(new Date(), dateFormat),
|
startDate: date.formatDate(new Date(), dateFormat),
|
||||||
endDate: computed(() =>
|
endDate: date.formatDate(new Date(), dateFormat),
|
||||||
date.formatDate(
|
|
||||||
date.addToDate(bookingForm.startDate, {
|
|
||||||
hours: bookingForm.duration,
|
|
||||||
}),
|
|
||||||
dateFormat
|
|
||||||
)
|
|
||||||
),
|
|
||||||
duration: 1,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(bookingForm, (b, a) => {
|
watch(bookingForm, (b, a) => {
|
||||||
@@ -119,12 +115,10 @@ const onSubmit = () => {
|
|||||||
// TODO
|
// TODO
|
||||||
};
|
};
|
||||||
|
|
||||||
const onClickTime = (data) => {
|
const updateBoat = (block: Timeblock) => {
|
||||||
bookingForm.boat = data.scope.resource;
|
bookingForm.boat = useBoatStore().boats.find((b) => b.$id === block.boatId);
|
||||||
bookingForm.startDate = date.formatDate(
|
bookingForm.startDate = date.formatDate(block.start, dateFormat);
|
||||||
date.addToDate(makeDateTime(data.scope.timestamp), { hours: 5 }), // A terrible hack to convert back to EST. TODO: FIX!!!!
|
bookingForm.endDate = date.formatDate(block.end, dateFormat);
|
||||||
dateFormat
|
|
||||||
);
|
|
||||||
console.log(bookingForm.startDate);
|
console.log(bookingForm.startDate);
|
||||||
};
|
};
|
||||||
const bookingDuration = computed(() => {
|
const bookingDuration = computed(() => {
|
||||||
@@ -144,13 +138,4 @@ const bookingSummary = computed(() => {
|
|||||||
? `${bookingForm.boat.name} @ ${bookingForm.startDate} for ${bookingDuration.value}`
|
? `${bookingForm.boat.name} @ ${bookingForm.startDate} for ${bookingDuration.value}`
|
||||||
: '';
|
: '';
|
||||||
});
|
});
|
||||||
|
|
||||||
const limitDate = (startDate: string) => {
|
|
||||||
return date.isBetweenDates(
|
|
||||||
startDate,
|
|
||||||
new Date(),
|
|
||||||
date.addToDate(new Date(), { days: 21 }),
|
|
||||||
{ inclusiveFrom: true, inclusiveTo: true, onlyDate: true }
|
|
||||||
);
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { DateOptions, date } from 'quasar';
|
import { DateOptions, date } from 'quasar';
|
||||||
import { Boat, useBoatStore } from '../boat';
|
import { Boat, useBoatStore } from '../boat';
|
||||||
import { ID } from 'src/boot/appwrite';
|
|
||||||
import {
|
import {
|
||||||
parseTimestamp,
|
parseTimestamp,
|
||||||
today,
|
today,
|
||||||
@@ -43,7 +42,7 @@ export function getSampleTimeBlocks(): 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 = Math.random() < 0.5 ? templateA : templateB;
|
const template = templateB;
|
||||||
result.push(
|
result.push(
|
||||||
...boats
|
...boats
|
||||||
.map((b): Timeblock[] => {
|
.map((b): Timeblock[] => {
|
||||||
@@ -67,16 +66,16 @@ export function getSampleReservations(): Reservation[] {
|
|||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
user: 'John Smith',
|
user: 'John Smith',
|
||||||
start: '12:00',
|
start: '7:00',
|
||||||
end: '15:00',
|
end: '10:00',
|
||||||
boat: '1',
|
boat: '1',
|
||||||
status: 'confirmed',
|
status: 'confirmed',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
user: 'Bob Barker',
|
user: 'Bob Barker',
|
||||||
start: '18:00',
|
start: '16:00',
|
||||||
end: '21:00',
|
end: '19:00',
|
||||||
boat: '1',
|
boat: '1',
|
||||||
status: 'confirmed',
|
status: 'confirmed',
|
||||||
},
|
},
|
||||||
@@ -91,24 +90,24 @@ export function getSampleReservations(): Reservation[] {
|
|||||||
{
|
{
|
||||||
id: 4,
|
id: 4,
|
||||||
user: 'Vince McMahon',
|
user: 'Vince McMahon',
|
||||||
start: '15:00',
|
start: '10:00',
|
||||||
end: '18:00',
|
end: '13:00',
|
||||||
boat: '2',
|
boat: '2',
|
||||||
status: 'pending',
|
status: 'pending',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 5,
|
id: 5,
|
||||||
user: 'Heather Graham',
|
user: 'Heather Graham',
|
||||||
start: '09:00',
|
start: '13:00',
|
||||||
end: '12:00',
|
end: '19:00',
|
||||||
boat: '3',
|
boat: '4',
|
||||||
status: 'confirmed',
|
status: 'confirmed',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 6,
|
id: 6,
|
||||||
user: 'Lawrence Fishburne',
|
user: 'Lawrence Fishburne',
|
||||||
start: '18:00',
|
start: '13:00',
|
||||||
end: '21:00',
|
end: '16:00',
|
||||||
boat: '3',
|
boat: '3',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -17,9 +17,10 @@ import {
|
|||||||
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[]>(getSampleReservations());
|
||||||
|
const timeblocks = ref<Timeblock[]>(getSampleTimeBlocks());
|
||||||
|
|
||||||
const getTimeblocksForDate = (date: string): Timeblock[] => {
|
const getTimeblocksForDate = (date: string): Timeblock[] => {
|
||||||
return getSampleTimeBlocks().filter((b) =>
|
return timeblocks.value.filter((b) =>
|
||||||
compareDate(parsed(b.start) as Timestamp, parsed(date) as Timestamp)
|
compareDate(parsed(b.start) as Timestamp, parsed(date) as Timestamp)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user