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

main
Last change on this file since c44c5ed was 2518b3a, checked in by Aleksandar Panovski <apano77@…>, 2 weeks ago

Added menu tag

succesfull testing and implemnation

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