-
Signup page
-
+
+
Sign up
+
Hello there! Register and start managing your application
+
+
+
+
Have an existing account ?
+
+ Log In
+
);
diff --git a/src/pages/Auth/SignUp/SignupForm.js b/src/pages/Auth/SignUp/SignupForm.js
index 53308a4..596d165 100644
--- a/src/pages/Auth/SignUp/SignupForm.js
+++ b/src/pages/Auth/SignUp/SignupForm.js
@@ -1,19 +1,20 @@
import React from 'react';
import { useDispatch } from 'react-redux';
+import { useNavigate } from 'react-router-dom';
import FormComponent from '../../../components/Form/FormComponent';
import { SignUpSchema, signUpInitialValues } from '../../../models/signup.model';
import FormSubmitButton from '../../../components/Button/FormSubmitButton';
import { TextInputField } from '../../../components/Form/FormField';
-// import { useEffect } from 'react';
-import { userSignUp } from '../../../redux/thunk';
import HideableTextFormField from '../../../components/Form/HideableTextFormField';
+import { registerUser } from '../../../redux/thunk';
const SignUpForm = () => {
const dispatch = useDispatch();
-
+ const navigate = useNavigate();
const handleSubmit = (values) => {
console.log(values);
- dispatch(userSignUp(values));
+ dispatch(registerUser(values));
+ navigate('/');
};
return (
@@ -23,10 +24,10 @@ const SignUpForm = () => {
onSubmit={handleSubmit}
className="sign-up-form"
>
-
+
-
+
Sign Up
diff --git a/src/pages/LandingPage/Home/Home.js b/src/pages/LandingPage/Home/Home.js
index b9b633b..c66aa39 100644
--- a/src/pages/LandingPage/Home/Home.js
+++ b/src/pages/LandingPage/Home/Home.js
@@ -10,7 +10,7 @@ const Home = () => (
-
THE NEW VESPA TRIDENT
+
CarBooky
diff --git a/src/pages/LandingPage/MyReservation/MyReservation.js b/src/pages/LandingPage/MyReservation/MyReservation.js
index f05aff7..cdfd767 100644
--- a/src/pages/LandingPage/MyReservation/MyReservation.js
+++ b/src/pages/LandingPage/MyReservation/MyReservation.js
@@ -1,5 +1,11 @@
import React from 'react';
+import MyReservationsList from '../../../components/MyReservations/MyReservationsList';
-const MyReservations = () =>
My Reservations
;
+const MyReservations = () => (
+
+
My Current Reservations
+
+
+);
export default MyReservations;
diff --git a/src/pages/LandingPage/ReserveCars/ReserveCars.js b/src/pages/LandingPage/ReserveCars/ReserveCars.js
new file mode 100644
index 0000000..c980859
--- /dev/null
+++ b/src/pages/LandingPage/ReserveCars/ReserveCars.js
@@ -0,0 +1,11 @@
+import React from 'react';
+import ReserveCar from '../../../components/ReserveCars/ReserveCar';
+
+const ReserveCars = () => (
+
+);
+
+export default ReserveCars;
diff --git a/src/pages/LandingPage/ReservedCars/MyReservation.js b/src/pages/LandingPage/ReservedCars/MyReservation.js
deleted file mode 100644
index 1781a90..0000000
--- a/src/pages/LandingPage/ReservedCars/MyReservation.js
+++ /dev/null
@@ -1,5 +0,0 @@
-import React from 'react';
-
-const Reserved = () =>
My Reservations
;
-
-export default Reserved;
diff --git a/src/pages/UserDashboard/DashboardHome.js b/src/pages/UserDashboard/DashboardHome.js
index a86c37b..e781ef9 100644
--- a/src/pages/UserDashboard/DashboardHome.js
+++ b/src/pages/UserDashboard/DashboardHome.js
@@ -46,7 +46,7 @@ const DashboardHome = () => {
LATEST MODELS
- Please select a Vespa Model
+ Please select a Vehicle
diff --git a/src/redux/authentication/authenticationSlice.js b/src/redux/authentication/authenticationSlice.js
index 24364f0..6209695 100644
--- a/src/redux/authentication/authenticationSlice.js
+++ b/src/redux/authentication/authenticationSlice.js
@@ -1,7 +1,5 @@
import { createSlice } from '@reduxjs/toolkit';
-import {
- userSignUp, userLogin, userLogout, fetchUser,
-} from '../thunk';
+import { loginUser, logoutUser, registerUser } from '../thunk';
const initialState = {
authenticatedUser: {},
@@ -12,49 +10,48 @@ const initialState = {
const authenticationSlice = createSlice({
name: 'authenticatedUser',
initialState,
+ reducers: {},
extraReducers: (builder) => {
builder
- .addCase(userSignUp.pending, (state) => {
+ .addCase(registerUser.pending, (state) => {
state.status = 'loading';
})
- .addCase(userSignUp.fulfilled, (state, action) => {
+ .addCase(registerUser.fulfilled, (state, action) => {
state.authenticatedUser = action.payload.data;
- state.status = action.payload.status === 200 ? 'succeeded' : 'failed';
+ state.status = action.payload.status === 'succeeded' ? 'succeeded' : 'failed';
})
- .addCase(userSignUp.rejected, (state, action) => {
+ .addCase(registerUser.rejected, (state, action) => {
state.status = 'failed';
state.error = action.error.message;
})
- .addCase(userLogin.pending, (state) => {
+ .addCase(loginUser.pending, (state) => {
state.status = 'loading';
- })
- .addCase(userLogin.fulfilled, (state, action) => {
- state.authenticatedUser = action.payload.data;
- state.status = action.payload.status === 200 ? 'succeeded' : 'failed';
- })
- .addCase(userLogin.rejected, (state, action) => {
+ console.log('Action staus: blahh is', state.status);
+ })
+ .addCase(loginUser.fulfilled, (state, action) => {
+ if (action.payload.status === 'failed') {
+ state.status = 'failed';
+ state.error = action.payload.error;
+ } else {
+ state.authenticatedUser = action.payload.user;
+ state.status = action.payload.status || 'succeeded';
+ console.log('Action paylod to check if there is a status:', action.payload);
+ console.log('Authenticated user is: ', state.authenticatedUser);
+ console.log('Authenticated user action status: ', state.status);
+ }
+ })
+ .addCase(loginUser.rejected, (state, action) => {
state.status = 'failed';
state.error = action.error.message;
})
- .addCase(userLogout.pending, (state) => {
+ .addCase(logoutUser.pending, (state) => {
state.status = 'loading';
})
- .addCase(userLogout.fulfilled, (state, action) => {
+ .addCase(logoutUser.fulfilled, (state, action) => {
state.authenticatedUser = {};
state.status = action.payload.status;
})
- .addCase(userLogout.rejected, (state, action) => {
- state.status = 'failed';
- state.error = action.error.message;
- })
- .addCase(fetchUser.pending, (state) => {
- state.status = 'loading';
- })
- .addCase(fetchUser.fulfilled, (state, action) => {
- state.authenticatedUser = action.payload.data;
- state.status = 'succeeded';
- })
- .addCase(fetchUser.rejected, (state, action) => {
+ .addCase(logoutUser.rejected, (state, action) => {
state.status = 'failed';
state.error = action.error.message;
});
diff --git a/src/redux/reservations/reservationSlice.js b/src/redux/reservations/reservationSlice.js
new file mode 100644
index 0000000..0422aad
--- /dev/null
+++ b/src/redux/reservations/reservationSlice.js
@@ -0,0 +1,44 @@
+import { createSlice } from '@reduxjs/toolkit';
+import { fetchCarReservations, postReserveCar } from '../thunk';
+
+const initialState = {
+ reservation: {},
+ reservations: [],
+ status: 'idle',
+ error: null,
+};
+
+const reservationSlice = createSlice({
+ name: 'reservation',
+ initialState,
+ reducers: {},
+ extraReducers: (builder) => {
+ builder
+ .addCase(postReserveCar.pending, (state) => {
+ state.status = 'loading';
+ state.error = null;
+ })
+ .addCase(postReserveCar.fulfilled, (state, action) => {
+ state.status = 'succeeded';
+ state.reservation = action.payload.data;
+ })
+ .addCase(postReserveCar.rejected, (state, action) => {
+ state.status = 'failed';
+ state.error = action.error.message;
+ })
+ .addCase(fetchCarReservations.pending, (state) => {
+ state.status = 'loading';
+ state.error = null;
+ })
+ .addCase(fetchCarReservations.fulfilled, (state, action) => {
+ state.status = 'succeeded';
+ state.reservations = action.payload.data;
+ })
+ .addCase(fetchCarReservations.rejected, (state, action) => {
+ state.status = 'failed';
+ state.error = action.error.message;
+ });
+ },
+});
+
+export default reservationSlice.reducer;
diff --git a/src/redux/store.js b/src/redux/store.js
index 4e76c9c..1fa3e5f 100644
--- a/src/redux/store.js
+++ b/src/redux/store.js
@@ -1,10 +1,12 @@
import { configureStore } from '@reduxjs/toolkit';
import logger from 'redux-logger';
import authenticationReducer from './authentication/authenticationSlice';
+import reservationReducer from './reservations/reservationSlice';
const store = configureStore({
reducer: {
authencation: authenticationReducer,
+ reservation: reservationReducer,
},
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(logger),
});
diff --git a/src/redux/thunk.js b/src/redux/thunk.js
index 28f9dea..8572991 100644
--- a/src/redux/thunk.js
+++ b/src/redux/thunk.js
@@ -1,73 +1,127 @@
-import { createAsyncThunk } from '@reduxjs/toolkit';
import axios from 'axios';
+import { createAsyncThunk } from '@reduxjs/toolkit';
+
+const baseURL = 'http://localhost:4000/api';
-const baseURL = 'http://localhost:3000/api';
+const setAuthenticationToken = ({ headers }) => {
+ const authorizationHeader = headers.get('authorization');
-const setAuthenticationToken = ({ headers }) => localStorage.setItem('token', headers.get('Authorization'));
+ if (authorizationHeader) {
+ localStorage.setItem('token', authorizationHeader);
+ }
+};
+
+export const getAuthenticationToken = () => localStorage.getItem('token') || false;
const removeAuthenticationToken = () => localStorage.removeItem('token');
-export const userSignUp = createAsyncThunk(
- 'users/signup',
- async (user, thunkAPI) => {
- try {
- const response = await axios.post(`${baseURL}/users/sign_up`, user, {
- withCredentials: true,
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify(user),
- });
+const handleResponse = async (response) => {
+ const { status, data, headers } = response;
- setAuthenticationToken(response);
+ console.log('Response headers: ', headers);
- return response.data;
- } catch (error) {
- return thunkAPI.rejectWithValue(error);
- }
- },
-);
+ if (status === 200 || status === 201) {
+ return { data, status: 'succeeded' };
+ }
+
+ if (status === 401 || status === 500) {
+ removeAuthenticationToken();
+ return { status: 'expired', error: 'Unauthorized', message: 'Session has expired' };
+ }
-export const userLogin = createAsyncThunk(
- 'users/login',
+ return { status: 'failed', error: 'Request failed', message: data.message };
+};
+
+export const loginUser = createAsyncThunk(
+ 'auth/login',
async (user, thunkAPI) => {
try {
- const response = await axios.post(`${baseURL}/users/sign_in`, user);
+ const response = await axios.post(`${baseURL}/users/sign_in`, { user });
+ const { data, headers } = response;
+ console.log('the data from async method:');
+ console.log(data);
+ console.log('the headers:');
+ console.log(headers);
- setAuthenticationToken(response);
+ if (response.status === 200 || response.status === 201) {
+ setAuthenticationToken({ headers });
+ }
- return response.data;
+ return data;
} catch (error) {
return thunkAPI.rejectWithValue(error);
}
},
);
-export const userLogout = createAsyncThunk(
- 'users/logout',
- async (_, thunkAPI) => {
- try {
- const response = await axios.delete(`${baseURL}/logout`);
+export const registerUser = createAsyncThunk('auth/register', async (user, thunkAPI) => {
+ try {
+ const response = await axios.post(`${baseURL}/users`, { user });
+ const { data, headers } = response;
- removeAuthenticationToken();
+ if (response.status === 200 || response.status === 201) {
+ setAuthenticationToken({ headers });
+ }
- return response.data;
- } catch (error) {
- return thunkAPI.rejectWithValue(error);
+ const handledResponse = await handleResponse(response);
+ return { ...data, ...handledResponse };
+ } catch (error) {
+ return thunkAPI.rejectWithValue(error);
+ }
+});
+
+export const logoutUser = createAsyncThunk('auth/logout', async (_, thunkAPI) => {
+ try {
+ const response = await axios.delete(`${baseURL}/users`, {
+ headers: { Authorization: localStorage.getItem('token') },
+ });
+
+ const { status, message } = await handleResponse(response);
+
+ if (status === 'succeeded') {
+ removeAuthenticationToken();
}
- },
-);
-export const fetchUser = createAsyncThunk(
- 'users/fetchUser',
- async (_, thunkAPI) => {
- try {
- const response = await axios.get(`${baseURL}/users`);
+ return { status, message };
+ } catch (error) {
+ return thunkAPI.rejectWithValue(error);
+ }
+});
- setAuthenticationToken(response);
+export const postReserveCar = createAsyncThunk(
+ 'reservations/newReserve',
+ async ({ carId, reservationData }, thunkAPI) => {
+ try {
+ const token = localStorage.getItem('token');
+ const response = await axios.post(`${baseURL}/car/${carId}/new_reserve`, reservationData, {
+ headers: {
+ Authorization: token,
+ },
+ });
+ const { data } = await handleResponse(response);
- return response.data;
+ if (response.status === 200 || response.status === 201) {
+ return { data, status: 'succeeded' };
+ }
+ return { status: 'failed', error: 'Request failed', message: data.message };
} catch (error) {
return thunkAPI.rejectWithValue(error);
}
},
);
+
+export const fetchCarReservations = createAsyncThunk(
+ 'reservations/fetchCarReservations',
+ async (_, thunkAPI) => {
+ try {
+ const token = localStorage.getItem('token');
+ const response = await axios.get(`${baseURL}/my_reservations`, {
+ headers: {
+ Authorization: token,
+ },
+ });
+ return response.data;
+ } catch (error) {
+ return thunkAPI.rejectWithValue(error);
+ }
+ },
+);
diff --git a/src/routes/routeConstants.js b/src/routes/routeConstants.js
index 0fe389d..8e181ff 100644
--- a/src/routes/routeConstants.js
+++ b/src/routes/routeConstants.js
@@ -7,7 +7,7 @@ export const FORGOT_PASSWORD = 'forgot-password';
// Landing-page routes
export const HOME = '/';
export const MY_RESERVATIONS = 'my-reservations';
-export const RESERVED_CARS = 'reserved-cars';
+export const RESERVE_CARS = 'reserve-cars';
export const ADD_NEW_CAR = 'add-new-car';
export const DELETE_RESERVATION = 'delete-reservation';
export const CONTACT = 'contact'
diff --git a/src/routes/routes.js b/src/routes/routes.js
index 85cb409..fc5989e 100644
--- a/src/routes/routes.js
+++ b/src/routes/routes.js
@@ -7,7 +7,7 @@ import SignIn from '../pages/Auth/SignIn/SignIn';
import SignUp from '../pages/Auth/SignUp/SignUp';
import NotFound404 from '../pages/NotFound404';
import MyReservations from '../pages/LandingPage/MyReservation/MyReservation';
-import Reserved from '../pages/LandingPage/ReservedCars/MyReservation';
+import ReserveCars from '../pages/LandingPage/ReserveCars/ReserveCars';
import ItemDetail from '../pages/UserDashboard/ItemDetail/ItemDetail';
import Home from '../pages/LandingPage/Home/Home';
import {
@@ -17,7 +17,7 @@ import {
FORGOT_PASSWORD,
MY_RESERVATIONS,
HOME,
- RESERVED_CARS,
+ RESERVE_CARS,
USERDASHBOARDHOME,
USERS_DASHBOARD,
ADD_NEW_CAR,
@@ -49,7 +49,7 @@ export default function Router() {
{ path: USERS_DASHBOARD, element: },
{ path: USERDASHBOARDHOME, element: },
{ path: MY_RESERVATIONS, element: },
- { path: RESERVED_CARS, element: },
+ { path: RESERVE_CARS, element: },
{ path: ADD_NEW_CAR, element: },
{ path: ITEM_DETAIL, element: },
{ path: DELETE_RESERVATION, element: },
diff --git a/src/styles/App.css b/src/styles/App.css
index cca57b3..743ca5e 100644
--- a/src/styles/App.css
+++ b/src/styles/App.css
@@ -2,4 +2,67 @@
margin: 0;
padding: 0;
box-sizing: border-box;
+ font-family: 'Lato', sans-serif;
+}
+
+.signin-page-outer,
+.signup-page-outer,
+.my-reservations-page-outer,
+.reserve-cars-page-outer {
+ background: #ffd700;
+ width: 100%;
+ height: 100vh;
+ display: flex;
+ gap: 2rem;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+}
+
+.reserve-page-title {
+ text-align: center;
+}
+
+p.user-headline {
+ text-align: center;
+ width: 70%;
+}
+
+.reserve-car-outer {
+ background: url('../assets/yellow-background-image-2.png') center/cover;
+ height: 100vh;
+}
+
+.reserve-car-inner {
+ color: #000;
+ width: 100%;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+}
+
+#reserve-car-head-title {
+ font-size: 2rem;
+}
+
+#reserve-page-head-description {
+ width: 70%;
+ text-align: center;
+ padding: 2rem;
+}
+
+.city-submit-div {
+ display: flex;
+ gap: 2rem;
+ width: 100%;
+ min-height: 100px;
+ padding: 20px;
+}
+
+select.css-zdsokt {
+ border: none;
+ border-radius: 3rem;
+ min-width: 150px;
}