Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion src/api/reservation.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ export class ReservationAPI {
return axiosInstanceReservations.get("/req/user");
}

static getActiveReservationsByGuest(): Promise<AxiosResponse<ReservationDTO[]>> {
return axiosInstanceReservations.get('/reservations/guest/active');
}

static getActiveReservationsByHost(): Promise<AxiosResponse<ReservationDTO[]>> {
return axiosInstanceReservations.get('/reservations/host/active');
}

static getRequestsByRoom(roomId: number): Promise<AxiosResponse<ReservationRequestDTO[]>> {
return axiosInstanceReservations.get(`/req/room/${roomId}`);
}
Expand All @@ -58,4 +66,12 @@ export class ReservationAPI {
params: { from, to },
});
}
}

static rejectRequest(id: number): Promise<AxiosResponse<void>> {
return axiosInstanceReservations.put(`/req/${id}/reject`);
}

static approveRequest(id: number): Promise<AxiosResponse<any>> {
return axiosInstanceReservations.put(`/req/${id}/approve`);
}
}
2 changes: 2 additions & 0 deletions src/api/room.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export interface RoomDTO {
maxGuests: number,
photos: string[],
commodities: string[],
autoApprove: boolean
}

export interface RoomCreateDTO {
Expand All @@ -22,6 +23,7 @@ export interface RoomCreateDTO {
maxGuests: number,
photosPayload: string[],
commodities: string[],
autoApprove: boolean
}

export interface CreateRoomAvailabilityListDTO {
Expand Down
2 changes: 1 addition & 1 deletion src/components/Navbar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ const computeNavbar = () => {
case UserRole.Host:
navbarItems.value.push({ label: 'New Room', icon: 'pi pi-plus-circle', command: () => goto('/new-room') });
navbarItems.value.push({ label: 'My Rooms', icon: 'pi pi-building', command: () => goto('/my-rooms') });
navbarItems.value.push({ label: 'Reservations', icon: 'pi pi-address-book', command: () => goto('/') });
navbarItems.value.push({ label: 'Reservations', icon: 'pi pi-address-book', command: () => goto(`/reservation/host`) });
navbarItems.value.push({ label: 'Notifications', icon: 'pi pi-bell', command: () => goto('/') });
navbarItems.value.push({ label: 'Ratings', icon: 'pi pi-star', command: () => goto('/') });
break;
Expand Down
115 changes: 115 additions & 0 deletions src/components/room/RoomActiveRequestEditor.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
<script setup lang="ts">
import { ref, onMounted, watch } from "vue";
import { ReservationAPI, type ReservationRequestDTO } from '../../api/reservation.api';
import type { AxiosError, AxiosResponse } from "axios";

const props = defineProps<{ roomId: number }>();
const requests = ref<ReservationRequestDTO[]>([]);
const requestsError = ref('');
const loading = ref(false);

onMounted(() => {
loadRequests();
});
watch(() => props.roomId, () => loadRequests());

const loadRequests = () => {
loading.value = true;
requestsError.value = '';

ReservationAPI.getRequestsByRoom(props.roomId).then((res: AxiosResponse<ReservationRequestDTO[]>) => {
requests.value = res.data;
}).catch((err: AxiosError) => {
requestsError.value = err.message;
}).finally(() => {
loading.value = false;
});
};

const acceptRequest = (reservationRequestId: number) => {
loading.value = true;
requestsError.value = '';

ReservationAPI.approveRequest(reservationRequestId)
.then(() => {
loadRequests();
})
.catch((err: AxiosError) => {
requestsError.value = err.message;
})
.finally(() => {
loading.value = false;
});
};


const rejectRequest = (reservationRequestId: number) => {
loading.value = true;
requestsError.value = '';

ReservationAPI.rejectRequest(reservationRequestId)
.then(() => {
loadRequests();
})
.catch((err: AxiosError) => {
requestsError.value = err.message;
})
.finally(() => {
loading.value = false;
});
};
</script>

<template>
<ProgressBar v-if="loading" mode="indeterminate" style="height: 6px"></ProgressBar>
<div v-if="requests.length === 0">
You have no active reservation requests for this room.
</div>
<DataTable v-else :value="requests" tableStyle="width: 100%; margin: auto">
<Column field="dateFrom" style="width: 18rem">
<template #header>
Start day <i class="pi pi-forward"></i>
</template>
<template #body="slotProps">
{{ new Date(slotProps.data.dateFrom).toDateString() }}
</template>
</Column>
<Column field="dateTo" style="width: 18rem">
<template #header>
<i class="pi pi-backward"></i> End day
</template>
<template #body="slotProps">
{{ new Date(slotProps.data.dateTo).toDateString() }}
</template>
</Column>
<Column field="guestCount">
<template #header>
<i class="pi pi-users"></i> Guests
</template>
</Column>
<Column field="cost">
<template #header>
<i class="pi pi-money-bill"></i> Cost
</template>
<template #body="slotProps">
${{ slotProps.data.cost }}
</template>
</Column>
<Column style="width: 4rem">
<template #body="slotProps">
<Button v-on:click="acceptRequest(slotProps.data.id)" severity="success">Accept</Button>
</template>
</Column>
<Column style="width: 4rem">
<template #body="slotProps">
<Button v-on:click="rejectRequest(slotProps.data.id)" severity="danger">Reject</Button>
</template>
</Column>
</DataTable>

<Message v-show="requestsError.length > 0" severity="error" size="small" variant="simple">
{{ requestsError }}
</Message>
</template>

<style scoped></style>
2 changes: 2 additions & 0 deletions src/router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import RoomsOfHost from '../views/room/RoomsOfHost.vue';
import ReservationCreate from '../views/reservation/ReservationCreate.vue';
import ReservationsOfGuest from '../views/reservation/ReservationsOfGuest.vue';
import RoomFind from '../views/room/RoomFind.vue';
import ReservationsOfHost from '../views/reservation/ReservationsOfHost.vue';

/**
* A special type for route guards.
Expand Down Expand Up @@ -41,6 +42,7 @@ const routes: RouteRecordRaw[] = [
{ path: '/new-room', component: RoomCreate, meta: { role: [UserRole.Host] } },
{ path: '/room/:id', component: RoomDetails, meta: { role: 'all' } },
{ path: '/my-rooms', component: RoomsOfHost, meta: { role: [UserRole.Host] } },
{ path: '/reservation/host', component: ReservationsOfHost, meta: { role: [UserRole.Host] } },

{ path: `/reservation/new/:id`, component: ReservationCreate, meta: { role: [UserRole.Guest] } },
{ path: `/reservation/user/:id`, component: ReservationsOfGuest, meta: { role: [UserRole.Guest] } },
Expand Down
5 changes: 5 additions & 0 deletions src/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,9 @@ html a {

html a:visited {
color: blue;
}

:root {
--warning-color: #f7b733;
--warning-color-text: #4b3a00;
}
77 changes: 68 additions & 9 deletions src/views/reservation/ReservationsOfGuest.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,19 @@
import { onMounted, ref } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { useAuthStore } from '../../stores/auth-store';
import { ReservationAPI, ReservationRequestStatus, type CreateReservationRequestDTO, type ReservationRequestDTO } from '../../api/reservation.api';
import { ReservationAPI, ReservationRequestStatus, type ReservationDTO, type CreateReservationRequestDTO, type ReservationRequestDTO } from '../../api/reservation.api';
import type { AxiosError, AxiosResponse } from 'axios';
import { useToast } from 'primevue';

const route = useRoute();
const router = useRouter();
const auth = useAuthStore();

const reservations = ref<ReservationDTO[]>([]);
const reservationRequests = ref<ReservationRequestDTO[]>([]);
const userId = ref(-1);

const reservationsError = ref('');
const reservationRequestError = ref('');
const loading = ref(false);

Expand All @@ -30,8 +33,17 @@ onMounted(() => {

const loadReservations = () => {
loading.value = true;
reservationsError.value = '';
reservationRequestError.value = '';

ReservationAPI.getActiveReservationsByGuest().then((res: AxiosResponse<ReservationDTO[]>) => {
reservations.value = res.data;
}).catch((err: AxiosError) => {
reservationsError.value = err.message;
}).finally(() => {
loading.value = false;
});

ReservationAPI.getRequestsByGuest().then((res: AxiosResponse<ReservationRequestDTO[]>) => {
reservationRequests.value = res.data;
}).catch((err: AxiosError) => {
Expand All @@ -41,6 +53,10 @@ const loadReservations = () => {
});
}

const deleteResrvation = (reservationRequestId: number) => {
// TODO: Implement
}

const deleteRequest = (reservationRequestId: number) => {
ReservationAPI.deleteRequest(reservationRequestId).then((res) => {
loadReservations();
Expand All @@ -55,33 +71,76 @@ const deleteRequest = (reservationRequestId: number) => {
<ProgressBar v-if="loading"></ProgressBar>

<h3>Your reservations</h3>
<div v-if="true">
<div v-if="reservations.length === 0">
You have no reservations.
</div>
<div>

</div>
<DataTable v-else :value="reservations" tableStyle="width: 100%; margin: auto">
<Column field="roomId" header="Room ID">
<template #body="slotProps">
<RouterLink :to="`/room/${slotProps.data.roomId}`">#{{ slotProps.data.roomId }}</RouterLink>
</template>
</Column>
<Column field="dateFrom" style="width: 18rem">
<template #header>
Start day <i class="pi pi-forward"></i>
</template>
<template #body="slotProps">
{{ new Date(slotProps.data.dateFrom).toDateString() }}
</template>
</Column>
<Column field="dateTo" style="width: 18rem">
<template #header>
<i class="pi pi-backward"></i> End day
</template>
<template #body="slotProps">
{{ new Date(slotProps.data.dateTo).toDateString() }}
</template>
</Column>
<Column field="guestCount">
<template #header>
<i class="pi pi-users"></i> Guests
</template>
</Column>
<Column field="cost">
<template #header>
<i class="pi pi-money-bill"></i> Cost
</template>
<template #body="slotProps">
${{ slotProps.data.cost }}
</template>
</Column>
<Column header="Delete">
<template #body="slotProps">
<Button v-on:click="deleteResrvation(slotProps.data.id)" severity="danger">Delete</Button>
</template>
</Column>
</DataTable>

<Message v-show="reservationsError.length > 0" severity="error" size="small" variant="simple">
{{ reservationsError }}
</Message>

<h3>Your requests</h3>
<div>
<div v-if="reservationRequests.length == 0">
<div v-if="reservationRequests.length === 0">
You have no active reservation requests.
</div>
<DataTable v-else :value="reservationRequests" tableStyle="width: 80%; margin: auto">
<DataTable v-else :value="reservationRequests" tableStyle="width: 100%; margin: auto">
<Column field="roomId" header="Room ID">
<template #body="slotProps">
<RouterLink :to="`/room/${slotProps.data.roomId}`">#{{ slotProps.data.roomId }}</RouterLink>
</template>
</Column>
<Column field="dateFrom">
<Column field="dateFrom" style="width: 18rem">
<template #header>
Start day <i class="pi pi-forward"></i>
</template>
<template #body="slotProps">
{{ new Date(slotProps.data.dateFrom).toDateString() }}
</template>
</Column>
<Column field="dateTo">

<Column field="dateTo" style="width: 18rem">
<template #header>
<i class="pi pi-backward"></i> End day
</template>
Expand Down
Loading