0% found this document useful (0 votes)
5 views32 pages

Scroll Issue

The document is a React component for a reservation system that includes various UI elements such as modals, buttons, and forms. It utilizes libraries like Stripe for payment processing and React Calendar for date selection, while managing state for user interactions and restaurant selections. Additionally, it features a footer with links to privacy policy and social media, and handles loading states for various operations.

Uploaded by

Hammad Nadir
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
5 views32 pages

Scroll Issue

The document is a React component for a reservation system that includes various UI elements such as modals, buttons, and forms. It utilizes libraries like Stripe for payment processing and React Calendar for date selection, while managing state for user interactions and restaurant selections. Additionally, it features a footer with links to privacy policy and social media, and handles loading states for various operations.

Uploaded by

Hammad Nadir
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 32

"use client"; // Ensure this line is at the top

import Link from "next/link";


import { useState, useEffect, useRef } from "react";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { useRouter } from "next/navigation";
import { Card, CardContent } from "@/components/ui/card";
import { Label } from "@/components/ui/label";
import Calendar from 'react-calendar';
import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from
"@/components/ui/select";
import { Collapsible, CollapsibleTrigger, CollapsibleContent } from
"@/components/ui/collapsible";
import Logo from "../assets/logo.png";
import { Pin, UserCircleIcon,EyeIcon, EyeOffIcon } from "lucide-react";
import { SmileIcon, MenuIcon } from "lucide-react";
import { CalendarIcon } from "@heroicons/react/outline"; // Added MapPinIcon import
import { Elements ,CardElement,useStripe, useElements} from '@stripe/react-stripe-
js';
import { MapPin } from "lucide-react";
import { loadStripe } from '@stripe/stripe-js';
import CheckoutForm from './checkoutForm';
import resyLogo from '../assets/logo2.png';
import { TwitterIcon, FacebookIcon, InstagramIcon } from "lucide-react";
import { ToastContainer, toast } from 'react-toastify';
// import { Modal } from "@/components/ui/modal";
import { XIcon } from "lucide-react";

import 'react-toastify/dist/ReactToastify.css';
import res1 from '../assets/res1.jpeg';
import res2 from '../assets/res2.jpeg';
import res3 from '../assets/res3.jpeg';
import res4 from '../assets/res4.jpeg';
import res5 from '../assets/res5.jpeg';
import res6 from '../assets/res6.jpeg';
import res7 from '../assets/res7.jpeg';
import "./style.css";
import axios from "axios";
import { LoaderOverlay } from './loader';
import { restList } from "../../data/restList";
const stripePromise =
loadStripe('pk_test_51PQGnaDSTeiybpPZu7tYvyIpPsAlQREBAATsf0cupUqKJhNIuzx1TDoiqEyKK6
B6dgDSt7nKiQu2S7JNxBn3FR8o00DRHLPKD0'); // Replace with your actual Stripe public
key

const images = [res1, res2, res3, res4, res5, res6, res7];


export const Modal2 = ({ children, showCloseIcon = true }) => {
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black
bg-opacity-50">
<div className="bg-white p-6 rounded-lg shadow-lg max-w-lg w-full relative">
{/* {showCloseIcon && (
// <button
// onClick={() => setLoginModalOpen(false)} // Close the modal
// className="absolute top-2 right-2 text-gray-500 hover:text-gray-700
focus:outline-none"
// >
// <XIcon className="h-5 w-5" />
// </button>
)} */}
<div>{children}</div>
</div>
</div>
);
};

export const Modal = ({ children, showCloseIcon = true }) => {


return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black
bg-opacity-50">
<div className="bg-white p-6 rounded-lg shadow-lg max-w-lg w-full relative">
{showCloseIcon && (
<button
onClick={() => setLoginModalOpen(false)} // Close the modal
className="absolute top-2 right-2 text-gray-500 hover:text-gray-700
focus:outline-none"
>
<XIcon className="h-5 w-5" />
</button>
)}
<div>{children}</div>
</div>
</div>
);
};

export function Footer() {


return (
<footer className="bg-black text-white py-12">
<div className="container mx-auto flex flex-col items-center justify-between
space-y-8 md:flex-row md:space-y-0 md:space-x-4">
<div className="flex flex-col items-center md:items-start">
{Logo && <img src={Logo.src} alt="Logo" className="w-32 h-auto mb-4" />}
<p className="text-center md:text-left text-sm">
&copy; {new Date().getFullYear()} Reservationist. All rights reserved.
</p>
</div>
<div className="flex space-x-6">
<Link href="/privacy-policy" className="hover:text-gray-400">
Privacy Policy
</Link>
<Link href="mailto:info@thereservationist.io" className="hover:text-gray-
400">
Contact Us
</Link>
</div>
<div className="flex space-x-4">
<Link href="#" className="hover:text-gray-400">
<TwitterIcon className="w-6 h-6" />
</Link>
<Link href="#" className="hover:text-gray-400">
<FacebookIcon className="w-6 h-6" />
</Link>
<Link href="#" className="hover:text-gray-400">
<InstagramIcon className="w-6 h-6" />
</Link>
</div>
</div>
</footer>
);
}

function SearchIcon(props) {
return (
<svg
{...props}
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<circle cx="11" cy="11" r="8" />
<path d="m21 21-4.3-4.3" />
</svg>
);
}

function ChevronDownIcon(props) {
return (
<svg
{...props}
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="m6 9 6 6 6-6" />
</svg>
);
}

export function Component() {


const [isOpen, setIsOpen] = useState(false);
const [menuOpen, setMenuOpen] = useState(false);
const [searchText, setSearchText] = useState("");
const [searchResults, setSearchResults] = useState([]);
const [searchLoading, setSearchLoading] = useState(false);
const [searchError, setSearchError] = useState(null);
const [mounted, setMounted] = useState(false);
const [allRestaurants, setAllRestaurants] = useState([]);
const [loggedIn, setLoggedIn] = useState(false);
const [isSmallScreen, setIsSmallScreen] = useState(false);
const [userMenuOpen, setUserMenuOpen] = useState(false);
const [selectedRestaurant, setSelectedRestaurant] = useState(null);
const [selectedDate, setSelectedDate] = useState(null);
const [selectedGuests, setSelectedGuests] = useState(null);
const [selectedTime, setSelectedTime] = useState(null);
const [phoneNumber, setPhoneNumber] = useState("");
const [phoneNumber2, setPhoneNumber2] = useState("");
// const [searchLoading, setSearchLoading] = useState(false); // Loading state
for restaurant search
const [loginLoading, setLoginLoading] = useState(false); // Loading state for
login
const [otpLoading, setOtpLoading] = useState(false); // Loading state for OTP
verification
const isLoading = searchLoading || loginLoading || otpLoading;
const [selectedTimeRange, setSelectedTimeRange] = useState({ start: null, end:
null });

const [countryCode, setCountryCode] = useState("+1"); // Default country code


const [otpSent, setOtpSent] = useState(false);
const [otpVerified, setOtpVerified] = useState(false);
const [verificationCode, setVerificationCode] = useState("");
// const [clientSecret2, setClientSecret2] = useState("");
const [selectedTimeSlots, setSelectedTimeSlots] = useState("");
const [loading, setLoading] = useState(false); // For tracking loading state
const [clientSecret, setClientSecret] = useState(null); // Start as null
const [hasSavedCard, setHasSavedCard] = useState(false);
const [showModal, setShowModal] = useState(false);
const [loginModalOpen,setLoginModalOpen] = useState(false);
const [showLoginForm, setShowLoginForm] = useState(false);
const [closeTriggered, setCloseTriggered] = useState(false);
const [checkPhoneNumber, setCheckPhoneNumber] = useState(false);
const [sendOtpModalOpen, setSendOtpModalOpen] = useState(false);
const [verifyOtpModalOpen, setVerifyOtpModalOpen] = useState(false);
const pricingRef = useRef(null);
const containerRef = useRef(null);
const restaurantscroll = useRef(null);
const dateRef = useRef(null);
const guestRef = useRef(null);
const phoneRef = useRef(null);
const otpRef = useRef(null);
const timeRef = useRef(null);
const paymentRef = useRef(null);
const loginRef = useRef(null);
const restaurantsRef = useRef(null);
const [showOtpOverlay, setShowOtpOverlay] = useState(false);
const [loginModalManuallyClosed, setLoginModalManuallyClosed] = useState(false);
const [openedFromDateSelection, setOpenedFromDateSelection] = useState(false);
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [isFirstTime, setIsFirstTime] = useState(false);
const [loginTimeout, setLoginTimeout] = useState(null);
// const [password, setPassword] = useState(""); // State to hold password
const [isPasswordVisible, setIsPasswordVisible] = useState(false); // State to
toggle password visibility

// const handlePasswordChange = (e) => {


// setPassword(e.target.value); // Update the password state
// };

const togglePasswordVisibility = () => {


setIsPasswordVisible(!isPasswordVisible); // Toggle visibility state
};
const scrollToSection = (elementRef) => {
if (elementRef.current) {
const offset = 100; // Adjust this value as needed
const elementPosition = elementRef.current.getBoundingClientRect().top +
window.scrollY;
const offsetPosition = elementPosition - offset;

window.scrollTo({
top: offsetPosition,
behavior: 'smooth',
});
}
};

const handleTimeRangeChange = (type, time) => {


if(!localStorage.getItem("phoneNumber") && !localStorage.getItem("token") ){
// setSendOtpModalOpen(true);
}
setSelectedTimeRange((prev) => ({
...prev,
[type]: time,
}));
};
// const createPaymentIntent = async (userId) => {
// setLoading(true); // Set loading state to true
// try {
// const response = await
axios.post('https://api.thereservationist.io/api/user/createPaymentIntent', {
// userId,
// restaurantId: selectedRestaurant._id,

// });

// if (response.data.status === 'success') {


// const { clientSecret } = response.data.data;
// setClientSecret(clientSecret); / // Store
// toast.success('Payment intent created!');
// scrollToSection(paymentRef); // Scroll to the payment section
// } else {
// toast.error('Failed to create payment intent. Please try again.');
// }
// } catch (error) {
// console.error('Error creating payment intent:', error);
// toast.error('An error occurred. Please try again.');
// } finally {
// setLoading(false); // Stop loading once complete
// }
// };

useEffect(() => {

if (!localStorage.getItem("phoneNumber") && localStorage.getItem("token")) {


setSendOtpModalOpen(true)
}
setSelectedTimeRange({ start: null, end: null })
}, []);

useEffect(() => {
if (selectedDate) {
setTimeout(() => {
scrollToSection(guestRef);
}, 50);
}
}, [selectedDate]);

// useEffect(() => {
// if (selectedGuests) {
// scrollToSection(timeRef); // Scroll to the time selection section after
it's rendered
// }
// }, [selectedGuests]);
const openModal = () => setIsOpen(true);
const closeModal = () => setIsOpen(false);

const toggleMenu = () => setMenuOpen(!menuOpen);


const toggleUserMenu = () => setUserMenuOpen(!userMenuOpen);

const openLoginForm = () => {


setShowLoginForm(true);
scrollToSection(loginRef);
};
const options =
"seti_1PzyGyDSTeiybpPZQyTGBAW3_secret_QrhgxCePtG5DyxIdqqLFhON3Pkka9Ky" ?
"seti_1PzyGyDSTeiybpPZQyTGBAW3_secret_QrhgxCePtG5DyxIdqqLFhON3Pkka9Ky" : null;

// const handleSubmit = async (event) => {


// event.preventDefault();

// if (!stripe || !elements) {
// return; // Stripe.js has not loaded yet.
// }

// // Fetch the Setup Intent client_secret from your backend


// const response = await fetch('/create-setup-intent', {
// method: 'POST',
// headers: {
// 'Content-Type': 'application/json',
// },
// });
// const { clientSecret } = await response.json();

// // Confirm the Setup Intent with the card details


// const result = await stripe.confirmCardSetup(clientSecret, {
// payment_method: {
// card: elements.getElement(CardElement),
// billing_details: {
// name: 'User Name', // You can pass other billing details here
// },
// },
// });

// if (result.error) {
// // Handle errors
// setSetupError(result.error.message);
// } else {
// // Store the payment method ID (result.setupIntent.payment_method) in your
database
// const paymentMethodId = result.setupIntent.payment_method;

// // Send paymentMethodId to your backend for future use


// await fetch('/save-payment-method', {
// method: 'POST',
// headers: {
// 'Content-Type': 'application/json',
// },
// body: JSON.stringify({ paymentMethodId }),
// });

// console.log('Payment method successfully set up:', paymentMethodId);


// }
// };
useEffect(() => {
if (closeTriggered) {
console.log("helllllllllll")
// Reset all selections
setSelectedRestaurant(null);
setSelectedDate(null);
setSelectedGuests(null);
setSelectedTime(null);
setSelectedTimeSlots([]);
setClientSecret(null);
setCloseTriggered(false); // Reset the trigger after resetting selections
setTimeout(() => {
scrollToSection(restaurantsRef);
}, 50); }
}, [closeTriggered]);
// const LoaderOverlay = ({ message }) => (
// <div className="fixed inset-0 z-50 flex items-center justify-center bg-black
bg-opacity-50">
// <div className="text-white text-center">
// <div className="loader mb-4"></div> {/* Replace with a spinner if
available */}
// <p>{message}</p>
// </div>
// </div>
// );

const handleNewScan = () => {


console.log("hello5454");
setCloseTriggered(true); // Trigger the close action
setSelectedRestaurant(null); // Reset restaurant
setSelectedDate(null);
setSearchText("");
// Reset date
setSelectedGuests(null); // Reset guests
setSelectedTime(null); // Reset selected time
setSelectedTimeSlots([]); // Reset selected time slots
setClientSecret(null); // Reset payment client secret
setShowModal(false);

setTimeout(() => {
scrollToSection(restaurantsRef); // Scroll back to the restaurant section

}, 50);// Close the modal


};
const [loginInProgress, setLoginInProgress] = useState(false); // To track if
login API is in progress
// const [loginModalManuallyClosed, setLoginModalManuallyClosed] = useState(false);
// To track manual modal close

const handleLoginModalClose = () => {


setLoginModalOpen(false);
setLoginModalManuallyClosed(true); // Track that the user manually closed the
login modal
};
const handleLoginSuccess = async () => {
setLoginInProgress(true); // Indicate login is ongoing
setLoginModalOpen(false); // Close the modal during login
setLoginLoading(true);
// const email = document.querySelector("input[placeholder='Email']").value;
// const password =
document.querySelector("input[placeholder='Password']").value;
const timeout = setTimeout(() => {
setIsFirstTime(true); // Mark as first-time user after 10 seconds
}, 5000); // 10 seconds

setLoginTimeout(timeout);

try {
if (!email || !password) {
toast.error("Please fill in all fields");
return;
}
const response = await
axios.post('https://api.thereservationist.io/api/user/login', {
email,
password,
});
console.log(response,'[[][][][][][][][][');
if (response.data.status === "success") {
const { token, phoneNumber, userId } = response.data.data;

localStorage.setItem("token", token);
localStorage.setItem("userId", userId);
if (phoneNumber) {
localStorage.setItem("phoneNumber", phoneNumber);
setPhoneNumber2(phoneNumber);
}

setLoggedIn(true);
toast.success("Login successful!");

if (!phoneNumber) {
setSendOtpModalOpen(true); // Trigger OTP modal if phone is not verified
}
clearTimeout(timeout);
setIsFirstTime(false);
} else {
toast.error("Login failed. Please check your credentials.");

setLoginModalOpen(true);
}
} catch (error) {
setLoginModalOpen(true);
console.error("Login error:", error);
toast.error("Error during login. Please try again.");
// setLoginModalOpen(true);
setShowLoginForm(true);
} finally {
setLoginInProgress(false);
setLoginLoading(false); // Reset login progress
}
};

// useEffect(() => {
// // Ensure all required data is available before calling createSetupIntent
// if (selectedRestaurant && selectedDate && selectedGuests &&
selectedTimeSlots.length > 0) {
// createPaymentIntent(); // Call the API for creating setup intent
// }
// }, [selectedRestaurant, selectedDate, selectedGuests, selectedTimeSlots]);

const router = useRouter();


const createSetupIntent = async () => {
setLoading(true);
try {
const response = await
axios.post('https://api.thereservationist.io/api/user/createSetupIntent', {
userId:localStorage.getItem("userId"),
restaurantId: selectedRestaurant._id,
});

if (response.data.status === 'success') {


const { clientSecret, hasSavedCard } = response.data.data;
console.log(clientSecret,'++()++')
setClientSecret(clientSecret);
setHasSavedCard(hasSavedCard); // Track if the user already has a saved
card
if (hasSavedCard) {
toast.info('You already have a saved card.');
await handleReminders(selectedTimeRange);
setClientSecret(localStorage.getItem("token"));

setShowModal(true);
} else {
toast.success('Account created');
// await handleReminders(selectedTimeRange);

}
} else {
toast.error('Failed to create setup intent. Please try again.');
}
} catch (error) {
console.error('Error creating setup intent:', error);
toast.error('An error occurred. Please try again.');
} finally {
setLoading(false);
}
};
const handleReminders = async (selectedTimeRange) => {
console.log(selectedTimeRange);
const userId = localStorage.getItem("userId");
const restaurantId = selectedRestaurant._id;
console.log("herererererererererere",)
try {
const nextDay = new Date(selectedDate);
nextDay.setDate(nextDay.getDate() + 1);
const reminderData = {
userId,
restaurantId,
desiredDate: nextDay.toISOString().split('T')[0], // Convert date to
YYYY-MM-DD format
desiredTime: selectedTimeRange,
noOfGuests: selectedGuests,
};
const data=await
axios.post('https://api.thereservationist.io/api/user/remindMe', reminderData);
console.log(data,'api call here');
toast.success(`Reminder set successfully!`);
} catch (error) {
console.error('Error setting reminder:', error);
toast.error(`Failed to set reminder.`);
}
};
useEffect(() => {
const token = localStorage.getItem("token");
const storedPhoneNumber = localStorage.getItem("phoneNumber");

if (token && storedPhoneNumber) {


setLoggedIn(true);
setPhoneNumber2(storedPhoneNumber);
setOtpVerified(true);
}
}, []);

useEffect(() => {
if (phoneNumber2) {
setOtpVerified(true);
setTimeout(() => {
scrollToSection(paymentRef);
}, 50);
// scrollToSection(paymentRef);
}
}, [phoneNumber2]);

const handleNext = () => {


if (selectedTimeRange.start && selectedTimeRange.end) {
const slug = selectedRestaurant.restaurantName.toLowerCase().replace(/\s+/g,
'');
const date = selectedDate.toISOString().split("T")[0];
const guests = selectedGuests;
const start = selectedTimeRange.start;
const end = selectedTimeRange.end;

router.replace(
`/?restaurant=${slug}&date=${date}&guests=${guests}&start=${start}&end=$
{end}`,
undefined,
{ shallow: true, scroll: false }
);

setSelectedTime(selectedTimeRange);

if (!localStorage.getItem("token") && !loginInProgress) {


setLoginModalOpen(true);
setShowLoginForm(true);
setTimeout(() => {
scrollToSection(loginRef);
}, 50);
} else if (!localStorage.getItem("phoneNumber")) {
// Show OTP modal if needed
} else {
setTimeout(() => {
scrollToSection(paymentRef);
}, 50);
// scrollToSection(paymentRef);
}
} else {
toast.error("Please select both start and end times.");
}
};

useEffect(() => {
if (selectedGuests) {
setTimeout(() => {
scrollToSection(timeRef);
}, 50); }
}, [selectedGuests]);

const handleRestaurantCardClick = (restaurant) => {


const slug = restaurant.restaurantName.toLowerCase().replace(/\s+/g, '');
router.replace(`?restaurant=${slug}`, undefined, { shallow: true, scroll: false
});
if(!localStorage.getItem("token")){
// if (!loginInProgress && !loginModalManuallyClosed) {
// setLoginModalOpen(true);
// }
if (!localStorage.getItem("token") && !loginInProgress) {
setLoginModalOpen(true);
setOpenedFromDateSelection(false); // Opened from restaurant card selection
// Trigger login modal if not logged in and login not in progress
}
// setLoginModalOpen(true);
setSearchText(restaurant.restaurantName);
setSearchResults([restaurant]);
handleRestaurantSelect(restaurant);
setTimeout(() => {
scrollToSection(dateRef);
}, 50); }
setSearchText(restaurant.restaurantName);
setSearchResults([restaurant]);
handleRestaurantSelect(restaurant);
setTimeout(() => {
scrollToSection(dateRef);
}, 50); };

useEffect(() => {
setMounted(true);
const token = localStorage.getItem("token");
setLoggedIn(!!token);
console.log(restList.data, "restList.data");
setAllRestaurants(restList.data)
// axios
// .get("https://api.thereservationist.io/api/admin/restaurants")
// .then((response) => {
// if (response.data.status === "success") {
// console.log(response.data.data,'/////////');
// setAllRestaurants(response.data.data);
// }
// })
// .catch((error) => {
// console.error("Error fetching all restaurants:", error);
// });
}, []);

useEffect(() => {
if (loggedIn && phoneNumber2 && clientSecret) {
setTimeout(() => {
scrollToSection(paymentRef);
}, 50); }
}, [loggedIn, phoneNumber2, clientSecret]);

const handleSearchChange = async (event) => {


const value = event.target.value;
setSearchText(value);
setTimeout(() => {
scrollToSection(dateRef);
}, 50);
if (value.trim() !== "") {
setSearchLoading(true);
setSearchError(null);

try {
const response = await axios.get(
`https://api.thereservationist.io/api/user/restaurants/search?name=$
{value}`
);

if (response.data.status === "success") {


setSearchResults(response.data.data);
setSelectedRestaurant(null);
setSelectedDate(null);
setSelectedGuests(null);
setSelectedTime(null);
setOtpSent(false);
setOtpVerified(false);
} else {
setSearchResults([]);
setSearchError("No restaurants found.");
}
} catch (error) {
console.error("Search error:", error);
setSearchResults([]);
if (error.response) {
setSearchError(
error.response.data.message || "An error occurred while searching."
);
} else if (error.request) {
setSearchError("Network error. Please try again.");
} else {
setSearchError("An unknown error occurred.");
}
} finally {
setSearchLoading(false);
}
} else {
setSearchResults([]);
setSelectedRestaurant(null);
setSelectedDate(null);
setSelectedGuests(null);
setSelectedTime(null);
setOtpSent(false);
setOtpVerified(false);
}
};

const handleRestaurantSelect = (restaurant) => {


setSelectedRestaurant(restaurant);
setSelectedDate(null);
setSelectedGuests(null);
setSelectedTime(null);
setSelectedTimeSlots([]);
setOtpSent(false);
setOtpVerified(false);
setTimeout(() => {
scrollToSection(dateRef);
}, 50);
};

const handleDateSelect = (date) => {


const slug = selectedRestaurant.restaurantName.toLowerCase().replace(/\s+/g,
'');
const formattedDate = date.toISOString().split("T")[0];

router.push(`/?restaurant=${slug}&date=${formattedDate}`, undefined, { shallow:


true, scroll: false });

setSelectedDate(date);

if (!localStorage.getItem("token") && !loginInProgress) {


setLoginModalOpen(true);
setShowLoginForm(true);
setTimeout(() => {
scrollToSection(loginRef);
}, 50);
setOpenedFromDateSelection(true);
} else {
if (!localStorage.getItem("phoneNumber")) {
// Optionally show OTP modal
} else {
setTimeout(() => {
scrollToSection(guestRef);
}, 50);
}
}
};

const handleGuestsSelect = (guests) => {


const slug = selectedRestaurant.restaurantName.toLowerCase().replace(/\s+/g,
'');
const date = selectedDate.toISOString().split("T")[0];

router.replace(`/?restaurant=${slug}&date=${date}&guests=${guests}`, undefined,
{ shallow: true, scroll: false });

setSelectedGuests(guests);
setSelectedTime(null);
setOtpSent(false);
setOtpVerified(false);
};

const handleTimeSlotToggle = (timeSlot) => {


setSelectedTimeSlots((prevSelectedSlots) => {
if (prevSelectedSlots.includes(timeSlot)) {
return prevSelectedSlots.filter((slot) => slot !== timeSlot);
} else if (prevSelectedSlots.length < 3) {
return [...prevSelectedSlots, timeSlot];
}
return prevSelectedSlots;
});
};

const handlePhoneNumberChange = (e) => {


setPhoneNumber(e.target.value);
};

const handleCountryCodeChange = (value) => {


setCountryCode(value);
};
const handleSendOtp = () => {
setShowOtpOverlay(false); // Hide overlay after OTP preparation
const userId = localStorage.getItem("userId");

axios
.post("https://api.thereservationist.io/api/user/initiatePhoneVerification",
{
userId,
phoneNumber: `${countryCode}${phoneNumber}`,
})
.then((response) => {
if (response.data.status === "success") {
setOtpSent(true);
toast.success('OTP sent successfully!');
} else {
toast.error('Failed to send OTP.');
}
})
.catch((error) => {
console.error("Error sending OTP:", error);
toast.error('Error sending OTP. Please try again.');
});
};

const handleResendOtp = () => {


handleSendOtp();
};
useEffect(() => {
const token = localStorage.getItem("token");
const storedPhoneNumber = localStorage.getItem("phoneNumber");

// Check if the phone number is undefined or invalid and remove it


if (storedPhoneNumber === "undefined" || !storedPhoneNumber) {
localStorage.removeItem("phoneNumber");
}

// Proceed with the OTP flow if the user is logged in and no valid phone number
exists
if (token && (!storedPhoneNumber || storedPhoneNumber === "undefined")) {
setLoggedIn(true); // Ensure the logged-in state is set
setOtpSent(false); // Reset OTP state to ensure OTP can be sent again
setOtpVerified(false); // Ensure OTP verification starts from scratch
setPhoneNumber2(""); // Reset the stored phone number to trigger the OTP flow
setTimeout(() => {
scrollToSection(phoneRef);
}, 50); // Scroll to the phone verification section
}
}, []);

const handleVerifyOtp = () => {


setOtpLoading(true);
const userId = localStorage.getItem("userId");

axios
.post("https://api.thereservationist.io/api/user/verifyPhoneNumber", {
userId,
phoneNumber: `${countryCode}${phoneNumber}`,
verificationCode,
})
.then((response) => {
if (response.data.status === "success") {
setOtpVerified(true);
setPhoneNumber2(phoneNumber);
localStorage.setItem("phoneNumber", phoneNumber);
toast.success('Phone number verified successfully!');
setVerifyOtpModalOpen(false);
setOtpLoading(false);
setLoading(false);
} else {
toast.error('Failed to verify OTP.');
}
})
.catch((error) => {
console.error("Error verifying OTP:", error);
setOtpLoading(false); // Stop loader
toast.error('Error verifying OTP. Please try again.');
});
};

const handleLogout = () => {


localStorage.removeItem("token");
localStorage.removeItem("userId");
localStorage.removeItem("phoneNumber");
setLoggedIn(false);
setUserMenuOpen(false);
router.push("/");
window.location.reload();
};

return (
<div className="bg-background">
<ToastContainer />
<header className="sticky top-0 z-10 w-full bg-stone-950 shadow">
<nav className="container mx-auto flex items-center justify-between py-4 px-4
sm:px-6">
{/* Logo */}
<Link href="/" className="text-lg font-bold flex items-center">
<img
src={Logo.src}
alt="Logo"
className="h-8 object-contain"
/>
</Link>

{/* Desktop Navigation */}


<div className="hidden md:flex items-center space-x-4">
<Link href="/" className="text-white hover:text-gray-400">
Home
</Link>
<button
onClick={() => setTimeout(() => {
scrollToSection(pricingRef);
}, 50)}
className="text-white hover:text-gray-400 px-4 py-2 rounded-md"
>
Pricing
</button>

{loggedIn ? (
<div className="relative">
{/* User Icon */}
<button
onClick={toggleUserMenu}
className="flex items-center text-white focus:outline-none"
>
<UserCircleIcon className="h-6 w-6" />
</button>
{/* Dropdown Menu */}
{userMenuOpen && (
<div className="absolute right-0 mt-2 w-48 bg-white rounded-md shadow-
lg z-10">
<Link href="/profile" className="block px-4 py-2 text-sm text-gray-
700 hover:bg-gray-100">
My Profile
</Link>
<Link href="/reservations" className="block px-4 py-2 text-sm text-
gray-700 hover:bg-gray-100">
My Reservations
</Link>
<button
onClick={handleLogout}
className="w-full text-left px-4 py-2 text-sm text-gray-700
hover:bg-gray-100"
>
Logout
</button>
</div>
)}
</div>
) : (
<button
onClick={() => setLoginModalOpen(true)}
className="text-white hover:text-gray-400 px-4 py-2 rounded-md"
>
Login
</button>
)}
</div>

{/* Mobile Navigation */}


<div className="md:hidden flex items-center">
<button
onClick={toggleMenu}
className="text-white focus:outline-none"
>
{menuOpen ? (
<XIcon className="h-6 w-6" />
) : (
<MenuIcon className="h-6 w-6" />
)}
</button>

{/* Mobile Dropdown Menu */}


{menuOpen && (
<div className="absolute top-16 right-0 w-48 bg-white rounded-md shadow-lg
z-10">
<Link href="/" className="block px-4 py-2 text-sm text-gray-700
hover:bg-gray-100">
Home
</Link>
<button
onClick={() => setTimeout(() => {
scrollToSection(pricingRef);
}, 50)}
className="block w-full text-left px-4 py-2 text-sm text-gray-700
hover:bg-gray-100"
>
Pricing
</button>

{loggedIn ? (
<>
<Link href="/profile" className="block px-4 py-2 text-sm text-gray-
700 hover:bg-gray-100">
My Profile
</Link>
<Link href="/reservations" className="block px-4 py-2 text-sm text-
gray-700 hover:bg-gray-100">
My Reservations
</Link>
<button
onClick={handleLogout}
className="block w-full text-left px-4 py-2 text-sm text-gray-700
hover:bg-gray-100"
>
Logout
</button>
</>
) : (
<button
onClick={() => setLoginModalOpen(true)}
className="block w-full text-left px-4 py-2 text-sm text-gray-700
hover:bg-gray-100"
>
Login
</button>
)}
</div>
)}
</div>
</nav>

{/* Login Modal */}


{/* {loginModalOpen && (
<Modal onClose={handleLoginModalClose}>
<div className="p-6">
<button
onClick={handleLoginModalClose}
className="absolute top-2 right-2 text-gray-500 hover:text-gray-700
focus:outline-none"
>
<XIcon className="h-5 w-5" />
</button>
<img
src={resyLogo.src}
alt="Resy Logo"
width={100}
className="w-32 h-auto mb-4 m-auto"
/>
<p className="text-center text-sm text-muted-foreground mb-4">
Enter your Resy.com credentials to continue
</p>
<Input type="email" placeholder="Email" value={email} // Bind input to
state
onChange={(e) => setEmail(e.target.value)} className="w-full mb-4" />
<div className="relative">
<Input type={isPasswordVisible ? "text" : "password"}
placeholder="Password" value={password} onChange={(e) =>
setPassword(e.target.value)} className="w-full mb-4" />
<button
type="button"
onClick={togglePasswordVisibility} // Toggle visibility on click
className="absolute right-3 top-3"
>
{isPasswordVisible ? (
<EyeOffIcon className="h-5 w-5 text-gray-500" />
) : (
<EyeIcon className="h-5 w-5 text-gray-500" />
)}
</button>
</div>
<Button onClick={handleLoginSuccess} className="w-full">
Login to continue
</Button>
</div>
</Modal>
)} */}
</header>

<main>
{/* Loader when searching */}
{searchLoading && (
<div className="flex justify-center my-8">
<LoaderOverlay loading={isLoading} />

</div>
)}

{/* Loader when logging in */}


{/* {loginLoading && (
<div className="flex flex-col items-center justify-center my-8">
<LoaderOverlay loading={isLoading}message="Please wait a moment while we create
your account!" />
<p className="mt-4 text-center text-sm text-gray-500">
Please wait a moment while we create your account!
</p>
</div>
)} */}

{/* Loader when verifying OTP */}


{otpLoading && (
<div className="flex justify-center my-8">
<LoaderOverlay loading={isLoading} />

</div>
)}
<header className="relative mb-8">
<div className="absolute inset-0 bg-cover bg-center bg-no-repeat blur-sm
opacity-50" />
<div className="absolute inset-0 bg-gradient-to-t from-background to-
transparent" />
<div
className="relative mx-auto md:w-11/12 lg:w-2/3 flex h-full flex-col
items-center justify-center gap-6 px-2 text-center text-secondary-foreground sm:px-
6 md:gap-6">
<h1 className="text-4xl leading-snug mb-0 mt-5 md:mt-16 font-bold
tracking-tight sm:px-1 md:text-5xl xl:text-6xl sm:leading-snug md:leading-snug
lg:leading-snug xl:leading-snug">
<span className="text-accent-foreground">Book</span> Exclusive
Restaurant <span className="text-white text-nowrap font-extrabold px-2 bg-
resy">Reservations</span>
<span className="bg-gradient-to-tr from-popover-foreground to-slate-
800 text-transparent bg-clip-text">with Ease</span>
</h1>
<p
className="text-md sm:text-lg md:text-xl lg:text-1xl xl:text-2xl max-
w-xs sm:max-w-md md:max-w-xl lg:max-w-2xl"
>Sick and tired of waitlists? The Reservationist will automatically
secure the first open reservation for you! You only pay if we're successful!
</p>
</div>
</header>

{/* Search bar and other dynamic sections start here */}
<div className="flex flex-col gap-0.5 w-full text-center max-w-2xl mx-auto
mt-2 p-1 pt-2 text-secondary-foreground rounded-lg shadow-2xl shadow-stone-500
space-y-0 bg-gradient-to-b from-stone-950 to-stone-600" ref={containerRef}>
<h3 className="text-sm font-semibold text-white">Create Your Booking
Bot 🤖</h3>

<div ref={dateRef} name="select-restaurant" id="select-restaurant">


<div className="flex flex-col gap-2.5 bg-stone-100 shadow-2xl w-full p-
6 rounded-md">
<div className="flex bg-gradient-to-bl from-rose-400 to-accent-
foreground m-auto text-xl justify-center align-middle font-extrabold w-10 h-10
text-stone-100 rounded-full leading-loose shadow-stone-700 shadow-sm">1</div>
<h4 className="text-xl font-extrabold m-auto text-transparent bg-
clip-text bg-gradient-to-tr from-stone-950 to-stone-700">Select Restaurant</h4>
<div className="relative">
<form className="flex items-center rounded-lg bg-background shadow-lg">
<Input
type="text"
placeholder="Search for a restaurant"
value={searchText}
onChange={handleSearchChange}
className="flex-1 rounded-l-lg border-none bg-transparent py-3 px-4 pl-4 pr-2
text-sm focus:outline-none"
/>
<Button className="rounded-r-lg py-3 px-4" style={{ background: "#e11d47" }}>
<SearchIcon className="h-5 w-5" />
</Button>
</form>
<div style={{ maxHeight: "400px", overflowY: "auto" }}>
{searchResults.length > 0 && (
<div className="mt-2 w-full bg-white shadow-lg rounded-md space-y-4 p-4">
{searchResults.map((result, index) => (
<div
key={index}
className="flex items-center p-4 hover:bg-gray-100 cursor-pointer
rounded-md"
onClick={() => handleRestaurantSelect(result)}
>
<img
src={images[index].src}
alt={`Restaurant ${result.restaurantName} Image`}
width={100}
height={100}
className="rounded-md"
/>
<div className="ml-4">
<h3 className="text-lg font-semibold">
{result.restaurantName}
</h3>

</div>
</div>
))}
</div>
)}
</div>
</div>

</div>
</div>

{selectedRestaurant && (
<div ref={restaurantscroll} name="select-date" id="select-date">
<div className="flex flex-col gap-2.5 mt-4 p-6 bg-stone-100 rounded-
md shadow-lg">
<div className="flex bg-gradient-to-bl from-rose-400 to-accent-
foreground m-auto text-xl justify-center align-middle font-extrabold w-10 h-10
text-slate-100 rounded-full leading-loose shadow-stone-700 shadow-sm">2</div>
<h2 className="text-xl font-extrabold m-auto text-transparent bg-
clip-text bg-gradient-to-tr from-stone-950 to-stone-700">Select a Booking Date</h2>
<Calendar
onChange={handleDateSelect}
value={selectedDate}
className="w-full"
calendarType="gregory"
tileClassName={({ date, view }) =>
view === 'month' && date.getDate() === (selectedDate ?
selectedDate.getDate() : null) &&
date.getMonth() === (selectedDate ? selectedDate.getMonth() :
null) &&
date.getFullYear() === (selectedDate ?
selectedDate.getFullYear() : null)
? 'bg-accent-foreground text-white rounded-full'
: undefined
}
/>
</div>
</div>
)}

{selectedRestaurant && selectedDate && (


<div ref={guestRef} name="select-guests" id="select-guests">
<div ref={guestRef} className="flex flex-col gap-2.5 mt-4 p-6 bg-stone-100
rounded-md shadow-lg">
<div className="flex bg-gradient-to-bl from-rose-400 to-accent-foreground m-
auto text-xl justify-center align-middle font-extrabold w-10 h-10 text-slate-100
rounded-full leading-loose shadow-stone-700 shadow-sm">3</div>
<h2 className="text-xl font-extrabold m-auto text-transparent bg-clip-text
bg-gradient-to-tr from-stone-950 to-stone-700 mb-4">Select Number of Guests</h2>
<div className="flex flex-wrap justify-center gap-4">
{[...Array(9).keys()].map((i) => (
<Button
key={i + 1}
onClick={() => handleGuestsSelect(i + 1)}
className={`w-12 h-12 flex items-center justify-center ${selectedGuests
=== i + 1 ? 'bg-accent-foreground text-white' : ''}`}
>
{i + 1}
</Button>
))}
</div>
{selectedGuests && (
<p className="mt-4 text-center text-lg font-semibold text-gray-700">
Reservationist Fee: ${selectedGuests * 10}
</p>
)}
</div>
</div>
)}

{selectedRestaurant && selectedDate && selectedGuests && (


<div ref={timeRef} name="select-time" id="select-time">
<div className="flex flex-col gap-2.5 mt-4 p-6 bg-stone-100 rounded-md shadow-
lg">
<div className="flex bg-gradient-to-bl from-rose-400 to-accent-foreground m-
auto text-xl justify-center align-middle font-extrabold w-10 h-10 text-slate-100
rounded-full leading-loose shadow-stone-700 shadow-sm">4</div>
<h2 className="text-xl font-extrabold m-auto text-transparent bg-clip-text
bg-gradient-to-tr from-stone-950 to-stone-700 mb-4">Select Time Range</h2>
<div className="flex items-center gap-4">
<div className="flex-1">
<label className="block text-sm font-semibold mb-1">Start Time</label>
<Select onValueChange={(value) => handleTimeRangeChange("start", value)}
value={selectedTimeRange.start}>
<SelectTrigger className="w-full">
<SelectValue placeholder="Start Time" />
</SelectTrigger>
<SelectContent>
{Array.from({ length: 28 }, (_, i) => {
const hours = Math.floor(17 + i / 4); // Start from 5 PM
const minutes = (i % 4) * 15;
const timeLabel = `${hours % 12 || 12}:${minutes.toString().padStart(2,
"0")} ${hours >= 12 ? "PM" : "AM"}`;
return <SelectItem key={i} value={timeLabel}>{timeLabel}</SelectItem>;
})}
</SelectContent>
</Select>
</div>
<div className="flex-1">
<label className="block text-sm font-semibold mb-1">End Time</label>
<Select onValueChange={(value) => handleTimeRangeChange("end", value)}
value={selectedTimeRange.end}>
<SelectTrigger className="w-full">
<SelectValue placeholder="End Time" />
</SelectTrigger>
<SelectContent>
{Array.from({ length: 29 }, (_, i) => {
const hours = Math.floor(17 + i / 4); // Start from 5 PM
const minutes = (i % 4) * 15;
const timeLabel = `${hours % 12 || 12}:${minutes.toString().padStart(2,
"0")} ${hours >= 12 ? "PM" : "AM"}`;
const startHour = selectedTimeRange.start
? parseInt(selectedTimeRange.start.split(":")[0], 10) +
(selectedTimeRange.start.includes("PM") ? 12 : 0)
: 0;
const startMinutes = selectedTimeRange.start
? parseInt(selectedTimeRange.start.split(":")[1].split(" ")[0], 10)
: 0;
const currentHour = Math.floor(hours);
const currentMinutes = minutes;

// Ensure end times are always after the selected start time and include
times up to midnight (12 AM).
if (
!selectedTimeRange.start ||
(currentHour > startHour || (currentHour === startHour && currentMinutes
> startMinutes)) &&
currentHour < 24
) {
return <SelectItem key={i} value={timeLabel}>{timeLabel}</SelectItem>;
}
return null;
})}
</SelectContent>
</Select>
</div>

</div>
{selectedTimeRange.start && selectedTimeRange.end && (
<Button onClick={handleNext} className="mt-4 bg-accent-foreground text-
white">
{!isFirstTime ? "Create Reminder" : "Next"}
</Button>
)}
</div>
</div>
)}

{/* {selectedTime && !loggedIn && showLoginForm && (


<div ref={loginRef} name="login-section" id="login-section">
<div className="flex flex-col gap-2.5 mt-4 p-6 bg-stone-100 rounded-md shadow-
lg">
<div className="flex bg-gradient-to-bl from-rose-400 to-accent-foreground m-
auto text-xl justify-center align-middle font-extrabold w-10 h-10 text-slate-100
rounded-full leading-loose shadow-stone-700 shadow-sm">5</div>
<h2 className="text-xl font-extrabold m-auto text-transparent bg-clip-text
bg-gradient-to-tr from-stone-950 to-stone-700 mb-4">Login to Continue</h2>
<img
src={resyLogo.src}
alt="Resy Logo"
width={100}
className="w-32 h-auto mb-4 m-auto"
/>
<p className="text-center text-sm text-muted-foreground mb-4">
Enter your Resy.com credentials to continue
</p>
<Input
type="text"
placeholder="Email"
className="w-full mb-4"
/>
<Input
type="password"
placeholder="Password"
className="w-full mb-4"
/>
<Button
onClick={() => {
setLoginLoading(true); // Set loading state immediately on button click
handleLoginSuccess(); // Proceed with login function
}}
className="w-full"
>
Login
</Button>
</div>
</div>
)} */}

{sendOtpModalOpen && (
<Modal2 onClose={() => setSendOtpModalOpen(false)}>
<div className="p-6">
{/* <button
onClick={() => setSendOtpModalOpen(false)}
className="absolute top-2 right-2 text-gray-500 hover:text-gray-700
focus:outline-none"
>
<XIcon className="h-5 w-5" />
</button> */}
<h2 className="text-xl font-bold mb-4">Send OTP</h2>
<div className="flex gap-2 mb-4">
<Select onValueChange={handleCountryCodeChange} value={countryCode}
className="w-24">
<SelectTrigger className="w-full">
<SelectValue placeholder="Code" />
</SelectTrigger>
<SelectContent>
<SelectItem value="+1">+1 (USA)</SelectItem>
<SelectItem value="+92">+92 (Pakistan)</SelectItem>
<SelectItem value="+44">+44 (UK)</SelectItem>
</SelectContent>
</Select>
<Input
type="text"
placeholder="Phone number"
value={phoneNumber}
onChange={handlePhoneNumberChange}
className="w-full"
/>
</div>
<Button
onClick={() => {
handleSendOtp();
setSendOtpModalOpen(false);
setVerifyOtpModalOpen(true);
}}
disabled={!phoneNumber}
className="w-full"
>
Send OTP
</Button>
<span>
<h6>
<label>
<input type="checkbox" required className="mr-2" />
By entering my mobile number, I agree to receive text messages from The
Reservationist...
</label>
</h6>
</span>
</div>
</Modal2>
)}

{verifyOtpModalOpen && (
<Modal onClose={() => setVerifyOtpModalOpen(false)}>
<div className="p-6">
{/* <button
onClick={() => setVerifyOtpModalOpen(false)}
className="absolute top-2 right-2 text-gray-500 hover:text-gray-700
focus:outline-none"
>
<XIcon className="h-5 w-5" />
</button> */}
<h2 className="text-xl font-bold mb-4">Verify OTP</h2>
<Input
type="text"
placeholder="Enter OTP"
value={verificationCode}
onChange={(e) => setVerificationCode(e.target.value)}
className="w-full mb-4"
/>
<div className="flex justify-between gap-4 mt-4">
<Button
onClick={() => {
setVerifyOtpModalOpen(false);
setSendOtpModalOpen(true);
}}
className="w-1/2 bg-gray-200 text-gray-700 hover:bg-gray-300"
>
Back
</Button>
<Button onClick={handleVerifyOtp} disabled={!verificationCode}
className="w-1/2">
Verify OTP
</Button>
</div>
<div className="flex justify-between mt-4">
<Button variant="link" onClick={() => setSendOtpModalOpen(true)}>
Resend OTP
</Button>
</div>
</div>
</Modal>
)}

{showModal && (
<Modal onClose={() => setShowModal(false)}>
<div className="p-6">
<h2 className="text-xl font-bold mb-4">Confirmation</h2>
<p>You successfully created a reminder!</p>
<div className="mt-4 flex justify-end space-x-2">
<Button onClick={handleNewScan}>Create New Reminder</Button>
<Button onClick={handleNewScan}>Close</Button>

</div>
</div>
</Modal>
)}

{selectedTime && (
<div ref={paymentRef} name="payment-section" id="payment-section">
{loading ? (
<div className="flex justify-center my-8">
<LoaderOverlay loading={true} message="Processing your request..." />
</div>
) : clientSecret ? (
// Stripe part
<Elements stripe={stripePromise}>
<div className="p-6 rounded-lg bg-gray-100 shadow-lg"> {/* Main background
color */}
<CheckoutForm
clientSecret={clientSecret}
userId={localStorage.getItem('userId')}
restaurantId={selectedRestaurant._id}
selectedDate={selectedDate}
selectedGuests={selectedGuests}
selectedTimeSlots={selectedTimeRange}
setPaymentMethod={(paymentMethodId) => {
console.log('Payment Method saved:', paymentMethodId);
setShowModal(true);
setLoading(false);
// Handle further actions here
}}
/>
</div>
</Elements>
) : (
<div className="flex flex-col items-center justify-center my-8">
{loginLoading && (
<div className="flex flex-col items-center justify-center my-8">
<LoaderOverlay loading={true} message="Hang tight! We’re almost done. You’ll
only have to do this once!" />
<p className="mt-4 text-center text-sm text-gray-500">
Please wait a moment while we create your account!
</p>
</div>

)}
</div>

)}
</div>
)}

</div>

{/* Rest of the content */}


<section className="min-h-screen flex flex-col items-center justify-center
px-4 my-12 sm:px-6 md:my-16 lg:my-20" ref={restaurantsRef}>
<h2 className="text-3xl font-bold tracking-tight sm:text-4xl mb-12">
All Restaurants
</h2>
<div className="grid gap-16 md:grid-cols-2 lg:grid-cols-3 max-w-screen-lg mx-
auto">
{allRestaurants.map((restaurant, index) => (
<Card
key={index}
className="p-4 cursor-pointer group relative overflow-hidden"
onClick={() => handleRestaurantCardClick(restaurant)}
>
<CardContent className="flex flex-col items-center justify-center gap-6 p-6">
<img
src={restaurant?.restaurantImage || images[index % images.length].src}
alt={`Restaurant ${restaurant.restaurantName} Image`}
width={200}
height={200}
className="rounded-full"
style={{ aspectRatio: "200/200", objectFit: "cover" }}
/>
<div className="text-center">
<h3 className="text-xl font-semibold">
{restaurant.restaurantName}
</h3>
<div className="flex items-center justify-center text-muted-foreground">
<MapPin className="h-5 w-5 mr-1" />
<h5 className="text-lg">{restaurant.address}</h5>
</div>
</div>
</CardContent>

{/* Desktop Hover Overlay */}

</Card>

))}
</div>
{/* {loginModalOpen && (
<Modal showCloseIcon={!openedFromDateSelection}>
<div className="p-6">
{openedFromDateSelection ? null : (
<button
onClick={() => setLoginModalOpen(false)}
className="absolute top-2 right-2 text-gray-500 hover:text-gray-700
focus:outline-none"
>
<XIcon className="h-5 w-5" />
</button>
)}

<img
src={resyLogo.src}
alt="Resy Logo"
width={100}
className="w-32 h-auto mb-4 m-auto"
/>

<p className="text-center text-sm text-muted-foreground mb-4">


Enter your Resy.com credentials to continue
</p>

<Input
type="email"
placeholder="Email"
value={email} // Controlled input
onChange={(e) => setEmail(e.target.value)} // Update email state
className="w-full mb-4"
/>

<div className="relative">
<Input
type={isPasswordVisible ? "text" : "password"}
placeholder="Password"
value={password} // Controlled input
onChange={(e) => setPassword(e.target.value)} // Update password state
className="w-full mb-4"
/>
<button
type="button"
onClick={togglePasswordVisibility} // Toggle visibility on click
className="absolute right-3 top-3"
>
{isPasswordVisible ? (
<EyeOffIcon className="h-5 w-5 text-gray-500" />
) : (
<EyeIcon className="h-5 w-5 text-gray-500" />
)}
</button>
</div>
<Button
onClick={() => handleLoginSuccess()}
className="w-full"
>
Login to continue
</Button>
</div>
</Modal>
)} */}
</section>

<section className="flex flex-col text-center items-center justify-center


px-4 my-12 sm:px-6 md:my-16 mx-10 lg:my-20">
<div className="mx-auto max-w-3xl text-center">
<p className="text-base font-extrabold text-primary leading-tight">
We book sought after restaurant reservations as soon as slots open up
</p>
<h2 className="text-3xl mt-3 font-bold tracking-tight sm:text-4xl">
How The Reservationist Works
</h2>
<p className="mt-3 text-md text-muted-foreground sm:mt-4">
It can often take weeks to get a reservation at Michelin-Star
restaurants and popular establishments. The Reservationist helps you by automating
the waitlist process – as soon as a cancelation occurs, we use our custom-built AI
to snag that opening just for you. We currently support various restaurants in
Washington DC and will be expanding into new cities soon! </p>
</div>
<div className="flex flex-wrap justify-center mt-8 gap-8 sm:grid-cols-2
md:grid-cols-3">
<div className="flex flex-col top-0 w-60 items-center justify-center
gap-4 mx-0">
<SearchIcon className="h-12 w-12 text-primary" />
<h3 className="text-xl font-semibold">Deploy Our AI 🤖</h3>
<p className="text-muted-foreground">
Search our available restaurants from the list above and tell us the
dates and times you would like to book. Then, enter your Resy log-in information
along with your payment details. Our algorithm will immediately start searching for
your reservation. </p>
</div>
<div className="flex flex-col w-60 items-center gap-4">
<CalendarIcon className="h-12 w-12 text-primary" />
<h3 className="text-xl font-semibold">The Waiting Game</h3>
<p className="text-muted-foreground">
Hang tight! It may take several minutes or several days to confirm
your reservation. Once booked, we’ll text you the details of your confirmation!
</p>
</div>
<div className="flex flex-col w-60 items-center gap-4">
<SmileIcon className="h-12 w-12 text-primary" />
<h3 className="text-xl font-semibold">Celebrate</h3>
<p className="text-muted-foreground">
Now you get to enjoy the dining experience of your dreams! Whether
it’s your birthday, anniversary, or you just want to enjoy exquisite food, the
Reservationist is happy to help secure the most exclusive dining experiences!
</p>
</div>
</div>
</section>

<section
className="flex flex-col items-center justify-center px-4 my-12 sm:px-6 md:my-16
lg:my-20 mb-16" // Add `mb-16` for extra bottom margin
ref={pricingRef}
>
<div className="mx-auto max-w-3xl text-center">
<h2 className="text-3xl font-bold tracking-tight sm:text-4xl">
<button
onClick={() => setTimeout(() => {
scrollToSection(pricingRef);
}, 50)}
className="text-muted-foreground hover:text-foreground px-4 py-2 rounded-md"
style={{ color: "Black" }}
>
Pricing
</button>
</h2>
<div className="mt-3 flex flex-col items-center">
<div className="text-5xl font-extrabold text-accent-foreground">$</div>
<p className="mt-3 text-lg text-muted-foreground sm:mt-4 max-w-2xl">
The Reservationist is 100% risk-free! We won’t charge you unless we
successfully secure a reservation for you and your party. For as low as $10
per person, we can get you into the restaurants you’ve long sought to
enjoy!
</p>
</div>
</div>
</section>

<section className="flex flex-col items-center justify-center px-4 my-12


sm:px-6 md:my-16 lg:my-20">
<div className="mx-auto max-w-3xl text-center">
<h2 className="text-3xl font-bold tracking-tight sm:text-4xl">
Common Questions
</h2>
<p className="mt-3 text-lg text-muted-foreground sm:mt-4">
Get answers to the most frequently asked questions.
</p>
</div>
<div className="mt-8 space-y-4 w-full max-w-3xl">
<Collapsible>
<CollapsibleTrigger className="flex w-full items-center justify-
between rounded-lg bg-muted px-6 py-4 font-medium transition-colors hover:bg-
muted/80">
<span>How do I make a reservation?</span>
<ChevronDownIcon className="h-5 w-5 transition-transform" />
</CollapsibleTrigger>
<CollapsibleContent className="px-6 py-4 text-muted-foreground">
To make a reservation, simply use our search tool at the top of
this page
to find the restaurant you're interested in, select your desired
date, time,
and party size, and click the "Book" button.
</CollapsibleContent>
</Collapsible>
<Collapsible>
<CollapsibleTrigger className="flex w-full items-center justify-
between rounded-lg bg-muted px-6 py-4 font-medium transition-colors hover:bg-
muted/80">
<span>Can I cancel or modify my reservation?</span>
<ChevronDownIcon className="h-5 w-5 transition-transform" />
</CollapsibleTrigger>
<CollapsibleContent className="px-6 py-4 text-muted-foreground">
If you need to cancel your reservation, please do so by logging into
your Resy account and canceling there. Be mindful as Resy may charge you if your
cancellation does not abide by their policies (i.e. cancelling within 24 hours).

If you need to modify your reservation, please log into your Resy account, cancel
your reservation, and try booking again using The Reservationist. Please note that
you will not be refunded by The Reservationist if you need to modify your
reservation. </CollapsibleContent>
</Collapsible>
<Collapsible>
<CollapsibleTrigger className="flex w-full items-center justify-
between rounded-lg bg-muted px-6 py-4 font-medium transition-colors hover:bg-
muted/80">
<span>What happens if The Reservationist is not able to book a
table for me?</span>
<ChevronDownIcon className="h-5 w-5 transition-transform" />
</CollapsibleTrigger>
<CollapsibleContent className="px-6 py-4 text-muted-foreground">
That can happen, albeit rarely. If we are not able to secure your
reservation, just try again on another date! You are only charged if we confirm
your table! </CollapsibleContent>
</Collapsible>
<Collapsible>
<CollapsibleTrigger className="flex w-full items-center justify-
between rounded-lg bg-muted px-6 py-4 font-medium transition-colors hover:bg-
muted/80">
<span>How much does the Reservationist cost?</span>
<ChevronDownIcon className="h-5 w-5 transition-transform" />
</CollapsibleTrigger>
<CollapsibleContent className="px-6 py-4 text-muted-foreground">
At only $10 per person, The Reservationist is not only the fastest,
but the cheapest way to secure your exclusive reservations. If you think about it,
that’s only a small fraction of what your total bill will be! </CollapsibleContent>
</Collapsible>
<Collapsible>
<CollapsibleTrigger className="flex w-full items-center justify-
between rounded-lg bg-muted px-6 py-4 font-medium transition-colors hover:bg-
muted/80">
<span>Why was I charged by The Reservationist and RESY?</span>
<ChevronDownIcon className="h-5 w-5 transition-transform" />
</CollapsibleTrigger>
<CollapsibleContent className="px-6 py-4 text-muted-foreground">
Sometimes Restaurants require you to put a deposit on your
reservation. In this case, you will be charged $10 per person by The Reservationist
and whatever the deposit is by the restaurant. Please check Resy’s terms and
policies for any additional questions related to Resy deposits.
</CollapsibleContent>
</Collapsible>
<Collapsible>
<CollapsibleTrigger className="flex w-full items-center justify-
between rounded-lg bg-muted px-6 py-4 font-medium transition-colors hover:bg-
muted/80">
<span>Who do I reach out to for more questions?</span>
<ChevronDownIcon className="h-5 w-5 transition-transform" />
</CollapsibleTrigger>
<CollapsibleContent className="px-6 py-4 text-muted-foreground">
Please reach out to us by sending an email to
SOS@thereservationist.com.
</CollapsibleContent>
</Collapsible>
</div>
</section>
<Footer />
</main>
</div>
);
}

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy