Many tweaks to booking form.

This commit is contained in:
2023-12-01 00:08:29 -05:00
parent aed0462e05
commit 8600000e24
7 changed files with 312 additions and 242 deletions

View File

@@ -1,69 +1,110 @@
<template>
<div class="row justify-center">
<q-btn-group rounded>
<q-btn
color="primary"
rounded
icon="keyboard_arrow_left"
label="Prev"
@click="onPrev"
/>
<q-btn
color="primary"
rounded
icon="today"
label="Today"
@click="onToday"
/>
<q-btn
color="primary"
rounded
icon-right="keyboard_arrow_right"
label="Next"
@click="onNext"
/>
</q-btn-group>
</div>
<q-calendar-month
ref="calendar"
<q-card-section>
<div class="text-caption text-justify">
Use the calendar to pick a date. Tap a box in the grid for the boat and
start time. Select the duration below.
</div>
<div style="width: 100%; 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"
animated
bordered
mini-mode
date-type="rounded"
@change="onChange"
@moved="onMoved"
@click-date="onClickDate"
/>
</div></div
></q-card-section>
<q-calendar-resource
v-model="selectedDate"
:model-resources="boatStore.boats"
resource-key="id"
resource-label="name"
:interval-start="6"
:interval-count="18"
cell-width="64"
resource-min-height="40"
animated
bordered
mini-mode
date-type="rounded"
@change="onChange"
@moved="onMoved"
@resource-expanded="onResourceExpanded"
@click-date="onClickDate"
/>
<div style="float: right; display: flex; max-width: 1024px; width: 100%">
<q-calendar-resource
ref="calendar"
v-model="selectedDate"
:model-resources="boatStore.boats"
resource-key="id"
resource-label="name"
:interval-start="6"
:interval-count="18"
cell-width="64"
resource-min-height="40"
animated
bordered
@change="onChange"
@moved="onMoved"
@resource-expanded="onResourceExpanded"
@click-date="onClickDate"
@click-time="onClickTime"
@click-resource="onClickResource"
@click-head-resources="onClickHeadResources"
@click-interval="onClickInterval"
>
<template #resource-intervals="{ scope }">
<template v-for="(event, index) in getEvents(scope)" :key="index">
<q-badge outline :label="event.title" :style="getStyle(event)" />
</template>
@click-time="onClickTime"
@click-resource="onClickResource"
@click-head-resources="onClickHeadResources"
@click-interval="onClickInterval"
>
<template #resource-intervals="{ scope }">
<template v-for="(event, index) in getEvents(scope)" :key="index">
<q-badge outline :label="event.title" :style="getStyle(event)" />
</template>
</q-calendar-resource>
</div>
</template>
<template #resource-label="{ scope: { resource } }">
<div class="col-12">
{{ resource.name }}
<q-icon v-if="resource.defects" name="warning" color="warning" />
</div>
</template>
</q-calendar-resource>
<q-card-section>
<q-select
filled
v-model="duration"
:options="durations"
dense
@update:model-value="onUpdateDuration"
label="Duration (hours)"
stack-label
><template v-slot:append><q-icon name="timelapse" /></template></q-select
></q-card-section>
<q-card-section>
<q-btn
color="primary"
class="full-width"
icon="keyboard_arrow_down"
icon-right="keyboard_arrow_down"
label="Next: Crew & Passengers"
@click="onCloseSection"
/></q-card-section>
</template>
<script setup lang="ts">
@@ -73,10 +114,16 @@ import {
TimestampOrNull,
today,
parseDate,
parseTimestamp,
addToDate,
Timestamp,
} from '@quasar/quasar-ui-qcalendar';
import { Boat, useBoatStore } from 'src/stores/boat';
import { useScheduleStore } from 'src/stores/schedule';
import { date } from 'quasar';
import { computed } from 'vue';
const durations = [1, 1.5, 2, 2.5, 3, 3.5, 4];
type ResourceIntervalScope = {
resource: Boat;
@@ -86,15 +133,37 @@ type ResourceIntervalScope = {
};
const statusLookup = {
tentative: ['#f2c037', 'white'],
confirmed: ['#14539a', 'white'],
pending: ['white', 'grey'],
pending: ['#f2c037', 'white'],
tentative: ['white', 'grey'],
};
const calendar = ref();
const boatStore = useBoatStore();
const scheduleStore = useScheduleStore();
const selectedDate = ref(today());
const duration = ref(1);
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) {
//
}
}
function getEvents(scope: ResourceIntervalScope) {
const resourceEvents = scheduleStore.getBoatReservations(
@@ -131,11 +200,8 @@ function getStyle(event: {
};
}
const emit = defineEmits(['onClickDate', 'onClickTime']);
const emit = defineEmits(['onClickTime', 'onCloseSection', 'onUpdateDuration']);
function onToday() {
calendar.value.moveToToday();
}
function onPrev() {
calendar.value.prev();
}
@@ -143,16 +209,28 @@ function onNext() {
calendar.value.next();
}
function onClickDate(data) {
emit('onClickDate', data);
return;
}
function onClickTime(data) {
// TODO: Add a duration picker, here.
emit('onClickTime', data);
}
function onCloseSection() {
emit('onCloseSection');
}
function onUpdateDuration(value) {
emit('onUpdateDuration', value);
}
// eslint-disable-next-line @typescript-eslint/no-empty-function
const onClickInterval = () => {};
const onClickHeadResources = onClickInterval;
const onClickResource = onClickInterval;
const onResourceExpanded = onClickInterval;
const onMoved = onClickInterval;
const onChange = onClickInterval;
// eslint-disable-next-line @typescript-eslint/no-empty-function
const onClickHeadResources = () => {};
// eslint-disable-next-line @typescript-eslint/no-empty-function
const onClickResource = () => {};
// eslint-disable-next-line @typescript-eslint/no-empty-function
const onResourceExpanded = () => {};
// eslint-disable-next-line @typescript-eslint/no-empty-function
const onMoved = () => {};
// eslint-disable-next-line @typescript-eslint/no-empty-function
const onChange = () => {};
</script>

View File

@@ -0,0 +1,40 @@
<template>
<q-select
v-model="boat"
:options="boats"
option-value="id"
option-label="name"
label="Boat"
>
<template v-slot:prepend>
<q-item-section avatar>
<q-img v-if="boat?.iconsrc" :src="boat?.iconsrc" />
<q-icon v-else name="sailing" />
</q-item-section>
</template>
<template v-slot:option="scope">
<q-item v-bind="scope.itemProps">
<q-item-section avatar>
<q-img :src="scope.opt.iconsrc" />
</q-item-section>
<q-item-section>
<q-item-label>{{ scope.opt.name }}</q-item-label>
<q-item-label caption>{{ scope.opt.class }}</q-item-label>
</q-item-section>
<q-item-section avatar v-if="scope.opt.defects">
<q-icon name="warning" color="warning" />
<q-tooltip class="bg-amber text-black shadow-7"
>This boat has notices. Select it to see details.
</q-tooltip>
</q-item-section>
</q-item>
</template>
</q-select>
</template>
<script setup lang="ts">
import { Boat, useBoatStore } from 'src/stores/boat';
const boats = useBoatStore().boats;
const boat = <Boat | undefined>undefined;
</script>