Fix login bug. Improve reservations

This commit is contained in:
2024-05-21 16:32:31 -04:00
parent 737de91bbc
commit cd692a6f3b
10 changed files with 103 additions and 81 deletions

View File

@@ -21,7 +21,8 @@
"file": "^0.2.2",
"pinia": "^2.1.7",
"vue": "3",
"vue-router": "4"
"vue-router": "4",
"vue3-google-login": "^2.0.26"
},
"devDependencies": {
"@quasar/app-vite": "^1.9.1",

View File

@@ -1,5 +1,12 @@
import { boot } from 'quasar/wrappers';
import { Client, Account, Databases, Functions, ID } from 'appwrite';
import {
Client,
Account,
Databases,
Functions,
ID,
AppwriteException,
} from 'appwrite';
import { useAuthStore } from 'src/stores/auth';
import { Dialog, Notify } from 'quasar';
import type { Router } from 'vue-router';
@@ -90,7 +97,7 @@ async function logout() {
});
}
function login(email: string, password: string) {
async function login(email: string, password: string) {
const notification = Notify.create({
type: 'primary',
position: 'top',
@@ -100,31 +107,31 @@ function login(email: string, password: string) {
group: false,
});
const authStore = useAuthStore();
authStore
.login(email, password)
.then(() => {
notification({
type: 'positive',
message: 'Logged in!',
timeout: 2000,
spinner: false,
icon: 'check_circle',
});
console.log('Redirecting to index page');
appRouter.replace({ name: 'index' });
})
.catch(function (reason: Error) {
notification({
type: 'negative',
message: 'Login failed.',
timeout: 1,
});
try {
await authStore.login(email, password);
notification({
type: 'positive',
message: 'Logged in!',
timeout: 2000,
spinner: false,
icon: 'check_circle',
});
console.log('Redirecting to index page');
appRouter.replace({ name: 'index' });
} catch (error: unknown) {
notification({
type: 'negative',
message: 'Login failed.',
timeout: 2000,
});
if (error instanceof AppwriteException) {
Dialog.create({
title: 'Login Error!',
message: reason.message,
message: error.message,
persistent: true,
});
});
}
}
}
export {
client,

View File

@@ -52,7 +52,8 @@
)
"
>
{{ getUserName(reservation.user) || 'loading...' }}
{{ getUserName(reservation.user) || 'loading...' }}<br />
{{ reservation.reason }}
</div>
</div>
</template>

View File

@@ -30,8 +30,8 @@
filled
></q-input>
<q-btn
type="submit"
@click="login(email, password)"
type="button"
@click="doLogin"
label="Login"
color="primary"
></q-btn>
@@ -44,7 +44,7 @@
></q-btn> -->
</q-form>
</q-card-section>
<q-card-section><GoogleOauthComponent /></q-card-section>
<!-- <q-card-section><GoogleOauthComponent /></q-card-section> -->
</q-card>
</q-page>
</q-page-container>
@@ -69,8 +69,12 @@
<script setup lang="ts">
import { ref } from 'vue';
import { login } from 'boot/appwrite';
import GoogleOauthComponent from 'src/components/GoogleOauthComponent.vue';
// import GoogleOauthComponent from 'src/components/GoogleOauthComponent.vue';
const email = ref('');
const password = ref('');
const doLogin = async () => {
login(email.value, password.value);
};
</script>

View File

@@ -1,7 +1,7 @@
<template>
<q-page>
<q-list>
<q-form @submit="onSubmit" @reset="onReset" class="q-gutter-sm">
<q-form @reset="onReset" class="q-gutter-sm">
<q-item>
<q-item-section :avatar="true">
<q-icon name="person"
@@ -45,52 +45,41 @@
</li>
</ol>
</q-banner>
<!-- <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"
label="Next: Booking Details"
@click="resourceView = false"
/></q-card-section> -->
/></q-card-section>
</q-expansion-item>
<!-- <q-expansion-item
<q-expansion-item
expand-separator
icon="people"
label="Crew and Passengers"
label="Booking Details"
default-opened
><q-banner v-if="bookingForm.boat"
>Passengers:
{{ bookingForm.members.length + bookingForm.guests.length }} /
{{ bookingForm.boat.maxPassengers }}</q-banner
>
<q-item
class="q-my-sm"
v-for="passenger in [...bookingForm.members, ...bookingForm.guests]"
:key="passenger.name"
>
<q-item-section avatar>
<q-avatar color="primary" text-color="white" size="sm">
{{
passenger.name
.split(' ')
.map((i) => i.charAt(0))
.join('')
.toUpperCase()
}}
</q-avatar>
</q-item-section>
<q-item-section>{{ passenger.name }}</q-item-section>
<q-item-section side>
<q-btn color="negative" flat dense round icon="cancel" />
</q-item-section>
</q-item>
<q-separator />
</q-expansion-item> -->
>
<div class="row">
<div class="col-auto">
<q-select
filled
v-model="bookingForm.reason"
:options="reason_options"
label="Reason for sail"
/>
</div>
</div>
</q-expansion-item>
<q-item-section>
<q-btn label="Submit" type="submit" color="primary" />
<q-btn
label="Submit"
type="submit"
@click="onSubmit"
color="primary"
/>
</q-item-section> </q-form
></q-list>
</q-page>
@@ -113,22 +102,25 @@ interface BookingForm {
boat?: Boat;
startDate?: string;
endDate?: string;
reason: string;
members: { name: string }[];
guests: { name: string }[];
}
const reason_options = ['Open Sail', 'Private Sail', 'Racing', 'Other'];
const auth = useAuthStore();
const dateFormat = 'MMM D, YYYY h:mm A';
const resourceView = ref(true);
const interval = ref<Interval>();
const bookingForm = ref<BookingForm>({
bookingId: getNewId(),
name: auth.currentUser?.name,
boat: <Boat | undefined>undefined,
startDate: date.formatDate(new Date(), dateFormat),
endDate: date.formatDate(new Date(), dateFormat),
members: [{ name: 'Karen Henrikso' }, { name: "Rich O'hare" }],
guests: [{ name: 'Bob Barker' }, { name: 'Taylor Swift' }],
startDate: '',
endDate: '',
reason: 'Open Sail',
members: [],
guests: [],
});
const router = useRouter();
const reservationStore = useReservationStore();
@@ -138,11 +130,8 @@ watch(interval, (new_interval) => {
bookingForm.value.boat = useBoatStore().boats.find(
(b) => b.$id === new_interval?.boatId
);
bookingForm.value.startDate = date.formatDate(
new_interval?.start,
dateFormat
);
bookingForm.value.endDate = date.formatDate(new_interval?.end, dateFormat);
bookingForm.value.startDate = new_interval?.start;
bookingForm.value.endDate = new_interval?.end;
});
const onReset = () => {
@@ -150,6 +139,7 @@ const onReset = () => {
};
const onSubmit = () => {
console.log('SUBMIT!');
const booking = bookingForm.value;
if (
!(booking.boat && booking.startDate && booking.endDate && auth.currentUser)
@@ -163,9 +153,11 @@ const onSubmit = () => {
end: booking.endDate,
user: auth.currentUser.$id,
status: 'confirmed',
reason: booking.reason,
};
console.log(reservation);
// TODO: Fix this. It will always look successful
reservationStore.createReservation(reservation);
reservationStore.createReservation(reservation); // Probably should pass the notify as a callback to the reservation creation.
$q.notify({
color: 'green-4',
textColor: 'white',
@@ -195,7 +187,9 @@ const bookingSummary = computed(() => {
return bookingForm.value.boat &&
bookingForm.value.startDate &&
bookingForm.value.endDate
? `${bookingForm.value.boat.name} @ ${bookingForm.value.startDate} for ${bookingDuration.value}`
? `${bookingForm.value.boat.name} @ ${new Date(
bookingForm.value.startDate
).toLocaleString()} for ${bookingDuration.value}`
: '';
});
</script>

View File

@@ -36,12 +36,13 @@ export default route(function (/* { store, ssrContext } */) {
});
Router.beforeEach((to) => {
const auth = useAuthStore();
const publicPages = routes
.filter((route) => route.meta?.publicRoute)
.map((r) => r.path);
const authRequired = !publicPages.includes(to.path);
if (auth.currentUser) {
return to.meta.accountRoute ? { name: 'index' } : true;
} else {
return to.name == 'login' ? true : { name: 'login' };
if (authRequired && !useAuthStore().currentUser) {
return '/login';
}
});

View File

@@ -51,6 +51,7 @@ export const useReservationStore = defineStore('reservation', () => {
reservation
);
reservations.value.set(response.$id, response as Reservation);
console.info('Reservation booked: ', response);
} catch (e) {
console.error('Error creating Reservation: ' + e);
}

View File

@@ -71,6 +71,7 @@ export function getSampleReservations(): Reservation[] {
end: '10:00',
boat: '66359729003825946ae1',
status: 'confirmed',
reason: 'Open Sail',
},
{
id: '2',
@@ -79,6 +80,7 @@ export function getSampleReservations(): Reservation[] {
end: '19:00',
boat: '66359729003825946ae1',
status: 'confirmed',
reason: 'Open Sail',
},
{
id: '3',
@@ -87,6 +89,7 @@ export function getSampleReservations(): Reservation[] {
end: '13:00',
boat: '663597030029b71c7a9b',
status: 'tentative',
reason: 'Open Sail',
},
{
id: '4',
@@ -95,6 +98,7 @@ export function getSampleReservations(): Reservation[] {
end: '13:00',
boat: '663597030029b71c7a9b',
status: 'pending',
reason: 'Open Sail',
},
{
id: '5',
@@ -103,6 +107,7 @@ export function getSampleReservations(): Reservation[] {
end: '19:00',
boat: '663596b9000235ffea55',
status: 'confirmed',
reason: 'Private Sail',
},
{
id: '6',
@@ -110,6 +115,7 @@ export function getSampleReservations(): Reservation[] {
start: '13:00',
end: '16:00',
boat: '663596b9000235ffea55',
reason: 'Open Sail',
},
];
const boatStore = useBoatStore();
@@ -137,6 +143,7 @@ export function getSampleReservations(): Reservation[] {
end: date.adjustDate(now, makeOpts(splitTime(entry.end))).toISOString(),
resource: boat.$id,
reservationDate: now,
reason: entry.reason,
status: entry.status as StatusTypes,
};
});

View File

@@ -8,6 +8,7 @@ export type Reservation = Partial<Models.Document> & {
end: string;
resource: string; // Boat ID
status?: StatusTypes;
reason: string;
};
// 24 hrs in advance only 2 weekday, and 1 weekend slot

View File

@@ -5278,6 +5278,11 @@ vue-tsc@^1.8.22:
"@vue/language-core" "1.8.27"
semver "^7.5.4"
vue3-google-login@^2.0.26:
version "2.0.26"
resolved "https://registry.yarnpkg.com/vue3-google-login/-/vue3-google-login-2.0.26.tgz#0e55dbb3c6cbb78872dee0de800624c749d07882"
integrity sha512-BuTSIeSjINNHNPs+BDF4COnjWvff27IfCBDxK6JPRqvm57lF8iK4B3+zcG8ud6BXfZdyuiDlxletbEDgg4/RFA==
vue@3:
version "3.4.25"
resolved "https://registry.yarnpkg.com/vue/-/vue-3.4.25.tgz#e59d4ed36389647b52ff2fd7aa84bb6691f4205b"