source: my-react-app/src/components/ReservationConfirmation.js@ e48199a

main
Last change on this file since e48199a was e48199a, checked in by Aleksandar Panovski <apano77@…>, 10 days ago

Final version for DB

  • Property mode set to 100644
File size: 9.6 KB
RevLine 
[d24f17c]1import React, { useState, useEffect } from 'react';
2import { useParams } from 'react-router-dom';
3import axios from 'axios';
4import { useNavigate } from 'react-router-dom';
[e15e8d9]5import { useLocation } from 'react-router-dom';
6import { jwtDecode } from "jwt-decode";
[f5b256e]7import {request} from "../axios_helper";
8import restaurants from "./Restaurants";
[d24f17c]9
10const ReservationConfirmation = () => {
11 const navigate = useNavigate();
12
[e15e8d9]13 const location = useLocation();
14 const preOrderedItems = location.state?.preOrderedItems || [];
[d24f17c]15 const [restaurant, setRestaurant] = useState({});
[f5b256e]16 const [user, setUser] = useState({});
[d24f17c]17 const [table, setTable] = useState({});
18 const [reservationDateTime, setReservationDateTime] = useState('');
19 const [partySize, setPartySize] = useState('');
20 const [specialRequests, setSpecialRequests] = useState('');
21 const { tableNumber, timeSlot, restaurantId } = useParams();
22
[f5b256e]23 const adjustedTimeSlot = new Date(new Date(timeSlot).getTime() + 60 * 60 * 1000).toISOString();
[d24f17c]24 useEffect(() => {
[f5b256e]25 const fetchDetails = async () => {
[d24f17c]26 try {
[8ca35dc]27 const tableResponse = await axios.get(`http://localhost:8081/api/tables/${tableNumber}`);
[d24f17c]28 setTable(tableResponse.data);
[8ca35dc]29 const restaurantResponse = await axios.get(`http://localhost:8081/api/restaurants/${restaurantId}`);
[d24f17c]30 setRestaurant(restaurantResponse.data);
[f5b256e]31
32 const token = localStorage.getItem("token");
33 if (!token) {
34 console.error("No token found");
35 return;
36 }
37 const decodedToken = jwtDecode(token);
38 const userId = decodedToken.iss;
39
40 const userResponse = await axios.get(`http://localhost:8081/api/user/${userId}`);
41 setUser(userResponse.data);
[d24f17c]42 } catch (error) {
[f5b256e]43 console.error('Error fetching table or restaurant details:', error);
[d24f17c]44 }
45 };
[f5b256e]46 fetchDetails();
[d24f17c]47 }, [tableNumber, restaurantId]);
48
49 const handleSubmit = async (e) => {
50 e.preventDefault();
[8ca35dc]51
[f5b256e]52 const payload = {
53 reservationID: 0,
54 userEmail: user.email,
55 rating: parseFloat(restaurant.rating) || null,
56 tableNumber: parseInt(table.id, 10),
[2518b3a]57 restaurantId: restaurant.restaurantId,
[f5b256e]58 reservationDateTime: adjustedTimeSlot,
59 partySize: parseInt(partySize, 10),
60 status: 'Reserved',
61 specialRequests: specialRequests.trim(),
62 paymentStatus: 'Pending',
[2518b3a]63 preOrderedItems: preOrderedItems.map(item => ({
[b67dfd3]64 preorderedItemName: item.itemName,
[2518b3a]65 quantity: item.quantity,
[e48199a]66 price: item.price,
67 menuID: item.menuID
[2518b3a]68 }))
[f5b256e]69 };
[d24f17c]70
[8ca35dc]71
[f5b256e]72 try {
[2518b3a]73 const response = await axios.post('http://localhost:8081/api/reservations', payload, {
74 headers: {
75 'Content-Type': 'application/json'
76 }
77 });
[f5b256e]78 navigate("/reservations")
[d24f17c]79 } catch (error) {
[f5b256e]80 if (error.response) {
81 alert('The selected time slot is no longer available. Please choose another time.');
82 } else {
83 alert('Network error. Please check your internet connection.');
84 }
[d24f17c]85 }
86 };
87
[f5b256e]88 const calculateCheckOutTime = (checkInTime) => {
89 const checkIn = new Date(checkInTime);
90 checkIn.setHours(checkIn.getHours() + 2);
91 return checkIn.toISOString();
92 };
93
[d24f17c]94 const initialRemainingTime = localStorage.getItem('remainingTime') || 300;
[f5b256e]95 const [remainingTime, setRemainingTime] = useState(parseInt(initialRemainingTime, 10));
[d24f17c]96
97 useEffect(() => {
98 const timer = setInterval(() => {
99 setRemainingTime((prevTime) => {
100 const newTime = prevTime - 1;
[f5b256e]101 localStorage.setItem('remainingTime', newTime.toString());
[d24f17c]102 return newTime;
103 });
104 }, 1000);
105
106 return () => clearInterval(timer);
107 }, []);
108
109 useEffect(() => {
110 if (remainingTime <= 0) {
111 localStorage.removeItem('remainingTime');
[f5b256e]112 alert("Time has expired. Please try reserving again.");
[e48199a]113 navigate('/restaurants');
[d24f17c]114 }
[f5b256e]115 }, [remainingTime, navigate]);
[d24f17c]116
117 const formatTime = (timeInSeconds) => {
118 const minutes = Math.floor(timeInSeconds / 60);
119 const seconds = timeInSeconds % 60;
120 return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
121 };
122
[f5b256e]123 const formatTimeSlot = (timeSlot) => {
124 const utcDate = new Date(timeSlot);
125 const localDate = new Date(utcDate.toLocaleString("en-US", { timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone }));
126 const formattedDate = localDate.toLocaleDateString();
127 const formattedTime = localDate.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
128 return `${formattedDate} - ${formattedTime}`;
129 };
130
[e15e8d9]131 const grandTotal = preOrderedItems.reduce((acc, item) => acc + item.price * item.quantity, 0).toFixed(2);
132 const itemQuantityString = preOrderedItems
133 .map(item => `${item.itemName}:${item.quantity}`)
134 .join(',');
135
[d24f17c]136 return (
137 <div className="container mt-5">
138 <div className="row justify-content-center">
139 <div className="col-md-6">
140 <div className="card">
141 <div className="card-header">
142 <h3 className="text-center">Reservation Confirmation</h3>
143 <p>Remaining Time: {formatTime(remainingTime)}</p>
144 </div>
145 <form onSubmit={handleSubmit}>
146 <div className="card-body">
147 <h5 className="card-title">Reservation Details</h5>
148 <p className="card-text">
[f5b256e]149 <strong>Restaurant:</strong> {restaurant.name || 'Loading...'} <br />
150 <strong>Cuisine type:</strong> {restaurant.cuisineType || 'Loading...'} <br />
151 <strong>Selected Time Slot:</strong> {formatTimeSlot(timeSlot)} <br />
152 <strong>Party size:</strong>{' '}
153 <input
154 type="number"
155 max={table.capacity}
156 value={partySize}
157 onChange={(e) => setPartySize(e.target.value)}
158 />
159 <strong>Table size:</strong> {table.capacity} <br />
160 <strong>Special Requests:</strong>{' '}
161 <input
162 type="text"
163 value={specialRequests}
164 onChange={(e) => setSpecialRequests(e.target.value)}
165 />
166 <br />
[d24f17c]167 </p>
168 <p className="card-text text-success">
[f5b256e]169 <strong>
170 Check-in Time: Grace period of 15 minutes +/- the slot. For more information, call the restaurant.
171 </strong>
172 <br />
[d24f17c]173 </p>
[e15e8d9]174 {preOrderedItems.length > 0 ? (
175 <div className="row">
176 {preOrderedItems.map((item) => (
177 <div key={item.menuID} className="col-md-4 mb-4">
178 <div className="list-group shadow-sm p-3">
179 <p className="item"><strong>Item:</strong> {item.itemName}</p>
180 <p className="item"><strong>Price:</strong> ${item.price.toFixed(2)}</p>
181 <p className="item"><strong>Quantity:</strong> {item.quantity}</p>
182 <p className="item"><strong>Total:</strong> ${(item.price * item.quantity).toFixed(2)}</p>
183 </div>
184 </div>
185 ))}
186
187 <div className="col-12 mt-4">
188 <div className="list-group shadow-sm p-4 text-center">
189 <h4>Grand Total: ${grandTotal}</h4>
190 </div>
191 </div>
192 </div>
193 ) : (
194 <p>No pre-ordered items.</p>
195 )}
[d24f17c]196 </div>
197 <div className="card-footer">
198 <button type="submit" className="btn btn-primary">Submit</button>
[f5b256e]199 <a href="/restaurants" className="btn btn-secondary mx-2">Back to Restaurants</a>
200 <a href="/" className="btn btn-secondary">Back to Home</a>
[d24f17c]201 </div>
202 </form>
203 </div>
204 </div>
205 </div>
206 </div>
207 );
208};
209
[f5b256e]210export default ReservationConfirmation;
Note: See TracBrowser for help on using the repository browser.