Commit 0978e293 by Syed Abdul Rahman

seat booking functionality implemented

parent afaedd02
{ {
"users": [ "users": [
{ {
"id": "2297", "id": "8329",
"email": "syedabdul.rahman@krds.com" "email": "syedabdul.rahman@krds.com",
"selectionCount": "3"
}, },
{ {
"id": "aa8d", "id": "3450",
"email": "abdul@gmail.com" "email": "abdul@gmail.com",
"selectionCount": "4"
}, },
{ {
"id": "6dc7", "id": "06b5",
"email": "test@krds.com" "email": "lucy@gmail.com",
"selectionCount": "3"
}, },
{ {
"id": "4422", "id": "92dd",
"email": "test@krds.fr" "email": "",
}, "selectionCount": "3"
{
"id": "6c91",
"email": "anas@gmail.com"
},
{
"id": "68fd",
"email": "lucy@gmail.com"
} }
], ],
"selectedSeats": [ "selectedSeats": [
{ {
"id": "4422", "id": "8329",
"userid": "4422", "userid": "8329",
"selected": [ "selected": []
{
"row": 5,
"seat": 5
},
{
"row": 5,
"seat": 6
},
{
"row": 4,
"seat": 7
},
{
"row": 4,
"seat": 8
},
{
"row": 6,
"seat": 7
}
]
} }
] ]
} }
\ No newline at end of file
import { Routes, Route, Navigate } from 'react-router-dom'; import { Routes, Route, Navigate } from 'react-router-dom';
import Login from './pages/LoginPage/Index'; import Login from './pages/LoginPage/Index';
// import SeatBooking from './components/TopLevel/SeatBooking/Index'; import BookingPage from './pages/BookingPage/Index';
import { getItem } from '../src/lib/localStorage'; import { getItem } from '../src/lib/localStorage';
const Navigates = () => { const Navigates = () => {
const isAuthenticated = getItem('user');
const RequireAuth = ({ children }) => { const RequireAuth = ({ children }) => {
const isAuthenticated = getItem('user') ? true : false;
return isAuthenticated ? children : <Navigate to="/login" replace />; return isAuthenticated ? children : <Navigate to="/login" replace />;
}; };
...@@ -16,8 +16,7 @@ const Navigates = () => { ...@@ -16,8 +16,7 @@ const Navigates = () => {
path="/" path="/"
element={ element={
<RequireAuth> <RequireAuth>
{/* <SeatBooking /> */} <BookingPage />
<div>SEAT BOOKING</div>
</RequireAuth> </RequireAuth>
} }
/> />
......
...@@ -69,12 +69,19 @@ const BookingWrapper = ({ ...@@ -69,12 +69,19 @@ const BookingWrapper = ({
); );
if (found) { if (found) {
seatClass += 'current-selected'; seatClass += 'current-selected';
} } else {
if (selectedBy) { if (selectedBy) {
seatClass += console.log(
selectedBy == currentUser selectedBy == currentUser,
? 'selected-by-me' selectedBy,
: 'selected-by-other'; currentUser,
'FINAL'
);
seatClass +=
selectedBy == currentUser
? 'selected-by-me'
: 'selected-by-other';
}
} }
return ( return (
......
import styles from './styles.module.css' import styles from './styles.module.css';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
const Seat = ({ id,status, ...rest }) => { const Seat = ({ id, status, ...rest }) => {
return ( return <div {...rest} className={` ${styles[status]} ${styles.seat} `}></div>;
<div {...rest} className={` ${styles[status]} ${styles.seat} `}></div> };
)
}
Seat.propTypes = { Seat.propTypes = {
onClick: PropTypes.func.isRequired, onClick: PropTypes.func.isRequired
// status: PropTypes.oneOf(['available', 'reserved', 'selected']) // status: PropTypes.oneOf(['available', 'reserved', 'selected'])
} };
export default Seat; export default Seat;
\ No newline at end of file
.seat { .seat {
width: 1.5em; width: 1.5em;
height: 1.5em; height: 1.5em;
border: 1.5px solid rgba(255, 255, 255, 0.825); border: 1.5px solid rgba(255, 255, 255, 0.825);
border-radius: 5px; border-radius: 5px;
cursor: pointer; cursor: pointer;
} }
.seat:hover { .seat:hover {
background-color: aqua; background-color: aqua;
} }
.reserved { .reserved {
background-color: rgba(211, 208, 208, 0.181); background-color: rgba(211, 208, 208, 0.181);
pointer-events: none; pointer-events: none;
border: unset; border: unset;
} }
.selected { .selected {
background-color: aqua; background-color: aqua;
} }
.theatre { .theatre {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
flex-direction: column; flex-direction: column;
gap: 10px; gap: 10px;
} }
.seat-row { .seat-row {
display: flex; display: flex;
gap: 10px; gap: 10px;
} }
.aisle { .aisle {
width: 1em; width: 1em;
} }
.seat-gap { .seat-gap {
width: 1em; width: 1em;
height: 1em; height: 1em;
margin: 0; margin: 0;
visibility: hidden; visibility: hidden;
} }
.selected-by-me { .selected-by-me {
background-color: aqua !important; background-color: aqua;
border: 1px solid aqua; border: 1px solid aqua;
} }
.current-selected{ .current-selected {
background-color: aqua !important; background-color: aqua !important;
border: 1px solid aqua; border: 1px solid aqua;
} }
.selected-by-other { .selected-by-other {
background-color: #bec0c26d !important; background-color: #bec0c26d !important;
border: 1px solid #bec0c26d; border: 1px solid #bec0c26d;
pointer-events: none; pointer-events: none;
} }
.booking-wrapper{ .booking-wrapper {
width: 100%; width: 100%;
} }
@media screen and (min-width: 768px) { @media screen and (min-width: 768px) {
.seat {
.seat { width: 1.8em;
width: 1.8em; height: 1.8em;
height: 1.8em; }
}
.aisle {
.aisle { width: 2em;
width: 2em; }
}
.seat-gap {
.seat-gap { width: 2em;
width: 2em; height: 2em;
height: 2em; }
}
.seat-row {
.seat-row { gap: 15px;
gap: 15px; }
}
.theatre {
.theatre { gap: 15px;
gap: 15px; }
} }
}
\ No newline at end of file
...@@ -8,7 +8,6 @@ const Login = ({ onSubmit }) => { ...@@ -8,7 +8,6 @@ const Login = ({ onSubmit }) => {
e.preventDefault(); e.preventDefault();
const formData = new FormData(e.target); const formData = new FormData(e.target);
const data = { ...Object.fromEntries(formData) }; const data = { ...Object.fromEntries(formData) };
console.log(data?.email);
onSubmit(data?.email); onSubmit(data?.email);
}; };
......
...@@ -18,7 +18,7 @@ export const createUser = async (email) => { ...@@ -18,7 +18,7 @@ export const createUser = async (email) => {
return data; return data;
}; };
export const getSelectedSeats = async () => { export const getSelectedSeatsApi = async () => {
const response = await fetch(`${appConstants.API_URL_SEATS}`); const response = await fetch(`${appConstants.API_URL_SEATS}`);
const data = await response.json(); const data = await response.json();
return data; return data;
...@@ -47,3 +47,19 @@ export const createSeatApi = async (body) => { ...@@ -47,3 +47,19 @@ export const createSeatApi = async (body) => {
body: JSON.stringify(body) body: JSON.stringify(body)
}); });
}; };
export const saveSeatSelectionCountApi = async (id, count) => {
await fetch(`${appConstants.API_URL_USERS}/${id}`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ selectionCount: count })
});
};
export const getSeatSelectionCountApi = async (id) => {
const response = await fetch(`${appConstants.API_URL_USERS}/${id}`);
const data = await response.json();
return data ? data : null;
};
...@@ -8,5 +8,5 @@ export const getItem = (key) => { ...@@ -8,5 +8,5 @@ export const getItem = (key) => {
}; };
export const reset = () => { export const reset = () => {
localStorage.clear(); localStorage.clear();
} };
\ No newline at end of file
import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { getItem, reset, setItem } from '../../lib/localStorage';
import { appConstants } from '../../lib/AppConstants';
import {
getSelectedSeatsApi,
editSeatsApi,
createSeatApi,
saveSeatSelectionCountApi,
getSeatSelectionCountApi
} from '../../lib/api';
import BookingWrapper from '../../components/Layout/BookingWrapper/Index';
import Header from '../../components/Layout/Header/Index';
import Modal from '../../components/Shared/Modal/Index';
import Button from '../../components/Base/Button/Index';
import LegendWrapper from '../../components/TopLevel/LegendWrapper/Index';
import img from '../../assets/images/power-button_12080802.png';
import styles from './styles.module.css';
const BookingPage = () => {
let userId = getItem(appConstants.USER);
const navigate = useNavigate();
const [selectedSeats, setSelectedSeats] = useState(); //persisted seats state
const [currentSeats, setCurrentSeats] = useState([]); // currently selecting seats before booking(local)
const [showSeatsModal, setShowSeatsModal] = useState(
getItem(appConstants.MODAL)
);
const [showSeatsConfirmationModal, setShowSeatsConfirmationModal] =
useState(false);
const [noOfSeats, setNoOfSeats] = useState();
useEffect(() => {
const fetchSelectedSeats = async () => {
const data = await getSelectedSeatsApi();
const currentData = data.find((e) => e.userid == userId);
setSelectedSeats(data);
setCurrentSeats(currentData.selected);
};
const fetchSeatSelectionCount = async () => {
const data = await getSeatSelectionCountApi(userId);
setNoOfSeats(data?.selectionCount);
};
fetchSelectedSeats();
fetchSeatSelectionCount();
setShowSeatsModal(getItem(appConstants.MODAL));
}, []);
const data = [
{
row_id: 1,
columnData: [
{
id: 0,
status: 'aisle'
},
{
id: 1
},
{
id: 2
},
{
id: 3
},
{
id: 0
},
{
id: 4
},
{
id: 5
},
{
id: 6
},
{
id: 0,
status: 'aisle'
}
]
},
{
row_id: 2,
columnData: [
{
id: 1,
status: 'available'
},
{
id: 2,
status: 'available'
},
{
id: 3,
status: 'available'
},
{
id: 4,
status: 'available'
},
{
id: 0,
status: 'aisle'
},
{
id: 5,
status: 'available'
},
{
id: 6,
status: 'available'
},
{
id: 7,
status: 'available'
},
{
id: 8,
status: 'available'
}
]
},
{
row_id: 3,
columnData: [
{
id: 1
},
{
id: 2
},
{
id: 3
},
{
id: 4
},
{
id: 0
},
{
id: 5
},
{
id: 6
},
{
id: 7
},
{
id: 8
}
]
},
{
row_id: 4,
columnData: [
{
id: 1
},
{
id: 2
},
{
id: 3
},
{
id: 4
},
{
id: 0
},
{
id: 5
},
{
id: 6
},
{
id: 7
},
{
id: 8
}
]
},
{
row_id: 5,
columnData: [
{
id: 1
},
{
id: 2
},
{
id: 3
},
{
id: 4
},
{
id: 0
},
{
id: 5
},
{
id: 6
},
{
id: 7
},
{
id: 8
}
]
},
{
row_id: 6,
columnData: [
{
id: 1
},
{
id: 2
},
{
id: 3
},
{
id: 4
},
{
id: 0
},
{
id: 5
},
{
id: 6
},
{
id: 7
},
{
id: 8
}
]
},
{
row_id: 7,
columnData: [
{
id: 1
},
{
id: 2
},
{
id: 3
},
{
id: 4
},
{
id: 0
},
{
id: 5
},
{
id: 6
},
{
id: 7
},
{
id: 8
}
]
},
{
row_id: 8,
columnData: [
{
id: 0,
status: 'aisle'
},
{
id: 1
},
{
id: 2
},
{
id: 3
},
{
id: 0
},
{
id: 4
},
{
id: 5
},
{
id: 6
},
{
id: 0,
status: 'aisle'
}
]
}
];
const logout = () => {
reset();
navigate('/login');
};
const onModalChange = (value) => {
setNoOfSeats(value);
};
const modalClose = () => {
setItem('modal', false);
if (userId && noOfSeats) {
saveSeatSelectionCountApi(userId, noOfSeats);
}
setShowSeatsModal(false);
};
const finalCancel = () => {
const current = selectedSeats?.find((ele) => ele.userid == userId);
const mergedSeats = [
...currentSeats.filter(
(cs) =>
!current?.selected.some(
(ss) => ss.row === cs.row && ss.seat === cs.seat
)
),
...current?.selected.filter(
(ss) =>
!currentSeats.some((cs) => cs.row === ss.row && cs.seat === ss.seat)
)
];
setCurrentSeats(mergedSeats);
setShowSeatsConfirmationModal(false);
};
const onSelectSeats = (rowId, columnId) => {
const currentData = data.find((e) => e.userid == userId);
const currentSelected = currentData.selected;
let totalAllowedCount = currentSelected.length + currentSeats.length;
setCurrentSeats((prev) => {
const found = prev.find((e) => e.row == rowId && e.seat == columnId);
if (found) {
return currentSeats.filter(
(e) => !(e.row == rowId && e.seat == columnId)
);
} else {
if (totalAllowedCount < noOfSeats) {
if (currentSeats.length < noOfSeats) {
let obj = {
row: rowId,
seat: columnId
};
return [...prev, obj];
} else {
return [...prev];
}
}
}
});
};
const onSeatsConfirm = () => {
const current = selectedSeats?.find((ele) => ele.userid == userId);
const found = current?.selected.some((e) =>
currentSeats.some((e2) => e2.row == e.row && e2.seat == e.seat)
);
if (found) {
const mergedSeats = [
...currentSeats.filter(
(cs) =>
!current?.selected.some(
(ss) => ss.row === cs.row && ss.seat === cs.seat
)
),
...current?.selected.filter(
(ss) =>
!currentSeats.some((cs) => cs.row === ss.row && cs.seat === ss.seat)
)
];
setCurrentSeats(mergedSeats);
} else {
if (current) {
setCurrentSeats([...currentSeats, ...current.selected]);
} else {
setCurrentSeats([...currentSeats]);
}
}
setShowSeatsConfirmationModal(true);
};
const seatsFinal = async () => {
const existingUserSeatMap = selectedSeats?.find((ele) => ele.id == userId);
const fetchData = async () => {
let data = await getSelectedSeatsApi();
setSelectedSeats(data);
};
if (existingUserSeatMap) {
await editSeatsApi(userId, currentSeats);
} else {
let info = {
id: userId,
userid: userId,
selected: currentSeats
};
await createSeatApi(info);
}
setCurrentSeats([]);
fetchData();
setShowSeatsConfirmationModal(false);
};
console.log(currentSeats, 'currentSeats');
return (
<div className={styles['seat-booking-wrapper']}>
<img
src={img}
className={styles['logout']}
width={30}
onClick={() => logout()}
/>
<div className={styles['screen-wrapper']}>
<Header>Choose Seats</Header>
<BookingWrapper
seatData={data}
selectedSeats={selectedSeats}
onSeatClick={onSelectSeats}
currentSeats={currentSeats}
/>
</div>
<div className={styles['btn-wrapper']}>
<Button
size={'lg'}
onClick={onSeatsConfirm}
disabled={currentSeats.length > 0 ? false : true}>
Confirm
</Button>
</div>
<LegendWrapper />
{showSeatsModal && (
<Modal
onConfirm={modalClose}
onChange={onModalChange}
variant="input"
value={noOfSeats}
/>
)}
{showSeatsConfirmationModal && (
<Modal
onConfirm={seatsFinal}
onChange={onModalChange}
variant={'confirm'}
onCancel={finalCancel}
/>
)}
</div>
);
};
export default BookingPage;
.seat-booking-wrapper {
box-sizing: border-box;
flex-direction: column;
display: flex;
justify-content: space-evenly;
background-color: #3444c5;
height: 100%;
overflow-y: auto;
width: 100%;
}
.screen-wrapper {
padding: 1em 0;
}
.btn-wrapper {
display: flex;
justify-content: center;
align-items: center;
padding: 1em 0;
}
.logout {
position: absolute;
right: 10px;
top: 10px;
cursor: pointer;
}
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import Login from '../../components/TopLevel/Login/Index'; import Login from '../../components/TopLevel/Login/Index';
import { login, createUser } from '../../lib/api'; import { login, createUser, getSeatSelectionCountApi } from '../../lib/api';
import { setItem } from '../../lib/localStorage'; import { setItem } from '../../lib/localStorage';
import styles from './styles.module.css';
const LoginPage = () => { const LoginPage = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const onSubmit = async (email) => { const onSubmit = async (email) => {
const userData = await login(email); const userData = await login(email);
if (userData) { const fetchSeatSelectionCount = async (id) => {
const data = await getSeatSelectionCountApi(id);
if (data.selectionCount) {
setItem('modal', false);
} else {
setItem('modal', true);
}
navigate('/'); navigate('/');
};
if (userData) {
setItem('user', userData.id); setItem('user', userData.id);
setItem('modal', true); fetchSeatSelectionCount(userData?.id);
} else { } else {
const data = await createUser(email); const data = await createUser(email);
navigate('/');
setItem('user', data.id); setItem('user', data.id);
setItem('modal', true); fetchSeatSelectionCount(data?.id);
} }
}; };
return <Login onSubmit={onSubmit} />; return <Login onSubmit={onSubmit} />;
}; };
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment