From 4a492a6d9c77dfc13e5466481c1a64f0151cb496 Mon Sep 17 00:00:00 2001 From: eddyhakimi Date: Fri, 20 Jan 2023 14:10:53 +0100 Subject: [PATCH 1/4] Add error route Signed-off-by: eddyhakimi --- src/deskstar-frontend/pages/500.tsx | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/deskstar-frontend/pages/500.tsx diff --git a/src/deskstar-frontend/pages/500.tsx b/src/deskstar-frontend/pages/500.tsx new file mode 100644 index 00000000..c6b46e4a --- /dev/null +++ b/src/deskstar-frontend/pages/500.tsx @@ -0,0 +1,13 @@ +export default function Error() { + return ( +
+

Oooops!

+
+ An internal error occured! +
+
+ If the problem persists, contact your administrator. +
+
+ ); +} From a421426ee845b3b906dd27b8b50b90838b02ea1e Mon Sep 17 00:00:00 2001 From: eddyhakimi Date: Fri, 20 Jan 2023 14:28:05 +0100 Subject: [PATCH 2/4] Refactor error handling of bookings service Signed-off-by: eddyhakimi --- .../lib/api/BookingService.ts | 111 ++++++++++-------- src/deskstar-frontend/pages/bookings/add.tsx | 53 ++++----- .../pages/bookings/index.tsx | 47 ++++---- src/deskstar-frontend/pages/index.tsx | 94 ++++++++------- 4 files changed, 163 insertions(+), 142 deletions(-) diff --git a/src/deskstar-frontend/lib/api/BookingService.ts b/src/deskstar-frontend/lib/api/BookingService.ts index 1b2e88c0..bf8492c7 100644 --- a/src/deskstar-frontend/lib/api/BookingService.ts +++ b/src/deskstar-frontend/lib/api/BookingService.ts @@ -30,6 +30,13 @@ function getParams(queryOptions: QueryOptions) { return params; } +/** + * Gets list of bookings associated with user session + * @param session The associated user session + * @param queryOptions The Query parameter for the request + * @returns The total amount of bookings and list of bookings according to `queryOptions` + * @throws Error containing status code and/or error message + */ export async function getBookings( session: Session, queryOptions: QueryOptions @@ -47,12 +54,7 @@ export async function getBookings( }, }); - if (response.status !== 200) { - return { - amountOfBookings: 0, - bookings: [], - }; - } + if (!response.ok) throw Error(`${response.status} ${response.statusText}`); const data = await response.json(); @@ -74,6 +76,15 @@ export async function getBookings( return bookingsResponse; } +/** + * Creates a user booking + * @param session The associated user session + * @param deskId The desk id associated to the booking + * @param startTime Start time of the booking + * @param endTime End time of the booking + * @returns The response object + * @throws Error containing status code and/or error message + */ export async function createBooking( session: Session, deskId: string, @@ -92,35 +103,34 @@ export async function createBooking( endTime, }), }); - console.log( - JSON.stringify({ - deskId, - startTime, - endTime, - }) - ); - console.log(await response.status); - if (response.status !== 200) { + + if (!response.ok) { let text = await response.text(); // take away the quotes text = text.substring(1, text.length - 1); - console.log(text); - switch (text) { - case "User not found": - return "User not found"; - case "Desk not found": - return "The desk you are trying to book does not exist"; - case "Time slot not available": - return "The time slot you are trying to book is not available"; - default: - return "An unknown error occurred"; - } - } else { - return "success"; + + const textToErrorMessage: { [key: string]: string } = { + "User not found": "User not found", + "Desk not found": "The desk you are trying to book does not exist", + "Time slot not available": + "The time slot you are trying to book is not available", + }; + + throw Error( + `${response.status} ${textToErrorMessage[text] ?? response.statusText}` + ); } + + return response; } -// create function for deleting booking. BookingId is passed as query parameter +/** + * Deletes a booking associated to an user + * @param session The associated user session + * @param bookingId The bookings id + * @returns The response object + * @throws Error containing status code and/or error message + */ export async function deleteBooking(session: Session, bookingId: string) { const response = await fetch(BACKEND_URL + `/bookings/${bookingId}`, { method: "DELETE", @@ -130,32 +140,33 @@ export async function deleteBooking(session: Session, bookingId: string) { }); // check if response is 200. If not return error message - if ((await response.status) !== 200) { + if (!response.ok) { let text = await response.text(); text = text.substring(1, text.length - 1); - console.log(text); - switch (text) { - case "User not found": - return "User not found"; - case "Booking not found": - return "The booking you are trying to delete does not exist"; - case "You are not allowed to delete this booking": - return "You are not allowed to delete this booking"; - default: - return "An unknown error occurred"; - } - } else { - return "success"; + + const textToErrorMessage: { [key: string]: string } = { + "User not found": "User not found", + "Booking not found": + "The booking you are trying to delete does not exist", + "You are not allowed to delete this booking": + "You are not allowed to delete this booking", + }; + + throw Error( + `${response.status} ${textToErrorMessage[text] ?? response.statusText}` + ); } + return response; } /** - * Update start and end time for a given booking + * Update start and end time for a given booking associated to an user * @param session the user session - * @param bookingId + * @param bookingId The bookings id * @param startTime new start time for given booking * @param endTime new end time for given booking - * @returns + * @returns The updated booking + * @throws Error containing status code and/or error message */ export async function updateBooking( session: Session, @@ -163,7 +174,7 @@ export async function updateBooking( startTime: string, endTime: string ) { - return await fetch(BACKEND_URL + `/bookings/${bookingId}`, { + const response = await fetch(BACKEND_URL + `/bookings/${bookingId}`, { method: "PUT", headers: { "Content-Type": "application/json", @@ -174,4 +185,10 @@ export async function updateBooking( endTime, }), }); + + if (!response.ok) throw Error(`${response.status} ${response.statusText}`); + + //TODO: fix this + //return await response.json(); + return response; } diff --git a/src/deskstar-frontend/pages/bookings/add.tsx b/src/deskstar-frontend/pages/bookings/add.tsx index 6fc235e1..978e1f24 100644 --- a/src/deskstar-frontend/pages/bookings/add.tsx +++ b/src/deskstar-frontend/pages/bookings/add.tsx @@ -52,28 +52,15 @@ export default function AddBooking({ event.target.setAttribute("class", "btn loading"); try { - let message; - - let response = await createBooking( - session, - desk.deskId, - startDateTime, - endDateTime - ); - - if (response == "success") { - message = `You successfully booked the desk ${desk.deskName} from ${startDateTime} to ${endDateTime}`; - event.target.setAttribute("class", "btn btn-disabled"); - setButtonText("Booked"); - toast.success(message); - } else { - console.log(response); - message = response; - event.target.setAttribute("class", "btn btn-success"); - toast.error(message); - } + await createBooking(session, desk.deskId, startDateTime, endDateTime); + + const message = `You successfully booked the desk ${desk.deskName} from ${startDateTime} to ${endDateTime}`; + event.target.setAttribute("class", "btn btn-disabled"); + setButtonText("Booked"); + toast.success(message); } catch (error) { - toast.error(`Error calling createBooking: ${error}`); + console.error(error); + toast.error(`${error}`); event.target.setAttribute("class", "btn btn-success"); } } @@ -166,19 +153,27 @@ export const getServerSideProps: GetServerSideProps = async (context) => { authOptions ); - if (session) { - const buildings = await getBuildings(session); + if (!session) + return { + redirect: { + destination: "/login", + permanent: false, + }, + }; + try { + const buildings = await getBuildings(session); return { props: { buildings, }, }; + } catch (error) { + return { + redirect: { + destination: "500", + permanent: false, + }, + }; } - - return { - props: { - buildings: [], - }, - }; }; diff --git a/src/deskstar-frontend/pages/bookings/index.tsx b/src/deskstar-frontend/pages/bookings/index.tsx index a41bca47..024dd3b0 100644 --- a/src/deskstar-frontend/pages/bookings/index.tsx +++ b/src/deskstar-frontend/pages/bookings/index.tsx @@ -41,16 +41,12 @@ export default function Bookings({ console.log(`Pressed delete on ${booking.bookingId}`); try { - let response = await deleteBooking(session, booking.bookingId); - - if (response == "success") { - toast.success("Booking successfully deleted!"); - refreshData(currentPage); - } else { - toast.error(response); - } + await deleteBooking(session, booking.bookingId); + + toast.success("Booking successfully deleted!"); + refreshData(currentPage); } catch (error) { - toast.error("Error calling Server:" + error); + toast.error(`${error}`); } }; @@ -72,21 +68,17 @@ export default function Bookings({ }; try { - const response = await updateBooking( + await updateBooking( session, booking.bookingId, update.startTime, update.endTime ); - console.log(response); - if (!response.ok) - return toast.error(`Error: ${response.status} ${response.statusText}`); toast.success(`Booking successfully updated.`); - refreshData(currentPage); } catch (error) { - toast.error("Error during update: " + error); + toast.error(`${error}`); } }; @@ -116,13 +108,21 @@ export const getServerSideProps: GetServerSideProps = async (context) => { authOptions ); - if (session) { - const { page } = context.query as { - page: string | undefined; + if (!session) + return { + redirect: { + destination: "/login", + permanent: false, + }, }; - const direction = "ASC"; + const { page } = context.query as { + page: string | undefined; + }; + + const direction = "ASC"; + try { const data = await getBookings(session, { n: DEFAULT_N, skip: parseInt(page ?? "0") * DEFAULT_N, @@ -136,11 +136,12 @@ export const getServerSideProps: GetServerSideProps = async (context) => { amountOfBookings: data.amountOfBookings, }, }; - } else { + } catch (error) { + console.error(error); return { - props: { - bookings: [], - amountOfBookings: 0, + redirect: { + destination: "/500", + permanent: false, }, }; } diff --git a/src/deskstar-frontend/pages/index.tsx b/src/deskstar-frontend/pages/index.tsx index 3c3076e7..15925b9c 100644 --- a/src/deskstar-frontend/pages/index.tsx +++ b/src/deskstar-frontend/pages/index.tsx @@ -4,7 +4,6 @@ import Head from "next/head"; import BookingsTable from "../components/BookingsTable"; import { IBooking } from "../types/booking"; //TODO: delete this when fetching from database -import { bookings } from "../bookings"; import { unstable_getServerSession } from "next-auth"; import { authOptions } from "./api/auth/[...nextauth]"; import { getBookings } from "../lib/api/BookingService"; @@ -20,8 +19,6 @@ export default function AppHome({ }) { const { data: session } = useSession(); - //console.log(session?.accessToken); - const [buttonText, setButtonText] = useState("Check in"); const [checkedIn, setCheckedIn] = useState(false); @@ -74,54 +71,65 @@ export const getServerSideProps: GetServerSideProps = async (context) => { authOptions ); - if (session) { - const start = new Date(); - start.setHours(0, 0, 0); + if (!session) { + return { + redirect: { + destination: "/login", + permanent: false, + }, + }; + } + + const start = new Date(); + start.setHours(0, 0, 0); - const data = await getBookings(session, { + let data; + try { + data = await getBookings(session, { n: 10, direction: "DESC", start: start.getTime(), }); + } catch (error) { + console.error(error); + return { + redirect: { + destination: "/500", + permanent: false, + }, + }; + } - const bookingsToday = data.bookings.filter((booking: IBooking) => { - const today = new Date().toISOString(); - const startOfDay = new Date(today); - startOfDay.setHours(0, 0, 0); - const endOfDay = new Date(today); - endOfDay.setHours(23, 59, 59); - const startTime = new Date(booking.startTime).toISOString(); - const endTime = new Date(booking.endTime).toISOString(); + const bookingsToday = data.bookings.filter((booking: IBooking) => { + const today = new Date().toISOString(); + const startOfDay = new Date(today); + startOfDay.setHours(0, 0, 0); + const endOfDay = new Date(today); + endOfDay.setHours(23, 59, 59); + const startTime = new Date(booking.startTime).toISOString(); + const endTime = new Date(booking.endTime).toISOString(); - return ( - (startTime >= startOfDay.toISOString() && - startTime <= endOfDay.toISOString()) || - (endTime >= startOfDay.toISOString() && - endTime <= endOfDay.toISOString()) - ); - }); + return ( + (startTime >= startOfDay.toISOString() && + startTime <= endOfDay.toISOString()) || + (endTime >= startOfDay.toISOString() && endTime <= endOfDay.toISOString()) + ); + }); - const bookingsTomorrow = data.bookings.filter((booking: IBooking) => { - const todayTimestamp = new Date().getTime(); + const bookingsTomorrow = data.bookings.filter((booking: IBooking) => { + const todayTimestamp = new Date().getTime(); - let tomorrowTimestamp = todayTimestamp + 86400000; - let tomorrow = new Date(tomorrowTimestamp); - tomorrow.setHours(0, 0, 0); - const startTime = new Date(booking.startTime).getTime(); - return startTime >= tomorrow.getTime(); - }); + let tomorrowTimestamp = todayTimestamp + 86400000; + let tomorrow = new Date(tomorrowTimestamp); + tomorrow.setHours(0, 0, 0); + const startTime = new Date(booking.startTime).getTime(); + return startTime >= tomorrow.getTime(); + }); - return { - props: { - bookingsToday, - bookingsTomorrow, - }, - }; - } else { - return { - props: { - bookings: [], - }, - }; - } + return { + props: { + bookingsToday, + bookingsTomorrow, + }, + }; }; From 3f103acb237c7a15682d1b4cb22317353bfd1d37 Mon Sep 17 00:00:00 2001 From: eddyhakimi Date: Fri, 20 Jan 2023 18:09:52 +0100 Subject: [PATCH 3/4] Refactor error handling of userService Signed-off-by: eddyhakimi --- src/deskstar-frontend/lib/api/UserService.ts | 74 ++++++++++++-- .../pages/api/auth/[...nextauth].ts | 27 ++--- src/deskstar-frontend/pages/users/index.tsx | 98 +++++++++---------- .../pages/users/requests.tsx | 33 ++++--- 4 files changed, 143 insertions(+), 89 deletions(-) diff --git a/src/deskstar-frontend/lib/api/UserService.ts b/src/deskstar-frontend/lib/api/UserService.ts index 73a33593..88a30efb 100644 --- a/src/deskstar-frontend/lib/api/UserService.ts +++ b/src/deskstar-frontend/lib/api/UserService.ts @@ -2,6 +2,12 @@ import { Session } from "next-auth"; import { IUser } from "../../types/users"; import { BACKEND_URL } from "./constants"; +/** + * Lists all users associated with users company + * @param session The associated user session + * @returns All users associated with the given usersessions company + * @throws Error containing status code and/or error message + */ export async function getUsers(session: Session): Promise { // const response = await fetch(BACKEND_URL + "/resources/locations"); const response = await fetch(BACKEND_URL + "/users", { @@ -10,10 +16,7 @@ export async function getUsers(session: Session): Promise { }, }); - if (response.status !== 200) { - console.log("Error fetching "); - return []; - } + if (!response.ok) throw Error(`${response.status} ${response.statusText}`); const data = await response.json(); @@ -30,47 +33,86 @@ export async function getUsers(session: Session): Promise { }); } -export function approveUser( +/** + * Approves an user by `userId` + * @param session The associated user session + * @param userId The user id + * @returns The fetch response object + * @throws Error containing status code and/or error message + */ +export async function approveUser( session: Session, userId: string ): Promise { - return fetch(BACKEND_URL + `/users/${userId}/approve`, { + const response = await fetch(BACKEND_URL + `/users/${userId}/approve`, { method: "POST", headers: { Authorization: `Bearer ${session.accessToken}`, }, }); + + if (!response.ok) throw Error(`${response.status} ${response.statusText}`); + + return response; } -export function declineUser( +/** + * Declines an user by `userId` + * @param session The associated user session + * @param userId The user id + * @returns The fetch response + * @throws Error containing status code and/or error message + */ +export async function declineUser( session: Session, userId: string ): Promise { - return fetch(BACKEND_URL + `/users/${userId}/decline`, { + const response = await fetch(BACKEND_URL + `/users/${userId}/decline`, { method: "POST", headers: { Authorization: `Bearer ${session.accessToken}`, }, }); + if (!response.ok) throw Error(`${response.status} ${response.statusText}`); + + return response; } +/** + * Deletes an user by `userId` + * @param session The associated user session + * @param userId The user id + * @returns The fetch response + * @throws Error containing status code and/or error message + */ export async function deleteUser( session: Session, userId: string ): Promise { - return fetch(BACKEND_URL + `/users/delete/${userId}`, { + const response = await fetch(BACKEND_URL + `/users/delete/${userId}`, { method: "DELETE", headers: { Authorization: `Bearer ${session.accessToken}`, }, }); + + if (!response.ok) throw Error(`${response.status} ${response.statusText}`); + + return response; } +/** + * Updates an user + * @param session The associated user session + * @param user The updated user data + * @returns The fetch response + * @throws Error containing status code and/or error message + */ export async function editUser( session: Session, user: IUser ): Promise { - return await fetch(BACKEND_URL + `/users/edit`, { + const response = await fetch(BACKEND_URL + `/users/edit`, { method: "POST", headers: { Authorization: `Bearer ${session.accessToken}`, @@ -85,6 +127,10 @@ export async function editUser( companyId: user.company, }), }); + + if (!response.ok) throw Error(`${response.status} ${response.statusText}`); + + return response; } type UserResponse = { @@ -102,6 +148,12 @@ type UserResponse = { }; }; +/** + * Get user from accessToken + * @param accessToken The jwt token received by the backend + * @returns The user associated with `accessToken` + * @throws Error containing status code and/or error message + */ export async function getUserMe( accessToken: String ): Promise { @@ -115,5 +167,7 @@ export async function getUserMe( }, }); + if (!response.ok) throw Error(`${response.status} ${response.statusText}`); + return response.json(); } diff --git a/src/deskstar-frontend/pages/api/auth/[...nextauth].ts b/src/deskstar-frontend/pages/api/auth/[...nextauth].ts index c8b7313a..43c75ebf 100644 --- a/src/deskstar-frontend/pages/api/auth/[...nextauth].ts +++ b/src/deskstar-frontend/pages/api/auth/[...nextauth].ts @@ -1,8 +1,6 @@ import NextAuth, { Awaitable, NextAuthOptions, User } from "next-auth"; import CredentialsProvider from "next-auth/providers/credentials"; -import { userAgent } from "next/server"; import { AuthResponse, authorize } from "../../../lib/api/AuthService"; -import { Session } from "next-auth"; import { getUserMe } from "../../../lib/api/UserService"; export const authOptions: NextAuthOptions = { @@ -33,12 +31,16 @@ export const authOptions: NextAuthOptions = { let accessToken = result as string; - const userResponse = await getUserMe(accessToken); - - if (!userResponse) { + let userResponse; + try { + userResponse = await getUserMe(accessToken); + } catch (error) { + console.error(error); return null; } + if (!userResponse) return null; + const user: User = { id: userResponse.userId, company: userResponse.company, @@ -49,11 +51,8 @@ export const authOptions: NextAuthOptions = { our_token: accessToken, }; - if (user) { - return user; - } else { - return null; - } + if (!user) return null; + return user; }, }), // ...add more providers here @@ -74,11 +73,15 @@ export const authOptions: NextAuthOptions = { session.accessToken = token.accessToken; // There is a cache definition in getUserMe to prevent overloading the backend - const userResponse = await getUserMe(token.accessToken); - if (!userResponse) { + let userResponse; + try { + userResponse = await getUserMe(token.accessToken); + } catch (error) { return session; } + if (!userResponse) return session; + session.user = { id: userResponse.userId, company: userResponse.company, diff --git a/src/deskstar-frontend/pages/users/index.tsx b/src/deskstar-frontend/pages/users/index.tsx index 30543b40..06ac7c26 100755 --- a/src/deskstar-frontend/pages/users/index.tsx +++ b/src/deskstar-frontend/pages/users/index.tsx @@ -58,58 +58,47 @@ export default function UsersOverview({ users }: { users: IUser[] }) { } async function doDelete() { - if (user) { - if (session == null) return; - let result = await deleteUser(session, user.userId); - - if (result.ok) { - toast.success(`User ${user.firstName} ${user.lastName} deleted!`); - - // Remove the user from userList - setUserList(userList.filter((u) => user.userId !== u.userId)); - } else { - toast.error( - `User ${user.firstName} ${user.lastName} could not be deleted!` - ); - } + if (!user || !session) return toast.error("Error: Couldn't delete user!"); + + try { + await deleteUser(session, user.userId); + toast.success(`User ${user.firstName} ${user.lastName} deleted!`); + // Remove the user from userList + setUserList(userList.filter((u) => user.userId !== u.userId)); + } catch (error) { + toast.error(`${error}`); } } async function doChangePermissions() { - if (user) { - if (session == null) return; + if (!user || !session) return; - // Toggle isAdmin - const newUser = { - ...user, - isAdmin: !user.isAdmin, - }; - - setUser(newUser); + // Toggle isAdmin + const newUser = { + ...user, + isAdmin: !user.isAdmin, + }; - let result = await editUser(session, newUser); + setUser(newUser); - if (result.ok) { - toast.success(`User ${user.firstName} ${user.lastName} updated!`); + try { + await editUser(session, newUser); + toast.success(`User ${user.firstName} ${user.lastName} updated!`); - // Change the user in userList - setUserList( - userList.map((u) => (newUser.userId === u.userId ? newUser : u)) - ); - } else { - toast.error( - `User ${user.firstName} ${user.lastName} could not be updated!` - ); - } + // Change the user in userList + setUserList( + userList.map((u) => (newUser.userId === u.userId ? newUser : u)) + ); + } catch (error) { + toast.error(`${error}`); } } async function doEdit(newUser: IUser): Promise { if (session == null) return false; - let result = await editUser(session, newUser); - - if (result.ok) { + try { + await editUser(session, newUser); setUser(newUser); toast.success(`User ${newUser.firstName} ${newUser.lastName} updated!`); @@ -121,12 +110,10 @@ export default function UsersOverview({ users }: { users: IUser[] }) { ) ); return true; - } else { - toast.error( - `User ${newUser.firstName} ${newUser.lastName} could not be updated!` - ); + } catch (error) { + toast.error(`${error}`); + return false; } - return false; } return ( @@ -177,7 +164,6 @@ export default function UsersOverview({ users }: { users: IUser[] }) { ); } -//TODO: delete this when using backend data instead of mockup export const getServerSideProps: GetServerSideProps = async (context) => { const session = await unstable_getServerSession( context.req, @@ -185,7 +171,15 @@ export const getServerSideProps: GetServerSideProps = async (context) => { authOptions ); - if (session) { + if (!session) + return { + redirect: { + destination: "/login", + permanent: false, + }, + }; + + try { const users = await getUsers(session); return { @@ -193,11 +187,13 @@ export const getServerSideProps: GetServerSideProps = async (context) => { users: users.filter((user: IUser) => user.isApproved), }, }; + } catch (error) { + console.error(error); + return { + redirect: { + destination: "/500", + permanent: false, + }, + }; } - - return { - props: { - users: [], - }, - }; }; diff --git a/src/deskstar-frontend/pages/users/requests.tsx b/src/deskstar-frontend/pages/users/requests.tsx index 0e6c5fc8..3c1d1b1d 100644 --- a/src/deskstar-frontend/pages/users/requests.tsx +++ b/src/deskstar-frontend/pages/users/requests.tsx @@ -46,11 +46,6 @@ export default function UserRequests({ const response: Response = decision ? await approveUser(session, user.userId) : await declineUser(session, user.userId); - - if (!response.ok) { - const error = await response.json(); - toast.error(error.detail); - } } // success @@ -65,9 +60,7 @@ export default function UserRequests({ ) ); } catch (error) { - toast.error( - `There has been a problem with your fetch operation: ${error}` - ); + toast.error(`${error}`); } }; @@ -99,19 +92,27 @@ export const getServerSideProps: GetServerSideProps = async (context) => { authOptions ); - if (session) { + if (!session) + return { + redirect: { + destination: "/login", + permanent: false, + }, + }; + try { const users = await getUsers(session); - return { props: { initialUsers: users.filter((user: IUser) => !user.isApproved), }, }; + } catch (error) { + console.error(error); + return { + redirect: { + destination: "/500", + permanent: false, + }, + }; } - - return { - props: { - users: [], - }, - }; }; From bc9f7771b9e402b8e2d723374d6eaebb788edbe5 Mon Sep 17 00:00:00 2001 From: eddyhakimi Date: Fri, 20 Jan 2023 19:08:53 +0100 Subject: [PATCH 4/4] Refactor error handling of resourceService Signed-off-by: eddyhakimi --- .../components/AddResourceModal.tsx | 36 +++---- .../components/Filterbar.tsx | 53 +++++----- .../lib/api/ResourceService.ts | 99 +++++++++++++------ .../pages/resources/index.tsx | 65 ++++++++---- 4 files changed, 164 insertions(+), 89 deletions(-) diff --git a/src/deskstar-frontend/components/AddResourceModal.tsx b/src/deskstar-frontend/components/AddResourceModal.tsx index ff0199dd..a468f8fb 100644 --- a/src/deskstar-frontend/components/AddResourceModal.tsx +++ b/src/deskstar-frontend/components/AddResourceModal.tsx @@ -6,7 +6,6 @@ import { createDeskType, createFloor, createRoom, - getDeskTypes, getFloors, getRooms, } from "../lib/api/ResourceService"; @@ -87,16 +86,18 @@ const AddResourceModal = ({ async function onSelectedBuildingChange( selectedBuilding: IBuilding | null | undefined ) { - if (!selectedBuilding) { - return; - } + if (!selectedBuilding) return; setBuilding(selectedBuilding); - if (!session) { - return []; + + if (!session) return []; + + try { + const resFloors = await getFloors(session, selectedBuilding.buildingId); + setFloors(resFloors); + } catch (error) { + toast.error(`${error}`); } - const resFloors = await getFloors(session, selectedBuilding.buildingId); - setFloors(resFloors); setFloor(null); setRoom(null); @@ -105,26 +106,25 @@ const AddResourceModal = ({ async function onSelectedFloorChange( selectedFloor: IFloor | null | undefined ) { - if (!selectedFloor) { - return; - } + if (!selectedFloor) return; setFloor(selectedFloor); - if (!session) { - return []; + if (!session) return []; + + try { + const resRooms = await getRooms(session, selectedFloor.floorId); + setRooms(resRooms); + } catch (error) { + toast.error(`${error}`); } - const resRooms = await getRooms(session, selectedFloor.floorId); - setRooms(resRooms); setRoom(null); } async function onSelectedDeskTypeChange( onSelectedDeskType: IDeskType | null | undefined ) { - if (!onSelectedDeskType) { - return; - } + if (!onSelectedDeskType) return; setDeskType(onSelectedDeskType); } diff --git a/src/deskstar-frontend/components/Filterbar.tsx b/src/deskstar-frontend/components/Filterbar.tsx index bb5d8de1..27ce7ceb 100644 --- a/src/deskstar-frontend/components/Filterbar.tsx +++ b/src/deskstar-frontend/components/Filterbar.tsx @@ -1,5 +1,6 @@ import { useSession } from "next-auth/react"; import { useState, Fragment } from "react"; +import { toast } from "react-toastify"; import { getDesks, getFloors, getRooms } from "../lib/api/ResourceService"; import { IBuilding } from "../types/building"; import { IDesk } from "../types/desk"; @@ -78,9 +79,12 @@ export default function Filterbar({ return; } - const floors = await getFloors(session, selectedBuilding.buildingId); - - setFloors(floors); + try { + const floors = await getFloors(session, selectedBuilding.buildingId); + setFloors(floors); + } catch (error) { + toast.error(`${error}`); + } setSelectedFloor(null); } @@ -97,37 +101,40 @@ export default function Filterbar({ return; } - const rooms = await getRooms(session, selectedFloor.floorId); + try { + const rooms = await getRooms(session, selectedFloor.floorId); + setRooms(rooms); + } catch (error) { + toast.error(`${error}`); + } - setRooms(rooms); setSelectedRoom(null); } async function setSelectedRoom(selectedRoom: IRoom | null) { _setSelectedRoom(selectedRoom); - if (!selectedRoom) { - return; - } + if (!selectedRoom) return; - if (!session) { - setRooms([]); - return; - } + if (!session) return setRooms([]); - const desks = await getDesks( - session, - selectedRoom.roomId, - startDateTime.getTime(), - endDateTime.getTime() - ); + try { + const desks = await getDesks( + session, + selectedRoom.roomId, + startDateTime.getTime(), + endDateTime.getTime() + ); - const filteredDesks = desks.filter((desk) => desk.bookings.length === 0); + const filteredDesks = desks.filter((desk) => desk.bookings.length === 0); - setDeskTypes(deskTypes); - setSelectedDeskType(null); // Equals all there + setDeskTypes(deskTypes); + setSelectedDeskType(null); // Equals all there - setDesks(filteredDesks); - setFilteredDesks(filteredDesks); + setDesks(filteredDesks); + setFilteredDesks(filteredDesks); + } catch (error) { + toast.error(`${error}`); + } } function setSelectedDeskType(selectedDeskType: IDeskType | null) { diff --git a/src/deskstar-frontend/lib/api/ResourceService.ts b/src/deskstar-frontend/lib/api/ResourceService.ts index e206db51..b30cb1a2 100644 --- a/src/deskstar-frontend/lib/api/ResourceService.ts +++ b/src/deskstar-frontend/lib/api/ResourceService.ts @@ -22,6 +22,12 @@ export enum ResourceResponse { Success, } +/** + * Lists buildings associated to company of given usersession + * @param session The user session + * @returns The list of company buildings + * @throws Error containing status code and/or error message + */ export async function getBuildings(session: Session): Promise { const response = await fetch(BACKEND_URL + "/resources/buildings", { headers: { @@ -29,17 +35,19 @@ export async function getBuildings(session: Session): Promise { }, }); - if (response.status !== 200) { - console.log(response.status); - console.log("Error fetching buildings"); - return []; - } + if (!response.ok) throw Error(`${response.status} ${response.statusText}`); const data = await response.json(); - return data; } +/** + * Lists all floors of a building + * @param session The user session + * @param buildingId The building id + * @returns All floors of `buildingId` + * @throws Error containing status code and/or error message + */ export async function getFloors( session: Session, buildingId: string @@ -53,17 +61,19 @@ export async function getFloors( } ); - if (response.status !== 200) { - console.log(response.status); - console.log("Error fetching floors"); - return []; - } + if (!response.ok) throw Error(`${response.status} ${response.statusText}`); const data = await response.json(); - return data; } +/** + * Lists all rooms of a floor + * @param session The user session + * @param floorId The floor id + * @returns All rooms of `floorId` + * @throws Error containing status code and/or error message + */ export async function getRooms( session: Session, floorId: string @@ -77,17 +87,21 @@ export async function getRooms( } ); - if (response.status !== 200) { - console.log(response.status); - console.log("Error fetching rooms"); - return []; - } + if (!response.ok) throw Error(`${response.status} ${response.statusText}`); const data = await response.json(); - return data; } +/** + * Lists all available desks of a room + * @param session The user session + * @param roomId The room id + * @param startTime + * @param endTime + * @returns All available desks + * @throws Error containing status code and/or error message + */ export async function getDesks( session: Session, roomId: string, @@ -104,17 +118,18 @@ export async function getDesks( } ); - if (response.status !== 200) { - console.log(response.status); - console.log("Error fetching desks"); - return []; - } + if (!response.ok) throw Error(`${response.status} ${response.statusText}`); const data = await response.json(); - return data; } +/** + * Lists all defined desk types associated to usersessions company + * @param session The user session + * @returns All defined desk types associated to usersessions company + * @throws Error containing status code and/or error message + */ export async function getDeskTypes(session: Session): Promise { const response = await fetch(BACKEND_URL + `/resources/desktypes`, { headers: { @@ -122,11 +137,7 @@ export async function getDeskTypes(session: Session): Promise { }, }); - if (response.status !== 200) { - console.log(response.status); - console.log("Error fetching desks"); - return []; - } + if (!response.ok) throw Error(`${response.status} ${response.statusText}`); const data = await response.json(); const resDeskTypes = data.map((e: any) => { @@ -139,6 +150,12 @@ export async function getDeskTypes(session: Session): Promise { return resDeskTypes; } +/** + * Creates a building + * @param session The user session + * @param createBuildingDto the building data for the post request + * @returns + */ export async function createBuilding( session: Session, createBuildingDto: CreateBuildingDto @@ -172,6 +189,12 @@ export async function createBuilding( return result; } +/** + * Creates a floor + * @param session The user session + * @param createFloorDto The floor data for the post request + * @returns + */ export async function createFloor( session: Session, createFloorDto: CreateFloorDto @@ -205,6 +228,12 @@ export async function createFloor( return result; } +/** + * Creates a room + * @param session The user session + * @param createRoomDto The room data for the post request + * @returns + */ export async function createRoom( session: Session, createRoomDto: CreateRoomDto @@ -238,6 +267,12 @@ export async function createRoom( return result; } +/** + * Creates a new desk type + * @param session The user session + * @param createDeskTypeDto The desktype data for the post request + * @returns + */ export async function createDeskType( session: Session, createDeskTypeDto: CreateDeskTypeDto @@ -271,6 +306,12 @@ export async function createDeskType( return result; } +/** + * Creates a desk + * @param session The user session + * @param createDeskDto The desk data for the post request + * @returns + */ export async function createDesk( session: Session, createDeskDto: CreateDeskDto diff --git a/src/deskstar-frontend/pages/resources/index.tsx b/src/deskstar-frontend/pages/resources/index.tsx index 9a47761e..115c1bc4 100644 --- a/src/deskstar-frontend/pages/resources/index.tsx +++ b/src/deskstar-frontend/pages/resources/index.tsx @@ -81,11 +81,16 @@ const ResourceOverview = ({ setIsFetching(true); const promises = await Promise.all( selectedBuildings.map(async (building) => { - if (!session) { + if (!session) return []; + + let resFloors; + try { + resFloors = await getFloors(session, building.buildingId); + } catch (error) { + toast.error(`${error}`); return []; } - const resFloors = await getFloors(session, building.buildingId); const enrichedFloors = resFloors.map((floor) => { floor.buildingName = building.buildingName; floor.location = building.location; @@ -104,11 +109,16 @@ const ResourceOverview = ({ setIsFetching(true); const promises = await Promise.all( selectedFloors.map(async (floor) => { - if (!session) { + if (!session) return []; + + let resRooms; + try { + resRooms = await getRooms(session, floor.floorId); + } catch (error) { + toast.error(`${error}`); return []; } - const resRooms = await getRooms(session, floor.floorId); const enrichedRooms = resRooms.map((room) => { room.building = floor.buildingName; room.location = floor.location; @@ -131,19 +141,27 @@ const ResourceOverview = ({ return []; } - const resDeskType = await getDesks( - session, - room.roomId, - new Date().getTime(), - new Date().getTime() - ); + let resDeskType; + try { + resDeskType = await getDesks( + session, + room.roomId, + new Date().getTime(), + new Date().getTime() + ); + } catch (error) { + toast.error(`${error}`); + return []; + } return resDeskType; }) ); const desks = promises.flat(); - const filteredDesks = desks.filter((desk) => desk.bookings.length === 0); + const filteredDesks: IDesk[] = desks.filter( + (desk) => desk.bookings.length === 0 + ); setDesks(filteredDesks); stopFetchingAnimation(); } @@ -354,7 +372,15 @@ export const getServerSideProps: GetServerSideProps = async (context) => { authOptions ); - if (session) { + if (!session) + return { + redirect: { + destination: "/login", + permanent: false, + }, + }; + + try { const buildings = await getBuildings(session); const deskTypes = await getDeskTypes(session); return { @@ -363,14 +389,15 @@ export const getServerSideProps: GetServerSideProps = async (context) => { deskTypes, }, }; + } catch (error) { + console.error(error); + return { + redirect: { + destination: "/500", + permanent: false, + }, + }; } - - return { - props: { - buildings: [], - deskTypes: [], - }, - }; }; export default ResourceOverview;