diff --git a/.env b/.env
new file mode 100644
index 0000000..6c40f26
--- /dev/null
+++ b/.env
@@ -0,0 +1 @@
+VITE_API_BASE_URL=https://hsytrailerapi.azurewebsites.net/api/
diff --git a/package-lock.json b/package-lock.json
index 3bc2a61..af956f7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,6 +11,7 @@
"axios": "^1.6.2",
"bootstrap": "^5.3.2",
"bootstrap-icons": "^1.11.1",
+ "dotenv": "^16.3.1",
"geolib": "^3.3.4",
"i18next": "^23.7.6",
"i18next-browser-languagedetector": "^7.2.0",
@@ -29,7 +30,8 @@
"react-i18next": "^13.5.0",
"react-router-dom": "^6.16.0",
"react-simple-star-rating": "^5.1.7",
- "react-toastify": "^9.1.3"
+ "react-toastify": "^9.1.3",
+ "uuid": "^9.0.1"
},
"devDependencies": {
"@types/react": "^18.2.15",
@@ -1324,6 +1326,17 @@
"csstype": "^3.0.2"
}
},
+ "node_modules/dotenv": {
+ "version": "16.3.1",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz",
+ "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/motdotla/dotenv?sponsor=1"
+ }
+ },
"node_modules/electron-to-chromium": {
"version": "1.4.567",
"dev": true,
@@ -4341,6 +4354,18 @@
"punycode": "^2.1.0"
}
},
+ "node_modules/uuid": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
+ "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
+ "funding": [
+ "https://github.com/sponsors/broofa",
+ "https://github.com/sponsors/ctavan"
+ ],
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
"node_modules/vite": {
"version": "4.5.0",
"dev": true,
diff --git a/package.json b/package.json
index ccb8c41..6b3705a 100644
--- a/package.json
+++ b/package.json
@@ -17,6 +17,7 @@
"axios": "^1.6.2",
"bootstrap": "^5.3.2",
"bootstrap-icons": "^1.11.1",
+ "dotenv": "^16.3.1",
"geolib": "^3.3.4",
"i18next": "^23.7.6",
"i18next-browser-languagedetector": "^7.2.0",
@@ -35,7 +36,8 @@
"react-i18next": "^13.5.0",
"react-router-dom": "^6.16.0",
"react-simple-star-rating": "^5.1.7",
- "react-toastify": "^9.1.3"
+ "react-toastify": "^9.1.3",
+ "uuid": "^9.0.1"
},
"devDependencies": {
"@types/react": "^18.2.15",
diff --git a/src/App.jsx b/src/App.jsx
index d2c3462..992f13b 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -13,6 +13,7 @@ import { StepperProvider } from './context/StepperContext';
import { useTranslation } from 'react-i18next';
import { useState } from 'react';
+
function App() {
const [itemReturned, setItemReturned] = useState(false);
diff --git a/src/components/BankButton.jsx b/src/components/BankButton.jsx
index a112a9b..62bfdd0 100644
--- a/src/components/BankButton.jsx
+++ b/src/components/BankButton.jsx
@@ -4,18 +4,41 @@ import { useState } from 'react';
import PopUpWarningModal from '../components/PopUpWarningModal';
import { useNavigate } from 'react-router-dom';
import { Trans, useTranslation } from 'react-i18next';
-
-const BankButton = ({ logo, bankName, rentId }) => {
+import useApi from '../hooks/useApi';
+import { useStepper } from '../hooks/useStepper';
+const BankButton = ({ logo, bankName, randomUUID }) => {
const [showWarningModal, setShowWarningModal] = useState(false);
+
const navigate = useNavigate();
const { t } = useTranslation();
+ const { userData } = useStepper();
+ const { postRequest } = useApi();
- const handleClick = (bankName) => {
+ const handleClick = async (bankName) => {
if (bankName === 'HSY') {
handleOpenWarningModal();
} else {
- navigate(`/rent-successful/${rentId}`);
+ try {
+ const bodyData = {
+ customerInfo: {
+ name: userData.firstName,
+ lastName: userData.lastName,
+ phoneNumber: userData.phoneNumber,
+ email: userData.emailAddress,
+ address: userData.streetName,
+ zipCode: userData.postalCode,
+ city: userData.cityName,
+ },
+ uuid: randomUUID,
+ };
+
+ const responce = await postRequest('add-reservation', bodyData);
+ console.log('BankButton.jsx 36', responce.updatedReservation._id);
+ navigate(`/rent-successful/${responce.updatedReservation._id}`);
+ } catch (error) {
+ console.log(error);
+ }
}
};
@@ -68,7 +91,7 @@ const BankButton = ({ logo, bankName, rentId }) => {
BankButton.propTypes = {
logo: PropTypes.any.isRequired,
bankName: PropTypes.string,
- rentId: PropTypes.string.isRequired,
+ randomUUID: PropTypes.string.isRequired,
};
export default BankButton;
diff --git a/src/components/BankType.jsx b/src/components/BankType.jsx
index 68b56bd..32d082d 100644
--- a/src/components/BankType.jsx
+++ b/src/components/BankType.jsx
@@ -2,7 +2,14 @@ import PropTypes from 'prop-types';
import BankButton from './BankButton';
import styles from '../css/BankButton.module.css';
-const BankType = ({ gridName, title, arrayName, paymentName, rentId }) => {
+const BankType = ({
+ gridName,
+ title,
+ arrayName,
+ paymentName,
+ rentId,
+ randomUUID,
+}) => {
return (
{title}
@@ -14,6 +21,7 @@ const BankType = ({ gridName, title, arrayName, paymentName, rentId }) => {
bankName={item.bankName}
key={item.bankName}
rentId={rentId}
+ randomUUID={randomUUID}
>
))}
@@ -28,5 +36,6 @@ BankType.propTypes = {
arrayName: PropTypes.array,
paymentName: PropTypes.string,
rentId: PropTypes.string.isRequired,
+ randomUUID: PropTypes.string.isRequired,
};
export default BankType;
diff --git a/src/components/PopUpWarningModal.jsx b/src/components/PopUpWarningModal.jsx
index 134d025..9736a51 100644
--- a/src/components/PopUpWarningModal.jsx
+++ b/src/components/PopUpWarningModal.jsx
@@ -40,7 +40,7 @@ PopUpWarningModal.propTypes = {
show: PropTypes.bool,
onHide: PropTypes.func,
title: PropTypes.string,
- body: PropTypes.obj,
+ body: PropTypes.string,
backButton: PropTypes.string,
acceptButton: PropTypes.string,
acceptButtonVariant: PropTypes.string,
diff --git a/src/components/SelectTime.jsx b/src/components/SelectTime.jsx
index b3b8148..8313a46 100644
--- a/src/components/SelectTime.jsx
+++ b/src/components/SelectTime.jsx
@@ -7,6 +7,7 @@ function SelectTime({
setSelectedStationAndTime,
stationName,
timeSlots,
+ randomUUID,
}) {
const handleClick = (stationName, timeSlot) => {
setSelectedStationAndTime({
@@ -16,6 +17,7 @@ function SelectTime({
const itemElements = timeSlots.map((item, index) => (
{
+ const { deleteRequest } = useApi();
const [showWarningModal, setShowWarningModal] = useState(false);
const { t } = useTranslation();
@@ -31,20 +36,8 @@ const ProductAndTime = ({
selectedProduct,
} = useStepper();
- // values will be used in future
- const currentDate = new Date();
- const currentMonth = currentDate.getMonth();
- const currentYear = currentDate.getFullYear();
- const currentDay = currentDate.getDate();
- const futureDates = [];
-
const navigate = useNavigate();
- for (let i = 1; i < 4; i++) {
- const randomDate = new Date(currentYear, currentMonth, currentDay + i);
- futureDates.push(randomDate);
- }
-
// handling the button click for selecting a time and date
const handleSubmit = () => {
if (
@@ -66,6 +59,29 @@ const ProductAndTime = ({
setShowWarningModal(true);
};
+ // TODO add before unload delete temp reservation with uuid
+ useEffect(() => {
+ const sendDeleteRequestOnUnload = async () => {
+ window.addEventListener('beforeunload', async () => {
+ try {
+ const responce = await deleteRequest(
+ 'delete-temp-reservation/',
+ randomUUID,
+ );
+ console.log('TimeForm.jsx 72 ', responce);
+ } catch (error) {
+ console.error('Error sending DELETE request:', error);
+ }
+ });
+ };
+
+ sendDeleteRequestOnUnload();
+
+ // Cleanup the event listener when the component unmounts
+ return () => {
+ window.removeEventListener('beforeunload', sendDeleteRequestOnUnload);
+ };
+ }, []);
return (
<>
{
+ const { postRequest } = useApi();
+ const { selectedDate, selectedProduct, selectAdaptor } = useStepper();
+
+ const handleClick = async () => {
setSelectedTime(stationName, buttonText);
+ try {
+ const bodyData = {
+ uuid: randomUUID,
+ station: stationName,
+ timeSlot: buttonText,
+ product: selectedProduct,
+ date: selectedDate,
+ isAdapter: selectAdaptor,
+ };
+ const isEmptyField = Object.values(bodyData).some((value) => !value);
+ if (isEmptyField) {
+ const response = await postRequest('add-temp-reservation', bodyData);
+ console.log('TimePeriodButton.jsx 30 ', response);
+ }
+ } catch (error) {
+ console.error('api error: ', error);
+ }
};
return (
@@ -35,6 +58,7 @@ TimePeriodButton.propTypes = {
stationName: PropTypes.string.isRequired,
setSelectedTime: PropTypes.func.isRequired,
setSelectedStation: PropTypes.func,
+ randomUUID: PropTypes.string,
};
export default TimePeriodButton;
diff --git a/src/components/UserForm.jsx b/src/components/UserForm.jsx
index c73aba1..170185d 100644
--- a/src/components/UserForm.jsx
+++ b/src/components/UserForm.jsx
@@ -13,7 +13,6 @@ import PopUpWarningModal from '../components/PopUpWarningModal';
import hsyLogo from '../assets/hsy_logo_dark.png';
import { useStepper } from '../hooks/useStepper';
import { useTranslation } from 'react-i18next';
-
const UserForm = ({ onSubmit, onPrevStep }) => {
const [validated, setValidated] = useState(false);
const [showInfoModal, setShowInfoModal] = useState(false);
diff --git a/src/hooks/useApi.js b/src/hooks/useApi.js
index 2e292f7..585d124 100644
--- a/src/hooks/useApi.js
+++ b/src/hooks/useApi.js
@@ -1,7 +1,8 @@
import API from '../utils/axios';
import { useState } from 'react';
import { errorHandling } from '../utils/errorHandling';
-
+import { postRequest, deleteRequest } from '../services/ApiServices';
+import { useNavigate } from 'react-router-dom';
const useApi = () => {
const [error, setError] = useState(null);
@@ -29,7 +30,44 @@ const useApi = () => {
return await errorHandling(deleteRent, (err) => setError(err));
};
- return { getRentById, updateRent, deleteRent, error };
+ const navigate = useNavigate();
+
+ const handleApiError = (error) => {
+ console.error('API Error:', error);
+ navigate('/error');
+ };
+
+ const handleApiSuccess = (response) => {
+ console.log('API Response:', response);
+ };
+
+ const postApiRequest = async (endpoint, data) => {
+ try {
+ const response = await postRequest(endpoint, data);
+ handleApiSuccess(response);
+ return response;
+ } catch (error) {
+ handleApiError(error);
+ }
+ };
+ const deleteApiRequest = async (endpoint, uuid) => {
+ try {
+ const response = await deleteRequest(endpoint, uuid);
+ handleApiSuccess(response);
+ return response;
+ } catch (error) {
+ handleApiError(error);
+ }
+ };
+
+ return {
+ postRequest: postApiRequest,
+ deleteRequest: deleteApiRequest,
+ getRentById,
+ updateRent,
+ deleteRent,
+ error,
+ };
};
-export default useApi;
+export default useApi;
\ No newline at end of file
diff --git a/src/pages/RentProcess.jsx b/src/pages/RentProcess.jsx
index 78357af..8cd0a9f 100644
--- a/src/pages/RentProcess.jsx
+++ b/src/pages/RentProcess.jsx
@@ -25,23 +25,25 @@ import styles from '../css/BankButton.module.css';
import BankType from '../components/BankType';
import PopUpWarningModal from '../components/PopUpWarningModal';
import { useTranslation } from 'react-i18next';
-
+import { v4 as uuidv4 } from 'uuid';
const RentProcessPage = () => {
const countdownDuration = 20 * 60 * 1000;
const [activeStep, setActiveStep] = useState(0);
const [isMobile, setIsMobile] = useState(window.innerWidth < 820);
const [showWarningModal, setShowWarningModal] = useState(false);
+ const [randomUUID, setRandomUUID] = useState('');
+ useEffect(() => {
+ setRandomUUID(uuidv4());
+ }, []);
+ const [reservationDeadline, setReservationDeadline] = useState(
+ calculateReservationDeadline(),
+ );
- // TODO: get the id from the response when the user has made a reservation
const mockRentData = {
id: '656e0884162df1917d30e826',
};
- const [reservationDeadline, setReservationDeadline] = useState(
- calculateReservationDeadline(),
- );
-
const { t } = useTranslation();
function calculateReservationDeadline() {
@@ -143,6 +145,7 @@ const RentProcessPage = () => {
title={t('Mobiilimaksutavat')}
arrayName={mobileBanks}
paymentName={styles.mobilePayment}
+ randomUUID={randomUUID}
/>
{
title={t('Korttimaksutavat')}
arrayName={cardPayments}
paymentName={styles.cardPayment}
+ randomUUID={randomUUID}
/>
{
title={t('Pankkimaksutavat')}
arrayName={bankPayments}
paymentName={styles.bankPayment}
+ randomUUID={randomUUID}
/>
{
title={t('Maksu paikan päällä')}
arrayName={irlPayments}
paymentName={styles.irlPayment}
+ randomUUID={randomUUID}
/>
);
@@ -185,6 +191,7 @@ const RentProcessPage = () => {
handleWarningModal={handleWarningModal}
onProductAndTimeSelected={handleProductAndTimeSelected}
onPrevStep={handlePrevStep}
+ randomUUID={randomUUID}
/>
);
case 2:
diff --git a/src/services/ApiServices.js b/src/services/ApiServices.js
new file mode 100644
index 0000000..927c865
--- /dev/null
+++ b/src/services/ApiServices.js
@@ -0,0 +1,31 @@
+const API_BASE_URL = 'https://hsytrailerapi.azurewebsites.net/api/';
+
+export const postRequest = async (endpoint, data) => {
+ const response = await fetch(`${API_BASE_URL}${endpoint}`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(data),
+ });
+
+ if (!response.ok) {
+ throw new Error(`HTTP error! Status: ${response.status}`);
+ }
+
+ return response.json();
+};
+export const deleteRequest = async (endpoint, uuid) => {
+ const response = await fetch(`${API_BASE_URL}${endpoint}${uuid}`, {
+ method: 'DELETE',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ });
+
+ if (!response.ok) {
+ throw new Error(`HTTP error! Status: ${response.status}`);
+ }
+
+ return response.json();
+};
diff --git a/src/utils/axios.js b/src/utils/axios.js
index eb25be9..d4dd889 100644
--- a/src/utils/axios.js
+++ b/src/utils/axios.js
@@ -1,10 +1,6 @@
import axios from 'axios';
-import { NODE_ENV, PUBLIC_DOMAIN, PUBLIC_PORT } from './constants';
-const baseURL =
- NODE_ENV === 'production'
- ? 'https://hsytrailerapi.azurewebsites.net/api/'
- : `http://${PUBLIC_DOMAIN ?? 'localhost'}:${PUBLIC_PORT ?? '3000'}/api/`;
+const baseURL = 'https://hsytrailerapi.azurewebsites.net/api/';
const axiosInstance = axios.create({
baseURL: baseURL,
diff --git a/vite.config.js b/vite.config.js
index 89e04ca..3e0094e 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -5,5 +5,14 @@ import react from '@vitejs/plugin-react'
export default defineConfig({
base: '/', // gh pages base config
plugins: [react()],
+ server: {
+ proxy: {
+ '/api': {
+ target: 'http://localhost:5173', // Point to your Vite development server
+ changeOrigin: true,
+ rewrite: (path) => path.replace(/^\/api/, ''),
+ },
+ },
+ },
});