diff --git a/db.json b/db.json index 9b3b7ed..4a0fa40 100644 --- a/db.json +++ b/db.json @@ -1,5 +1,5 @@ { - "bookingSeats": [ + "seatLayout": [ [ 0, "A1", @@ -101,20 +101,24 @@ { "mobile": "9999999997", "id": 3 + }, + { + "mobile": "3333333333", + "id": 4 } ], "reservedSeats": [ { "seats": [ - "F4" + "H5" ], "id": 1 }, { "seats": [ "H2", - "H3", - "H1" + "H1", + "H4" ], "id": 2 }, @@ -126,6 +130,12 @@ "G4" ], "id": 3 + }, + { + "seats": [ + "G8" + ], + "id": 4 } ] } \ No newline at end of file diff --git a/src/App.js b/src/App.js index d7dd5d2..c41c87d 100644 --- a/src/App.js +++ b/src/App.js @@ -1,23 +1,28 @@ import "./App.css"; -import { BrowserRouter as Router, Route, Routes } from "react-router-dom"; -import { _Login } from "../src/pages/login"; -import { _Seatlayout } from "../src/pages/seat-layout"; -import { _Confirmation } from "../src/pages/confirmation"; -import { _seatLimit } from "../src/pages/seat-limit"; - +import { Route, Routes } from "react-router-dom"; +import { LoginPage } from "../src/pages/login"; +import { SeatLayoutPage } from "../src/pages/seat-layout"; +import { ConfirmationPage } from "../src/pages/confirmation"; +import { SeatLimitPage } from "../src/pages/seat-limit"; +import { useEffect } from "react"; +import { useNavigate } from "react-router-dom"; function App() { + const navigate = useNavigate(); + useEffect(() => { + const userIsLoggedIn = sessionStorage.getItem("userId"); + userIsLoggedIn ?? navigate("/"); + // Navigate to the booking if the user is logged in + }, [navigate]); return ( <div className="App"> - <Router> - <Routes> - <Route path="/" element={<_Login />} /> - <Route path="/seat-limit" element={<_seatLimit />} /> - <Route path="/booking" element={<_Seatlayout />} /> - <Route path="/confirmation" element={<_Confirmation />} /> - {/* Default route */} - <Route path="*" element={<_Login />} /> - </Routes> - </Router> + <Routes> + <Route path="/" element={<LoginPage />} /> + <Route path="/seat-limit" element={<SeatLimitPage />} /> + <Route path="/booking" element={<SeatLayoutPage />} /> + <Route path="/confirmation" element={<ConfirmationPage />} /> + {/* Default route */} + <Route path="*" element={<LoginPage />} /> + </Routes> </div> ); } diff --git a/src/components/top-level/confirmation/index.js b/src/components/top-level/confirmation/index.js index 32fb83a..1a61e46 100644 --- a/src/components/top-level/confirmation/index.js +++ b/src/components/top-level/confirmation/index.js @@ -4,7 +4,7 @@ import style from "./confirmation.module.css"; import Button from "../../base/button"; import { Link } from "react-router-dom"; -function Confirmation({ seatCount, seats, onLogOut, onEdit }) { +function Confirmation({ seatCount = 0, seats = 0, onLogOut, onEdit }) { return ( <Layout title="Booking Confirmed"> <div className={style.confirmation}> diff --git a/src/components/top-level/login/index.js b/src/components/top-level/login/index.js index ed2daa5..b07dede 100644 --- a/src/components/top-level/login/index.js +++ b/src/components/top-level/login/index.js @@ -1,5 +1,4 @@ import { useState } from "react"; - import Button from "../../base/button"; import Input from "../../base/input"; import Layout from "../../base/layout"; @@ -12,7 +11,6 @@ function Login({ onSubmit }) { e.preventDefault(); if (mobile.length < 10) { setIsError(true); - toast.info("mobile should be 10 digits"); } else { onSubmit({ mobile }); @@ -26,21 +24,19 @@ function Login({ onSubmit }) { return ( <Layout title="Login"> - <form className="form" onSubmit={handleSubmit}> - <div> - <label className="label">Mobile:</label> - <Input - placeholder="Enter Mobile" - type="tel" - required - maxLength={10} - onChange={handleChange} - /> - {iserror && ( - <span className={styles.span}>Please enter 10 digits</span> - )} + <form className={styles.form} onSubmit={handleSubmit}> + <label className={styles.label}>Mobile:</label> + <Input + placeholder="Enter Mobile" + type="tel" + required + maxLength={10} + onChange={handleChange} + /> + {iserror && <span className={styles.span}>Please enter 10 digits</span>} + <div className={styles.button}> + <Button>Submit</Button> </div> - <Button>Submit</Button> </form> </Layout> ); diff --git a/src/components/top-level/login/login.module.css b/src/components/top-level/login/login.module.css index 62571f7..689f66b 100644 --- a/src/components/top-level/login/login.module.css +++ b/src/components/top-level/login/login.module.css @@ -1,3 +1,13 @@ .span { color: red; } +.form { + margin-inline: 1rem; +} +.button { + margin-top: 1rem; +} +.label { + color: #6c6a6a; + font-weight: 500; +} diff --git a/src/components/top-level/seat-layout/index.js b/src/components/top-level/seat-layout/index.js index 1e249ca..be5fb80 100644 --- a/src/components/top-level/seat-layout/index.js +++ b/src/components/top-level/seat-layout/index.js @@ -14,6 +14,11 @@ function SeatLayout({ isLoading, }) { const [selectedseat, setSelectedSeat] = useState([...selectedSeats]); + + useEffect(() => { + setSelectedSeat([...selectedSeats]); + }, [selectedSeats]); + const handleSeats = (seat) => { if (!selectedseat?.includes(seat) && selectedseat.length < seatLimit) { setSelectedSeat([...selectedseat, seat]); @@ -23,9 +28,7 @@ function SeatLayout({ toast.info(`Cannot select seat more than ${seatLimit}`); } }; - useEffect(() => { - setSelectedSeat([...selectedSeats]); - }, [selectedSeats]); + const handleConfirm = (e) => { e.preventDefault(); if (!selectedseat.length || selectedseat.length < seatLimit) { diff --git a/src/components/top-level/seat-limit/index.js b/src/components/top-level/seat-limit/index.js index 3202f06..cf4ec70 100644 --- a/src/components/top-level/seat-limit/index.js +++ b/src/components/top-level/seat-limit/index.js @@ -3,6 +3,7 @@ import Button from "../../base/button"; import Layout from "../../base/layout"; import Input from "../../base/input"; import { toast } from "react-toastify"; +import styles from "./seat-limit.module.css"; function SeatLimit({ onLimit, ...props }) { const [limit, setLimit] = useState(0); const handleLimit = (e) => { @@ -15,18 +16,18 @@ function SeatLimit({ onLimit, ...props }) { }; return ( <Layout title="How many seats?"> - <form onSubmit={handleLimit} className="form"> - <div> - <label className="label">No of seats:</label> - <Input - type="number" - value={limit} - onChange={(e) => setLimit(e.target.value)} - min="1" - max="10" - /> + <form onSubmit={handleLimit} className={styles.form}> + <label className="label">No of seats:</label> + <Input + type="number" + value={limit} + onChange={(e) => setLimit(e.target.value)} + min="1" + max="10" + /> + <div className={styles.button}> + <Button {...props}>Book Seats</Button> </div> - <Button {...props}>Book Seats</Button> </form> </Layout> ); diff --git a/src/components/top-level/seat-limit/seat-limit.module.css b/src/components/top-level/seat-limit/seat-limit.module.css index af54844..9170102 100644 --- a/src/components/top-level/seat-limit/seat-limit.module.css +++ b/src/components/top-level/seat-limit/seat-limit.module.css @@ -1,14 +1,10 @@ -/* .label { - color: #6c6a6a; +.form { + margin-inline: 1rem; } - -.form > *:not(input) { - padding: 17px; +.button { + margin-top: 1rem; } - -input[type="number"]::-webkit-inner-spin-button { - display: none; +.label { + color: #6c6a6a; + font-weight: 500; } -input[type="number"] { - -moz-appearence: textfield; -} */ diff --git a/src/components/top-level/seat/seat.module.css b/src/components/top-level/seat/seat.module.css index 9df1019..29738b2 100644 --- a/src/components/top-level/seat/seat.module.css +++ b/src/components/top-level/seat/seat.module.css @@ -11,20 +11,22 @@ input[type="checkbox"] { border: 1px solid #ccc; box-shadow: none; padding: 0.7rem; - cursor: pointer; } .checkbox-reserved { background: #848489; border: none !important; + cursor: not-allowed; } .checkbox-selected { background: #01fff7; border: none !important; + cursor: pointer; } .checkbox-available { padding: 0.6rem !important; + cursor: pointer; } .tooltip { diff --git a/src/_context/index.js b/src/context/index.js similarity index 52% rename from src/_context/index.js rename to src/context/index.js index 5ed8f42..e9d5a9a 100644 --- a/src/_context/index.js +++ b/src/context/index.js @@ -5,61 +5,86 @@ export const AppContext = createContext(); export const useAppContext = () => useContext(AppContext); export function ContextProvider({ children }) { - const [userseats, setUserSeats] = useState([]); - const [seats, setSeats] = useState([]); + const [userSeats, setUserSeats] = useState([]); + const [reservedSeats, setReservedSeats] = useState([]); + const [seatLayout, setSeatLayout] = useState([]); const [isLoading, setisLoading] = useState(false); - const LoginOrRegister = async ({ mobile }) => { + + const loginOrRegister = async ({ mobile }) => { try { - const user = await api.GetUserByMobile({ mobile }); + const user = await api.getUserByMobile({ mobile }); if (!user.length) { //register - const response = await api.Register({ mobile }); - return await response; + const response = await api.register({ mobile }); + sessionStorage.setItem("userId", response?.id); + await getAllReservedSeats(); + // return true for newly registered user + return true; } else { //login - return await user[0]; + sessionStorage.setItem("userId", user?.[0]?.id); + const response = await getAllReservedSeats(); + const userSelectedSeats = response.filter((e) => e.id == user[0]?.id); + sessionStorage.setItem( + "seatLimit", + userSelectedSeats?.[0]?.seats?.length + ); + // return false for already registered user + return false; } } catch (err) { toast.error(err.message); } }; - const AddOrUpdateSeats = async ({ id, seats }) => { + const addOrUpdateSeats = async ({ id, seats }) => { try { - const isSeats = userseats.filter((e) => e.id == id); - //check the id - if (!isSeats.length) { - //add seats - const response = await api.AddSeats({ seats }); - setUserSeats([...userseats, response]); + //check user already booked seats or not + if (!userSeats.length) { + //add seats into reserved seats + const response = await api.addSeats({ seats }); + setReservedSeats([...reservedSeats, response]); } else { - //update seats - const response = await api.UpdateSeats({ id, seats }); - const result = userseats.map((e) => (e[id] == id ? response : e)); - setUserSeats(result); + //update user seats only + const response = await api.updateSeats({ id, seats }); + setUserSeats(response.seats); } } catch (err) { toast.error(err.message); } }; - const GetAllSeats = async () => { + const getAllReservedSeats = async () => { setisLoading(false); try { //to clear the data for glitch + setReservedSeats([]); setUserSeats([]); - const response = await api.GetAllSeats(); - setUserSeats(response); + const response = await api.getAllReservedSeats(); + const userid = sessionStorage.getItem("userId"); + + response.map((e) => + e.id != userid + ? // condition1=get all reserved seats except logged user + setReservedSeats((prevReservedSeat) => [ + ...prevReservedSeat, + ...e.seats, + ]) + : // condition2=get only reserved seats of logged user + setUserSeats(e.seats) + ); + setisLoading(true); + return await response; } catch (err) { toast.error(err.message); } }; - const GetSeats = async () => { + const getSeatLayout = async () => { setisLoading(false); try { - setSeats([]); - const response = await api.GetSeats(); - setSeats(response); + setSeatLayout([]); + const response = await api.getSeatLayout(); + setSeatLayout(response); setisLoading(true); } catch (err) { toast.error(err.message); @@ -67,13 +92,14 @@ export function ContextProvider({ children }) { }; const value = { - userseats, + userSeats, + reservedSeats, isLoading, - seats, - LoginOrRegister, - AddOrUpdateSeats, - GetAllSeats, - GetSeats, + seatLayout, + loginOrRegister, + addOrUpdateSeats, + getAllReservedSeats, + getSeatLayout, }; return <AppContext.Provider value={value}>{children}</AppContext.Provider>; } diff --git a/src/index.css b/src/index.css index e4386c2..164a1d5 100644 --- a/src/index.css +++ b/src/index.css @@ -18,13 +18,6 @@ code { monospace; } -.label { - color: #6c6a6a; -} - -.form > *:not(input) { - padding: 17px; -} input[type="number"]::-webkit-inner-spin-button { display: none; } diff --git a/src/index.js b/src/index.js index 2a47a6b..6879271 100644 --- a/src/index.js +++ b/src/index.js @@ -3,27 +3,30 @@ import ReactDOM from "react-dom/client"; import "./index.css"; import App from "./App"; import reportWebVitals from "./reportWebVitals"; -import { ContextProvider } from "./_context"; +import { ContextProvider } from "./context"; import { ToastContainer } from "react-toastify"; import "react-toastify/dist/ReactToastify.css"; +import { BrowserRouter as Router } from "react-router-dom"; const root = ReactDOM.createRoot(document.getElementById("root")); root.render( - <ContextProvider> - <App /> + <Router> + <ContextProvider> + <App /> - <ToastContainer - position="top-right" - autoClose={3000} - hideProgressBar={false} - newestOnTop={false} - closeOnClick - rtl={false} - pauseOnFocusLoss - draggable - pauseOnHover - theme="colored" - /> - </ContextProvider> + <ToastContainer + position="top-right" + autoClose={3000} + hideProgressBar={false} + newestOnTop={false} + closeOnClick + rtl={false} + pauseOnFocusLoss + draggable + pauseOnHover + theme="colored" + /> + </ContextProvider> + </Router> ); // If you want to start measuring performance in your app, pass a function diff --git a/src/lib/api.js b/src/lib/api.js index 4c8b138..2726d87 100644 --- a/src/lib/api.js +++ b/src/lib/api.js @@ -1,13 +1,13 @@ const BASE_URL = process.env.REACT_APP_BASE_URL; -export async function Login({ mobile }) { +export async function login({ mobile }) { // Attempt to fetch user data const response = await fetch(`${BASE_URL}/users?mobile=${mobile}`); const result = await response.json(); return result; } -export async function Register({ mobile }) { +export async function register({ mobile }) { // User not found, register const user = await fetch(`${BASE_URL}/users`, { method: "POST", @@ -18,8 +18,9 @@ export async function Register({ mobile }) { return await user.json(); } -export async function AddSeats({ id, seats }) { +export async function addSeats({ id, seats }) { //add seat + //here id = userid const response = await fetch(`${BASE_URL}/reservedSeats `, { method: "POST", headers: { "Content-Type": "application/json" }, @@ -28,7 +29,7 @@ export async function AddSeats({ id, seats }) { return await response.json(); } -export async function UpdateSeats({ id, seats }) { +export async function updateSeats({ id, seats }) { //update const response = await fetch(`${BASE_URL}/reservedSeats/${id}`, { method: "PUT", @@ -38,21 +39,17 @@ export async function UpdateSeats({ id, seats }) { return await response.json(); } -export async function GetAllSeats() { +export async function getAllReservedSeats() { const response = await fetch(`${BASE_URL}/reservedSeats`); return await response.json(); } -export async function GetSeats() { - const response = await fetch(`${BASE_URL}/bookingSeats`); +export async function getSeatLayout() { + const response = await fetch(`${BASE_URL}/seatLayout`); return await response.json(); } -export async function GetUserByMobile({ mobile }) { +export async function getUserByMobile({ mobile }) { const response = await fetch(`${BASE_URL}/users?mobile=${mobile}`); return await response.json(); } -export async function GetSeatById(id) { - const response = await fetch(`${BASE_URL}/reservedSeats/${id}`); - return await response.json(); -} diff --git a/src/pages/confirmation.js b/src/pages/confirmation.js index 9f5cc38..7a2142e 100644 --- a/src/pages/confirmation.js +++ b/src/pages/confirmation.js @@ -1,38 +1,22 @@ import Confirmation from "../components/top-level/confirmation"; -import { useLocation, useNavigate } from "react-router-dom"; -import { useEffect, useState } from "react"; -import { useAppContext } from "../_context"; +import { useNavigate } from "react-router-dom"; +import { useAppContext } from "../context"; +import { useLocation } from "react-router-dom"; -export function _Confirmation() { - const [count, setCount] = useState(0); - const [reservedseat, setReservedSeat] = useState([]); - let { GetSeatById } = useAppContext(); +export function ConfirmationPage() { + let { getAllReservedSeats } = useAppContext(); const navigate = useNavigate(); const location = useLocation(); - useEffect(() => { - getSeatById(); - }, []); - const getSeatById = async () => { - if (location?.state?.seats.length) { - setCount(location.state.seats.length); - setReservedSeat(location.state.seats); - } else { - const userid = sessionStorage.getItem("userId"); - const userseats = await GetSeatById(userid); - setCount(userseats.seats.length); - setReservedSeat(userseats.seats); - } - }; - - const handleModify = (e) => { + const handleModify = async (e) => { e.preventDefault(); + await getAllReservedSeats(); navigate("/booking"); }; return ( <Confirmation - seatCount={count} - seats={reservedseat} + seatCount={location?.state?.seats?.length} + seats={location?.state?.seats} onEdit={handleModify} /> ); diff --git a/src/pages/login.js b/src/pages/login.js index d2e6468..244d244 100644 --- a/src/pages/login.js +++ b/src/pages/login.js @@ -1,38 +1,22 @@ -import { useEffect, useState } from "react"; -import { useAppContext } from "../_context"; +import { useEffect } from "react"; +import { useAppContext } from "../context"; import Login from "../components/top-level/login"; import { useNavigate } from "react-router-dom"; -export function _Login() { +export function LoginPage() { const navigate = useNavigate(); - const { userseats, LoginOrRegister, GetAllSeats } = useAppContext(); - const [isLogged, setIsLogged] = useState(false); + const { loginOrRegister } = useAppContext(); useEffect(() => { sessionStorage.clear(); }, []); - useEffect(() => { - isLogged && handleSeats(); - }, [isLogged]); - const handleLogin = async ({ mobile }) => { - //get all seats and then login - const users = await LoginOrRegister({ mobile }); - sessionStorage.setItem("userId", users?.id); - - await GetAllSeats(); - setIsLogged(true); - }; - const handleSeats = async () => { - const id = sessionStorage.getItem("userId"); - const userReservedSeats = userseats.filter((e) => e.id == id); - if (!userReservedSeats.length) { + const isRegistered = await loginOrRegister({ mobile }); + if (isRegistered) { navigate("/seat-limit"); } else { - sessionStorage.setItem("seatLimit", userReservedSeats[0].seats.length); - navigate("/booking", { state: { seats: userseats } }); + navigate("/booking"); } }; - return <Login onSubmit={handleLogin} />; } diff --git a/src/pages/seat-layout.js b/src/pages/seat-layout.js index 84d0901..fea948c 100644 --- a/src/pages/seat-layout.js +++ b/src/pages/seat-layout.js @@ -1,72 +1,31 @@ -import { useEffect, useState } from "react"; +import { useEffect } from "react"; import SeatLayout from "../components/top-level/seat-layout"; -import { useAppContext } from "../_context"; -import { useLocation, useNavigate } from "react-router-dom"; +import { useAppContext } from "../context"; +import { useNavigate } from "react-router-dom"; -export function _Seatlayout() { - let { userseats, isLoading, seats, AddOrUpdateSeats, GetAllSeats, GetSeats } = - useAppContext(); +export function SeatLayoutPage() { + let { + userSeats, + reservedSeats, + isLoading, + seatLayout, + addOrUpdateSeats, + getAllReservedSeats, + getSeatLayout, + } = useAppContext(); - const [reservedSeat, setReservedSeat] = useState([]); - const [selectedSeat, setSelectedSeat] = useState([]); const navigate = useNavigate(); const userId = sessionStorage.getItem("userId"); const seatLimit = sessionStorage.getItem("seatLimit"); - const location = useLocation(); - useEffect(() => { - GetSeats(); - if (!location?.state?.seats.length) { - GetAllSeats(); - } - const handleBeforeUnload = (event) => { - // Check if the page is about to unload or refresh - if (event.type === "beforeunload") { - navigate("/booking", { replace: true }); - } - }; - window.addEventListener("beforeunload", handleBeforeUnload); - return () => { - window.removeEventListener("beforeunload", handleBeforeUnload); - }; - }, []); - // useEffect(() => { - // GetSeats(); - // const handleBeforeUnload = (event) => { - // // Check if the page is about to unload or refresh - // if (event.type === "beforeunload") { - // sessionStorage.setItem("pageload", true); - // } - // }; - // window.addEventListener("beforeunload", handleBeforeUnload); - // return () => { - // window.removeEventListener("beforeunload", handleBeforeUnload); - // }; - // }, []); - // useEffect(() => { - // const reloadPage = sessionStorage.getItem("pageload"); - // reloadPage && GetAllSeats(); - // }, []); useEffect(() => { - handleReservedSeats(); - }, [userseats]); - - const handleReservedSeats = async () => { - let seatArray = []; - const selectedSeats = await userseats?.find((e) => e.id == userId); - const results = selectedSeats?.seats; - setSelectedSeat(results); - const reservedSeats = userseats?.filter((e) => e.id != userId); - reservedSeats?.forEach((element) => { - element?.seats.forEach((e) => { - seatArray = [...seatArray, e]; - setReservedSeat(seatArray); - }); - }); - }; + //it will check only on page refresh ,at the time of page refresh it will be empty + !reservedSeats.length && getAllReservedSeats(); + getSeatLayout(); + }, []); const handleConfirmed = async ({ id, seats }) => { - await AddOrUpdateSeats({ id, seats }); + await addOrUpdateSeats({ id, seats }); navigate("/confirmation", { state: { seats: seats } }); }; @@ -75,9 +34,9 @@ export function _Seatlayout() { isLoading={isLoading} userId={userId} seatLimit={seatLimit} - seats={seats} - selectedSeats={selectedSeat} - reservedSeats={reservedSeat} + seats={seatLayout} + selectedSeats={userSeats} + reservedSeats={reservedSeats} onClick={handleConfirmed} /> ); diff --git a/src/pages/seat-limit.js b/src/pages/seat-limit.js index f315ce3..46230b6 100644 --- a/src/pages/seat-limit.js +++ b/src/pages/seat-limit.js @@ -1,12 +1,10 @@ import SeatLimit from "../components/top-level/seat-limit"; import { useNavigate } from "react-router-dom"; -export function _seatLimit() { +export function SeatLimitPage() { const navigate = useNavigate(); const handleLimit = (e) => { sessionStorage.setItem("seatLimit", e.limit); - //for Auth Guard - sessionStorage.setItem("currentPath", "/booking"); navigate("/booking"); }; return <SeatLimit onLimit={handleLimit} />; diff --git a/src/pages/seed.js b/src/pages/seed.js deleted file mode 100644 index 19b1555..0000000 --- a/src/pages/seed.js +++ /dev/null @@ -1,10 +0,0 @@ -export const bookingSeats = [ - [0, "A1", "A2", "A3", 0, "A4", "A5", "A6", 0], - ["B1", "B2", "B3", "B4", 0, "B5", "B6", "B7", "B8"], - ["C1", "C2", "C3", "C4", 0, "C5", "C6", "C7", "C8"], - ["D1", "D2", "D3", "D4", 0, "D5", "D6", "D7", "D8"], - ["E1", "E2", "E3", "E4", 0, "E5", "E6", "E7", "E8"], - ["F1", "F2", "F3", "F4", 0, "F5", "F6", "F7", "F8"], - ["G1", "G2", "G3", "G4", 0, "G5", "G6", "G7", "G8"], - [0, "H1", "H2", "H3", 0, "H4", "H5", "H7", 0], -];