[cfc16a3] | 1 | import React, {useState, useEffect, useContext} from 'react';
|
---|
[d24f17c] | 2 | import 'bootstrap/dist/css/bootstrap.min.css';
|
---|
| 3 | import StarRating from './StarRating';
|
---|
| 4 | import { useNavigate } from 'react-router-dom';
|
---|
[cfc16a3] | 5 | import {RestaurantContext} from "./RestaurantContext";
|
---|
[f5b256e] | 6 | import {Alert} from "bootstrap";
|
---|
| 7 |
|
---|
| 8 | const parseTime = (timeString) => {
|
---|
| 9 | const [hours, minutes] = timeString.trim().split(':').map(Number);
|
---|
| 10 | return new Date().setHours(hours, minutes, 0, 0);
|
---|
| 11 | };
|
---|
| 12 |
|
---|
| 13 | const roundToNextQuarter = (date) => {
|
---|
| 14 | const minutes = date.getMinutes();
|
---|
| 15 | const roundedMinutes = Math.floor(minutes / 15) * 15;
|
---|
| 16 | date.setMinutes(roundedMinutes, 0, 0);
|
---|
| 17 | return date;
|
---|
| 18 | };
|
---|
| 19 |
|
---|
| 20 | const shouldMoveToNextDay = (currentTime, endTime) => {
|
---|
| 21 | return (endTime - currentTime) <= 2 * 60 * 60 * 1000;
|
---|
| 22 | };
|
---|
[d24f17c] | 23 |
|
---|
| 24 | const Restaurants = () => {
|
---|
| 25 | const [restaurants, setRestaurants] = useState([]);
|
---|
| 26 | const navigate = useNavigate();
|
---|
[cfc16a3] | 27 | const restaurantContext = useContext(RestaurantContext);
|
---|
[d24f17c] | 28 |
|
---|
| 29 | useEffect(() => {
|
---|
[cfc16a3] | 30 | setRestaurants(restaurantContext.restaurants);
|
---|
| 31 | }, [restaurantContext]);
|
---|
[d24f17c] | 32 |
|
---|
[f5b256e] | 33 |
|
---|
| 34 | const generateTimeSlots = (operatingHours) => {
|
---|
| 35 | const timeSlots = [];
|
---|
| 36 | const [startTimeStr, endTimeStr] = operatingHours.split('-').map((time) => time.trim());
|
---|
| 37 |
|
---|
| 38 | const startTime = parseTime(startTimeStr);
|
---|
| 39 | let endTime = parseTime(endTimeStr);
|
---|
| 40 |
|
---|
| 41 | const currentTime = new Date().getTime();
|
---|
| 42 | if (shouldMoveToNextDay(currentTime, endTime)) {
|
---|
| 43 | endTime += 24 * 60 * 60 * 1000;
|
---|
| 44 | }
|
---|
| 45 |
|
---|
| 46 | let currentTimeSlot = new Date(startTime);
|
---|
| 47 | currentTimeSlot = roundToNextQuarter(currentTimeSlot);
|
---|
| 48 |
|
---|
| 49 | while (currentTimeSlot.getTime() < endTime) {
|
---|
| 50 | timeSlots.push(currentTimeSlot.toISOString());
|
---|
| 51 | currentTimeSlot.setMinutes(currentTimeSlot.getMinutes() + 15);
|
---|
| 52 | }
|
---|
| 53 |
|
---|
| 54 | return timeSlots;
|
---|
| 55 | };
|
---|
| 56 |
|
---|
[d24f17c] | 57 | const handleDetailClick = (restaurantId) => {
|
---|
| 58 | navigate(`/restaurants/${restaurantId}`);
|
---|
[f5b256e] | 59 | };
|
---|
[d24f17c] | 60 |
|
---|
| 61 | const handleTimeSlotClick = (table, timeSlot, restaurant) => {
|
---|
| 62 | const tableNumber = table.id;
|
---|
| 63 | const formattedTimeSlot = timeSlot;
|
---|
| 64 | const restaurantId = restaurant.restaurantId;
|
---|
| 65 |
|
---|
[f5b256e] | 66 | const encodedTableNumber = encodeURIComponent(tableNumber);
|
---|
[d24f17c] | 67 | const encodedTimeSlot = encodeURIComponent(formattedTimeSlot);
|
---|
| 68 | const encodedRestaurantId = encodeURIComponent(restaurantId);
|
---|
| 69 |
|
---|
| 70 | navigate(`/reservationConfirmation/${encodedTableNumber}/${encodedTimeSlot}/${encodedRestaurantId}`);
|
---|
[f5b256e] | 71 | };
|
---|
[d24f17c] | 72 |
|
---|
| 73 | const renderTimeSlots = (tablesList, restaurant) => {
|
---|
| 74 | const currentTime = new Date().getTime();
|
---|
[f5b256e] | 75 | let renderedTimeSlots = {};
|
---|
| 76 |
|
---|
| 77 | if (tablesList.length === 0) {
|
---|
| 78 | return <p>No tables available for reservations at this restaurant.</p>;
|
---|
| 79 | }
|
---|
| 80 |
|
---|
| 81 | return tablesList.flatMap((table) => {
|
---|
| 82 | const tableTimeSlots = generateTimeSlots(restaurant.operatingHours);
|
---|
[d24f17c] | 83 |
|
---|
| 84 | if (!renderedTimeSlots[table.capacity]) {
|
---|
| 85 | renderedTimeSlots[table.capacity] = 0;
|
---|
| 86 | return (
|
---|
| 87 | <div key={table.capacity}>
|
---|
[f5b256e] | 88 | <h3>Table for {table.capacity} guests</h3>
|
---|
| 89 | {tableTimeSlots.map((timeSlot, index) => {
|
---|
[d24f17c] | 90 | const timeSlotTime = new Date(timeSlot).getTime();
|
---|
| 91 |
|
---|
[f5b256e] | 92 | if (timeSlotTime > currentTime && renderedTimeSlots[table.capacity] < 3) {
|
---|
| 93 | renderedTimeSlots[table.capacity]++;
|
---|
[d24f17c] | 94 | const timeSlotDateTime = new Date(timeSlot);
|
---|
[f5b256e] | 95 | const formattedTime = timeSlotDateTime.toLocaleTimeString([], {
|
---|
| 96 | hour: '2-digit',
|
---|
| 97 | minute: '2-digit'
|
---|
| 98 | });
|
---|
[d24f17c] | 99 |
|
---|
| 100 | return (
|
---|
[f5b256e] | 101 | <button
|
---|
| 102 | key={index}
|
---|
| 103 | className="btn btn-primary me-2 mb-2"
|
---|
| 104 | onClick={() => handleTimeSlotClick(table, timeSlot, restaurant)}
|
---|
| 105 | >
|
---|
| 106 | {formattedTime} {}
|
---|
[d24f17c] | 107 | </button>
|
---|
| 108 | );
|
---|
| 109 | } else {
|
---|
[f5b256e] | 110 | return null;
|
---|
[d24f17c] | 111 | }
|
---|
| 112 | })}
|
---|
| 113 | </div>
|
---|
| 114 | );
|
---|
| 115 | } else {
|
---|
| 116 | return null;
|
---|
| 117 | }
|
---|
| 118 | });
|
---|
[f5b256e] | 119 | };
|
---|
[d24f17c] | 120 |
|
---|
| 121 | return (
|
---|
| 122 | <div className="container">
|
---|
| 123 | <h2>Restaurants</h2>
|
---|
| 124 | <div className="row">
|
---|
| 125 | {restaurants.map((restaurant) => (
|
---|
[f5b256e] | 126 | <div key={restaurant.restaurantId} className="col-md-4 mb-4">
|
---|
[d24f17c] | 127 | <div className="card">
|
---|
| 128 | <div className="card-body">
|
---|
| 129 | <h5 className="card-title">
|
---|
[f5b256e] | 130 | {restaurant.name}
|
---|
[d24f17c] | 131 | </h5>
|
---|
| 132 | <p className="card-text">{restaurant.cuisineType}</p>
|
---|
| 133 | <p className="card-text">{restaurant.operatingHours}</p>
|
---|
| 134 | <p className="card-text">Ul. {restaurant.address}</p>
|
---|
[f5b256e] | 135 |
|
---|
| 136 | {restaurant.tablesList && restaurant.tablesList.length > 0 ? (
|
---|
| 137 | <div className="d-flex flex-wrap">
|
---|
| 138 | {renderTimeSlots(restaurant.tablesList, restaurant)}
|
---|
| 139 | </div>
|
---|
| 140 | ) : (
|
---|
| 141 | <p>No tables available for reservations at this restaurant.</p>
|
---|
| 142 | )}
|
---|
[d24f17c] | 143 | </div>
|
---|
[f5b256e] | 144 | <button onClick={() => handleDetailClick(restaurant.restaurantId)} className="btn btn-primary">
|
---|
| 145 | View Details
|
---|
[cfc16a3] | 146 | </button>
|
---|
[d24f17c] | 147 | </div>
|
---|
| 148 | </div>
|
---|
| 149 | ))}
|
---|
| 150 | </div>
|
---|
| 151 | </div>
|
---|
| 152 | );
|
---|
| 153 | };
|
---|
| 154 |
|
---|
[f5b256e] | 155 | export default Restaurants;
|
---|