Fix login bug. Improve reservations
This commit is contained in:
@@ -21,7 +21,8 @@
|
|||||||
"file": "^0.2.2",
|
"file": "^0.2.2",
|
||||||
"pinia": "^2.1.7",
|
"pinia": "^2.1.7",
|
||||||
"vue": "3",
|
"vue": "3",
|
||||||
"vue-router": "4"
|
"vue-router": "4",
|
||||||
|
"vue3-google-login": "^2.0.26"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@quasar/app-vite": "^1.9.1",
|
"@quasar/app-vite": "^1.9.1",
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
import { boot } from 'quasar/wrappers';
|
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 { useAuthStore } from 'src/stores/auth';
|
||||||
import { Dialog, Notify } from 'quasar';
|
import { Dialog, Notify } from 'quasar';
|
||||||
import type { Router } from 'vue-router';
|
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({
|
const notification = Notify.create({
|
||||||
type: 'primary',
|
type: 'primary',
|
||||||
position: 'top',
|
position: 'top',
|
||||||
@@ -100,31 +107,31 @@ function login(email: string, password: string) {
|
|||||||
group: false,
|
group: false,
|
||||||
});
|
});
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
authStore
|
try {
|
||||||
.login(email, password)
|
await authStore.login(email, password);
|
||||||
.then(() => {
|
notification({
|
||||||
notification({
|
type: 'positive',
|
||||||
type: 'positive',
|
message: 'Logged in!',
|
||||||
message: 'Logged in!',
|
timeout: 2000,
|
||||||
timeout: 2000,
|
spinner: false,
|
||||||
spinner: false,
|
icon: 'check_circle',
|
||||||
icon: 'check_circle',
|
});
|
||||||
});
|
console.log('Redirecting to index page');
|
||||||
console.log('Redirecting to index page');
|
appRouter.replace({ name: 'index' });
|
||||||
appRouter.replace({ name: 'index' });
|
} catch (error: unknown) {
|
||||||
})
|
notification({
|
||||||
.catch(function (reason: Error) {
|
type: 'negative',
|
||||||
notification({
|
message: 'Login failed.',
|
||||||
type: 'negative',
|
timeout: 2000,
|
||||||
message: 'Login failed.',
|
});
|
||||||
timeout: 1,
|
if (error instanceof AppwriteException) {
|
||||||
});
|
|
||||||
Dialog.create({
|
Dialog.create({
|
||||||
title: 'Login Error!',
|
title: 'Login Error!',
|
||||||
message: reason.message,
|
message: error.message,
|
||||||
persistent: true,
|
persistent: true,
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
export {
|
export {
|
||||||
client,
|
client,
|
||||||
|
|||||||
@@ -52,7 +52,8 @@
|
|||||||
)
|
)
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
{{ getUserName(reservation.user) || 'loading...' }}
|
{{ getUserName(reservation.user) || 'loading...' }}<br />
|
||||||
|
{{ reservation.reason }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -30,8 +30,8 @@
|
|||||||
filled
|
filled
|
||||||
></q-input>
|
></q-input>
|
||||||
<q-btn
|
<q-btn
|
||||||
type="submit"
|
type="button"
|
||||||
@click="login(email, password)"
|
@click="doLogin"
|
||||||
label="Login"
|
label="Login"
|
||||||
color="primary"
|
color="primary"
|
||||||
></q-btn>
|
></q-btn>
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
></q-btn> -->
|
></q-btn> -->
|
||||||
</q-form>
|
</q-form>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
<q-card-section><GoogleOauthComponent /></q-card-section>
|
<!-- <q-card-section><GoogleOauthComponent /></q-card-section> -->
|
||||||
</q-card>
|
</q-card>
|
||||||
</q-page>
|
</q-page>
|
||||||
</q-page-container>
|
</q-page-container>
|
||||||
@@ -69,8 +69,12 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { login } from 'boot/appwrite';
|
import { login } from 'boot/appwrite';
|
||||||
import GoogleOauthComponent from 'src/components/GoogleOauthComponent.vue';
|
// import GoogleOauthComponent from 'src/components/GoogleOauthComponent.vue';
|
||||||
|
|
||||||
const email = ref('');
|
const email = ref('');
|
||||||
const password = ref('');
|
const password = ref('');
|
||||||
|
|
||||||
|
const doLogin = async () => {
|
||||||
|
login(email.value, password.value);
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<q-page>
|
<q-page>
|
||||||
<q-list>
|
<q-list>
|
||||||
<q-form @submit="onSubmit" @reset="onReset" class="q-gutter-sm">
|
<q-form @reset="onReset" class="q-gutter-sm">
|
||||||
<q-item>
|
<q-item>
|
||||||
<q-item-section :avatar="true">
|
<q-item-section :avatar="true">
|
||||||
<q-icon name="person"
|
<q-icon name="person"
|
||||||
@@ -45,52 +45,41 @@
|
|||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
</q-banner>
|
</q-banner>
|
||||||
<!-- <q-card-section>
|
<q-card-section>
|
||||||
<q-btn
|
<q-btn
|
||||||
color="primary"
|
color="primary"
|
||||||
class="full-width"
|
class="full-width"
|
||||||
icon="keyboard_arrow_down"
|
icon="keyboard_arrow_down"
|
||||||
icon-right="keyboard_arrow_down"
|
icon-right="keyboard_arrow_down"
|
||||||
label="Next: Crew & Passengers"
|
label="Next: Booking Details"
|
||||||
@click="resourceView = false"
|
@click="resourceView = false"
|
||||||
/></q-card-section> -->
|
/></q-card-section>
|
||||||
</q-expansion-item>
|
</q-expansion-item>
|
||||||
<!-- <q-expansion-item
|
<q-expansion-item
|
||||||
expand-separator
|
expand-separator
|
||||||
icon="people"
|
icon="people"
|
||||||
label="Crew and Passengers"
|
label="Booking Details"
|
||||||
default-opened
|
default-opened
|
||||||
><q-banner v-if="bookingForm.boat"
|
>
|
||||||
>Passengers:
|
<div class="row">
|
||||||
{{ bookingForm.members.length + bookingForm.guests.length }} /
|
<div class="col-auto">
|
||||||
{{ bookingForm.boat.maxPassengers }}</q-banner
|
<q-select
|
||||||
>
|
filled
|
||||||
<q-item
|
v-model="bookingForm.reason"
|
||||||
class="q-my-sm"
|
:options="reason_options"
|
||||||
v-for="passenger in [...bookingForm.members, ...bookingForm.guests]"
|
label="Reason for sail"
|
||||||
:key="passenger.name"
|
/>
|
||||||
>
|
</div>
|
||||||
<q-item-section avatar>
|
</div>
|
||||||
<q-avatar color="primary" text-color="white" size="sm">
|
</q-expansion-item>
|
||||||
{{
|
|
||||||
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> -->
|
|
||||||
|
|
||||||
<q-item-section>
|
<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-item-section> </q-form
|
||||||
></q-list>
|
></q-list>
|
||||||
</q-page>
|
</q-page>
|
||||||
@@ -113,22 +102,25 @@ interface BookingForm {
|
|||||||
boat?: Boat;
|
boat?: Boat;
|
||||||
startDate?: string;
|
startDate?: string;
|
||||||
endDate?: string;
|
endDate?: string;
|
||||||
|
reason: string;
|
||||||
members: { name: string }[];
|
members: { name: string }[];
|
||||||
guests: { name: string }[];
|
guests: { name: string }[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const reason_options = ['Open Sail', 'Private Sail', 'Racing', 'Other'];
|
||||||
|
|
||||||
const auth = useAuthStore();
|
const auth = useAuthStore();
|
||||||
const dateFormat = 'MMM D, YYYY h:mm A';
|
|
||||||
const resourceView = ref(true);
|
const resourceView = ref(true);
|
||||||
const interval = ref<Interval>();
|
const interval = ref<Interval>();
|
||||||
const bookingForm = ref<BookingForm>({
|
const bookingForm = ref<BookingForm>({
|
||||||
bookingId: getNewId(),
|
bookingId: getNewId(),
|
||||||
name: auth.currentUser?.name,
|
name: auth.currentUser?.name,
|
||||||
boat: <Boat | undefined>undefined,
|
boat: <Boat | undefined>undefined,
|
||||||
startDate: date.formatDate(new Date(), dateFormat),
|
startDate: '',
|
||||||
endDate: date.formatDate(new Date(), dateFormat),
|
endDate: '',
|
||||||
members: [{ name: 'Karen Henrikso' }, { name: "Rich O'hare" }],
|
reason: 'Open Sail',
|
||||||
guests: [{ name: 'Bob Barker' }, { name: 'Taylor Swift' }],
|
members: [],
|
||||||
|
guests: [],
|
||||||
});
|
});
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const reservationStore = useReservationStore();
|
const reservationStore = useReservationStore();
|
||||||
@@ -138,11 +130,8 @@ watch(interval, (new_interval) => {
|
|||||||
bookingForm.value.boat = useBoatStore().boats.find(
|
bookingForm.value.boat = useBoatStore().boats.find(
|
||||||
(b) => b.$id === new_interval?.boatId
|
(b) => b.$id === new_interval?.boatId
|
||||||
);
|
);
|
||||||
bookingForm.value.startDate = date.formatDate(
|
bookingForm.value.startDate = new_interval?.start;
|
||||||
new_interval?.start,
|
bookingForm.value.endDate = new_interval?.end;
|
||||||
dateFormat
|
|
||||||
);
|
|
||||||
bookingForm.value.endDate = date.formatDate(new_interval?.end, dateFormat);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const onReset = () => {
|
const onReset = () => {
|
||||||
@@ -150,6 +139,7 @@ const onReset = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onSubmit = () => {
|
const onSubmit = () => {
|
||||||
|
console.log('SUBMIT!');
|
||||||
const booking = bookingForm.value;
|
const booking = bookingForm.value;
|
||||||
if (
|
if (
|
||||||
!(booking.boat && booking.startDate && booking.endDate && auth.currentUser)
|
!(booking.boat && booking.startDate && booking.endDate && auth.currentUser)
|
||||||
@@ -163,9 +153,11 @@ const onSubmit = () => {
|
|||||||
end: booking.endDate,
|
end: booking.endDate,
|
||||||
user: auth.currentUser.$id,
|
user: auth.currentUser.$id,
|
||||||
status: 'confirmed',
|
status: 'confirmed',
|
||||||
|
reason: booking.reason,
|
||||||
};
|
};
|
||||||
|
console.log(reservation);
|
||||||
// TODO: Fix this. It will always look successful
|
// 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({
|
$q.notify({
|
||||||
color: 'green-4',
|
color: 'green-4',
|
||||||
textColor: 'white',
|
textColor: 'white',
|
||||||
@@ -195,7 +187,9 @@ const bookingSummary = computed(() => {
|
|||||||
return bookingForm.value.boat &&
|
return bookingForm.value.boat &&
|
||||||
bookingForm.value.startDate &&
|
bookingForm.value.startDate &&
|
||||||
bookingForm.value.endDate
|
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>
|
</script>
|
||||||
|
|||||||
@@ -36,12 +36,13 @@ export default route(function (/* { store, ssrContext } */) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
Router.beforeEach((to) => {
|
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) {
|
if (authRequired && !useAuthStore().currentUser) {
|
||||||
return to.meta.accountRoute ? { name: 'index' } : true;
|
return '/login';
|
||||||
} else {
|
|
||||||
return to.name == 'login' ? true : { name: 'login' };
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ export const useReservationStore = defineStore('reservation', () => {
|
|||||||
reservation
|
reservation
|
||||||
);
|
);
|
||||||
reservations.value.set(response.$id, response as Reservation);
|
reservations.value.set(response.$id, response as Reservation);
|
||||||
|
console.info('Reservation booked: ', response);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Error creating Reservation: ' + e);
|
console.error('Error creating Reservation: ' + e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ export function getSampleReservations(): Reservation[] {
|
|||||||
end: '10:00',
|
end: '10:00',
|
||||||
boat: '66359729003825946ae1',
|
boat: '66359729003825946ae1',
|
||||||
status: 'confirmed',
|
status: 'confirmed',
|
||||||
|
reason: 'Open Sail',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '2',
|
id: '2',
|
||||||
@@ -79,6 +80,7 @@ export function getSampleReservations(): Reservation[] {
|
|||||||
end: '19:00',
|
end: '19:00',
|
||||||
boat: '66359729003825946ae1',
|
boat: '66359729003825946ae1',
|
||||||
status: 'confirmed',
|
status: 'confirmed',
|
||||||
|
reason: 'Open Sail',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '3',
|
id: '3',
|
||||||
@@ -87,6 +89,7 @@ export function getSampleReservations(): Reservation[] {
|
|||||||
end: '13:00',
|
end: '13:00',
|
||||||
boat: '663597030029b71c7a9b',
|
boat: '663597030029b71c7a9b',
|
||||||
status: 'tentative',
|
status: 'tentative',
|
||||||
|
reason: 'Open Sail',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '4',
|
id: '4',
|
||||||
@@ -95,6 +98,7 @@ export function getSampleReservations(): Reservation[] {
|
|||||||
end: '13:00',
|
end: '13:00',
|
||||||
boat: '663597030029b71c7a9b',
|
boat: '663597030029b71c7a9b',
|
||||||
status: 'pending',
|
status: 'pending',
|
||||||
|
reason: 'Open Sail',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '5',
|
id: '5',
|
||||||
@@ -103,6 +107,7 @@ export function getSampleReservations(): Reservation[] {
|
|||||||
end: '19:00',
|
end: '19:00',
|
||||||
boat: '663596b9000235ffea55',
|
boat: '663596b9000235ffea55',
|
||||||
status: 'confirmed',
|
status: 'confirmed',
|
||||||
|
reason: 'Private Sail',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '6',
|
id: '6',
|
||||||
@@ -110,6 +115,7 @@ export function getSampleReservations(): Reservation[] {
|
|||||||
start: '13:00',
|
start: '13:00',
|
||||||
end: '16:00',
|
end: '16:00',
|
||||||
boat: '663596b9000235ffea55',
|
boat: '663596b9000235ffea55',
|
||||||
|
reason: 'Open Sail',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const boatStore = useBoatStore();
|
const boatStore = useBoatStore();
|
||||||
@@ -137,6 +143,7 @@ export function getSampleReservations(): Reservation[] {
|
|||||||
end: date.adjustDate(now, makeOpts(splitTime(entry.end))).toISOString(),
|
end: date.adjustDate(now, makeOpts(splitTime(entry.end))).toISOString(),
|
||||||
resource: boat.$id,
|
resource: boat.$id,
|
||||||
reservationDate: now,
|
reservationDate: now,
|
||||||
|
reason: entry.reason,
|
||||||
status: entry.status as StatusTypes,
|
status: entry.status as StatusTypes,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ export type Reservation = Partial<Models.Document> & {
|
|||||||
end: string;
|
end: string;
|
||||||
resource: string; // Boat ID
|
resource: string; // Boat ID
|
||||||
status?: StatusTypes;
|
status?: StatusTypes;
|
||||||
|
reason: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 24 hrs in advance only 2 weekday, and 1 weekend slot
|
// 24 hrs in advance only 2 weekday, and 1 weekend slot
|
||||||
|
|||||||
@@ -5278,6 +5278,11 @@ vue-tsc@^1.8.22:
|
|||||||
"@vue/language-core" "1.8.27"
|
"@vue/language-core" "1.8.27"
|
||||||
semver "^7.5.4"
|
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:
|
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"
|
||||||
|
|||||||
Reference in New Issue
Block a user