Commit 28a2f91f by Manivasagam S

code changed

parent 840f2f6f
import { useState } from "react"; import { useState, useEffect } from "react";
import styles from "./Auth.module.css"; import styles from "./Auth.module.css";
import { ToastContainer, toast } from "react-toastify"; import { ToastContainer } from "react-toastify";
import "react-toastify/dist/ReactToastify.css"; import "react-toastify/dist/ReactToastify.css";
export const Auth = ({ LoginComponent, SignUpComponent, onLoginSubmit, onSignUpSubmit }) => { export const Auth = ({
const [isLoginMode, setIsLoginMode] = useState(true); LoginComponent,
SignUpComponent,
onLoginSubmit,
onSignUpSubmit,
loginLabel = "Login Form",
signupLabel = "SignUp Form",
loginTabLabel = "Login",
signupTabLabel = "Signup",
initialMode = "login",
showForgotPassword = true,
showSwitchPrompt = true,
}) => {
const [isLoginMode, setIsLoginMode] = useState(initialMode === "login");
useEffect(() => {
setIsLoginMode(initialMode === "login");
}, [initialMode]);
return ( return (
<div className={styles.container}> <div className={styles.container}>
<div className={styles.formCard}> <div className={styles.formCard}>
<h2 className={styles.header}>{isLoginMode ? "Login Form" : "SignUp Form"}</h2> <h2 className={styles.header}>
{isLoginMode ? loginLabel : signupLabel}
</h2>
<div className={styles.tabContainer}> <div className={styles.tabContainer}>
<button <button
className={`${styles.tabButton} ${isLoginMode ? styles.activeTab : ""}`} className={`${styles.tabButton} ${isLoginMode ? styles.activeTab : ""}`}
onClick={() => setIsLoginMode(true)} onClick={() => setIsLoginMode(true)}
> >
Login {loginTabLabel}
</button> </button>
<button <button
className={`${styles.tabButton} ${!isLoginMode ? styles.activeTab : ""}`} className={`${styles.tabButton} ${!isLoginMode ? styles.activeTab : ""}`}
onClick={() => setIsLoginMode(false)} onClick={() => setIsLoginMode(false)}
> >
Signup {signupTabLabel}
</button> </button>
</div> </div>
{isLoginMode ? ( {isLoginMode ? (
<div className={styles.login}> <div className={styles.login}>
<LoginComponent onLogin={onLoginSubmit} /> <LoginComponent onLogin={onLoginSubmit} />
<a href="#" className={styles.forgotLink}>Forgot password?</a> {showForgotPassword && (
<a href="#" className={styles.forgotLink}>Forgot password?</a>
)}
</div> </div>
) : ( ) : (
<div className={styles.signup}> <div className={styles.signup}>
<SignUpComponent onSubmit={() => { <SignUpComponent
setIsLoginMode(true); onSignUpSubmit={async (formData) => {
if (onSignUpSubmit) onSignUpSubmit(); if (onSignUpSubmit) {
}} /> await onSignUpSubmit(formData);
setIsLoginMode(true);
}
}}
/>
</div> </div>
)} )}
<div className={styles.signupPrompt}> {showSwitchPrompt && (
{isLoginMode ? "Not a member?" : "Already a member?"}{" "} <div className={styles.signupPrompt}>
<a href="#" onClick={() => setIsLoginMode(!isLoginMode)}> {isLoginMode ? "Not a member?" : "Already a member?"}{" "}
{isLoginMode ? "Signup now" : "Login"} <a href="#" onClick={() => setIsLoginMode(!isLoginMode)}>
</a> {isLoginMode ? "Signup now" : "Login"}
</div> </a>
</div>
)}
</div> </div>
<ToastContainer position="top-right" autoClose={3000} /> <ToastContainer position="top-right" autoClose={3000} />
......
import { Auth } from "./Auth.jsx";
import { Login } from "../Form/Login/Login.jsx";
import { SignUp } from "../Form/SignUp/SignUp.jsx";
import { action } from "@storybook/addon-actions";
export default {
title: "Auth/Auth",
component: Auth,
argTypes: {
loginLabel: { control: "text", name: "Login Title" },
signupLabel: { control: "text", name: "Signup Title" },
loginTabLabel: { control: "text", name: "Login Tab Label" },
signupTabLabel: { control: "text", name: "Signup Tab Label" },
initialMode: {
control: { type: "radio" },
options: ["login", "signup"],
name: "Initial Mode",
},
showForgotPassword: { control: "boolean", name: "Show Forgot Password" },
showSwitchPrompt: { control: "boolean", name: "Show Switch Prompt" },
},
};
const DummyLogin = ({ onLogin }) => (
<div>
<Login onLogin={onLogin} />
</div>
);
const DummySignUp = ({ onSubmit }) => (
<div>
<SignUp onSignUpSubmit={async (formData) => {
action('Sign Up Submitted')(formData); // logs action
return new Promise((resolve) => setTimeout(resolve, 1000)); // simulate async
}} />
</div>
);
export const AuthTabs = (args) => (
<Auth
{...args}
LoginComponent={DummyLogin}
SignUpComponent={DummySignUp}
onLoginSubmit={action("Login Submit")}
onSignUpSubmit={async(formData)=>action("Signup Submit")}
/>
);
AuthTabs.args = {
loginLabel: "Login Form",
signupLabel: "SignUp Form",
loginTabLabel: "Login",
signupTabLabel: "Signup",
initialMode: "login",
showForgotPassword: true,
showSwitchPrompt: true,
};
...@@ -80,7 +80,8 @@ ...@@ -80,7 +80,8 @@
background-color: var(--secondary); background-color: var(--secondary);
color: black; color: black;
border-radius: 15px; border-radius: 15px;
min-width: 12rem; width: 100%;
/* min-width: 12rem; */
} }
.secondary:hover { .secondary:hover {
......
import { Button } from './Button'; import { Button } from './Button';
import styles from '../Buttons/Button.module.css'; import styles from '../Buttons/Button.module.css';
export default { export default {
title: 'Base/Button', title: 'Base/Button',
component: Button, component: Button,
......
import { Screen } from "./Screen";
export default{
title:'Shared/Screen',
component:Screen,
parameters: {
backgrounds: {
default: 'blue-bg',
values: [{ name: 'blue-bg', value: '#3444c5' }],
},
}
}
export const Default=(args)=><Screen {...args}/>
\ No newline at end of file
...@@ -2,13 +2,12 @@ import React, { useState } from 'react'; ...@@ -2,13 +2,12 @@ import React, { useState } from 'react';
import { Input } from '../../Base/Input/Input'; import { Input } from '../../Base/Input/Input';
import { Button } from '../../Base/Buttons/Button'; import { Button } from '../../Base/Buttons/Button';
import styles from './SignUp.module.css'; import styles from './SignUp.module.css';
// import signup from '../../../assets/Signup.png'; import { getUserByPhone } from '../../../Api/userApi';
import { getUserByPhone, postUser } from '../../../Api/userApi';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css'; import 'react-toastify/dist/ReactToastify.css';
export const SignUp = ({ export const SignUp = ({
onSubmit, onSignUpSubmit,
label = "SignUp", label = "SignUp",
namePlaceholder = "Name", namePlaceholder = "Name",
emailPlaceholder = "Email", emailPlaceholder = "Email",
...@@ -34,8 +33,7 @@ export const SignUp = ({ ...@@ -34,8 +33,7 @@ export const SignUp = ({
setErrors((prev) => ({ ...prev, phoneNumber: '' })); setErrors((prev) => ({ ...prev, phoneNumber: '' }));
}; };
const handleSubmit = async (e) => { const validateForm = () => {
e.preventDefault();
const newErrors = {}; const newErrors = {};
if (!name.trim()) newErrors.name = 'Name is required'; if (!name.trim()) newErrors.name = 'Name is required';
...@@ -61,47 +59,39 @@ export const SignUp = ({ ...@@ -61,47 +59,39 @@ export const SignUp = ({
} }
setErrors(newErrors); setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
if (Object.keys(newErrors).length === 0) { const handleSubmit = async (e) => {
try { e.preventDefault();
const res = await getUserByPhone(phoneNumber);
if (res.data.length > 0) {
setErrors((prev) => ({
...prev,
phoneNumber: 'Phone number already registered',
}));
return;
}
const formData = { name, email, phoneNumber, reservedSeats: [] }; if (!validateForm()) return;
const postRes = await postUser(formData);
setName("");
setEmail("");
setPhoneNumber("");
if (postRes.status === 201) {
toast.success("Account created successfully!");
if (onSubmit) onSubmit(formData);
} else {
toast.error("Something went wrong. Please try again.");
}
} catch (err) { try {
console.error('Error submitting form:', err); const res = await getUserByPhone(phoneNumber);
toast.error("Network error or server not available."); if (res.data.length > 0) {
setErrors((prev) => ({
...prev,
phoneNumber: 'Phone number already registered',
}));
return;
} }
const formData = { name, email, phoneNumber };
await onSignUpSubmit?.(formData);
setName('');
setEmail('');
setPhoneNumber('');
} catch (error) {
toast.error("Something went wrong. Please try again.");
} }
}; };
return ( return (
<div className={styles.SignUpContainer}> <div className={styles.SignUpContainer}>
<div className={styles.wrapper}> <div className={styles.wrapper}>
{/* <div
className={styles.leftPane}
style={{ backgroundImage: `url(${signup})` }}
></div> */}
<div className={styles.rightPane}> <div className={styles.rightPane}>
{/* <h1 className={styles.text}>Create Account</h1> */}
<form onSubmit={handleSubmit} className={styles.form}> <form onSubmit={handleSubmit} className={styles.form}>
<Input <Input
type="text" type="text"
......
import styles from "../Screen/Screen.module.css" import styles from "./Screen.module.css";
export const Screen = ({ width = 600, height = 210 }) => ( export const Screen = ({ width = 600, height = 210 }) => (
<div className={styles.shadow}> <div className={styles.shadow}>
<svg width={width} height={height} viewBox="0 0 300 120" xmlns="http://www.w3.org/2000/svg"> <svg width={width} height={height} viewBox="0 0 300 120" xmlns="http://www.w3.org/2000/svg">
...@@ -11,6 +12,6 @@ export const Screen = ({ width = 600, height = 210 }) => ( ...@@ -11,6 +12,6 @@ export const Screen = ({ width = 600, height = 210 }) => (
</defs> </defs>
<path d="M30 60 Q150 36 270 60 L285 130 L10 130 Z" fill="url(#lightGradient)" /> <path d="M30 60 Q150 36 270 60 L285 130 L10 130 Z" fill="url(#lightGradient)" />
<path d="M30 60 Q150 36 270 60" stroke="#fff" strokeWidth="2" fill="none" strokeLinecap="round" /> <path d="M30 60 Q150 36 270 60" stroke="#fff" strokeWidth="2" fill="none" strokeLinecap="round" />
</svg> </svg>
</div> </div>
); );
import React from "react";
import { Screen } from "./Screen";
export default {
title: "Shared/Screen",
component: Screen,
parameters: {
backgrounds: {
default: "blue-bg",
values: [{ name: "blue-bg", value: "#3444c5" }],
},
},
argTypes: {
width: {
control: { type: "number" },
defaultValue: 600,
},
height: {
control: { type: "number" },
defaultValue: 210,
},
},
};
export const statndardScreen= (args) => <Screen {...args} />;
statndardScreen.args = {
width: 600,
height: 210,
};
...@@ -30,7 +30,7 @@ export const Notification = ({ title, type, msg, button, }) => { ...@@ -30,7 +30,7 @@ export const Notification = ({ title, type, msg, button, }) => {
</span> </span>
<h2 className={styles.title}>{title}</h2> <h2 className={styles.title}>{title}</h2>
<p className={styles.msg}>{msg}</p> <p className={styles.msg}>{msg}</p>
{button && button} <div className={styles.button}> {button && button}</div>
</div> </div>
</> </>
); );
......
...@@ -18,7 +18,9 @@ font-size: 2rem; ...@@ -18,7 +18,9 @@ font-size: 2rem;
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
.button{
max-width: 12rem;
}
.container:hover { .container:hover {
transform: scale(1.02); transform: scale(1.02);
opacity: 0.95; opacity: 0.95;
......
...@@ -3,12 +3,15 @@ import cn from 'classnames'; ...@@ -3,12 +3,15 @@ import cn from 'classnames';
import styles from './Seat.module.css'; import styles from './Seat.module.css';
export const Seat = ({ seatNo, status, onClick }) => { export const Seat = ({ seatNo, status, onClick }) => {
const isMine = status === 'mine' || status === 'mine-unselected';
const className = cn(styles.seat, { const className = cn(styles.seat, {
[styles.noSeat]: status === 'none', [styles.noSeat]: status === 'none',
[styles.available]: status === 'available', [styles.available]: status === 'available',
[styles.reserved]: status === 'reserved', [styles.reserved]: status === 'reserved',
[styles.selected]: status === 'selected', [styles.selected]: status === 'selected',
[styles.mine]:status === 'mine', [styles.mine]: isMine,
[styles.unselected]: status === 'mine-unselected',
}); });
return ( return (
...@@ -16,7 +19,7 @@ export const Seat = ({ seatNo, status, onClick }) => { ...@@ -16,7 +19,7 @@ export const Seat = ({ seatNo, status, onClick }) => {
className={className} className={className}
data-tooltip={seatNo} data-tooltip={seatNo}
role="button" role="button"
tabIndex={status === 'available' || status === 'selected' ? 0 : -1} tabIndex={['available', 'selected', 'mine', 'mine-unselected'].includes(status) ? 0 : -1}
onClick={onClick} onClick={onClick}
> >
{null} {null}
...@@ -26,6 +29,13 @@ export const Seat = ({ seatNo, status, onClick }) => { ...@@ -26,6 +29,13 @@ export const Seat = ({ seatNo, status, onClick }) => {
Seat.propTypes = { Seat.propTypes = {
seatNo: PropTypes.string.isRequired, seatNo: PropTypes.string.isRequired,
status: PropTypes.oneOf(['none', 'available', 'reserved', 'selected']), status: PropTypes.oneOf([
'none',
'available',
'reserved',
'selected',
'mine',
'mine-unselected'
]),
onClick: PropTypes.func, onClick: PropTypes.func,
}; };
...@@ -27,7 +27,12 @@ ...@@ -27,7 +27,12 @@
background-color:#5b66cb; background-color:#5b66cb;
} }
.mine{ .mine{
background-color: white; background-color: #01fff7;
cursor: pointer;
}
.unselected {
background-color:transparent;
border: 0.5px solid rgba(164, 175, 255, 1);
} }
.reserved:hover { .reserved:hover {
......
...@@ -2,18 +2,24 @@ import { useState, useEffect } from 'react'; ...@@ -2,18 +2,24 @@ import { useState, useEffect } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Seat } from '../Seat/Seat.jsx'; import { Seat } from '../Seat/Seat.jsx';
import styles from './SeatLayout.module.css'; import styles from './SeatLayout.module.css';
import { seatsData } from '../SeatLayout/SeatsData.js'; import { seatsData } from './SeatsData.js';
const populateSeatsArray = (selectedSeats, allReservedSeats) => { const populateSeatsArray = (selectedSeats, reservedSeats, localSelectedSeats) => {
return seatsData.map(({ row, seats }) => return seatsData.map(({ row, seats }) =>
seats.map((number) => { seats.map((number) => {
const seatNo = row + number; const seatNo = row + number;
if (number === '/') { if (number === '/') {
return { seatNo, status: 'none' }; return { seatNo, status: 'none' };
} else if (localSelectedSeats.includes(seatNo)) {
if (selectedSeats.includes(seatNo)) {
return { seatNo, status: 'mine' };
} else {
return { seatNo, status: 'mine-unselected' };
}
} else if (reservedSeats.includes(seatNo)) {
return { seatNo, status: 'reserved' };
} else if (selectedSeats.includes(seatNo)) { } else if (selectedSeats.includes(seatNo)) {
return { seatNo, status: 'selected' }; return { seatNo, status: 'selected' };
} else if (allReservedSeats.includes(seatNo)) {
return { seatNo, status: 'reserved' };
} else { } else {
return { seatNo, status: 'available' }; return { seatNo, status: 'available' };
} }
...@@ -22,67 +28,62 @@ const populateSeatsArray = (selectedSeats, allReservedSeats) => { ...@@ -22,67 +28,62 @@ const populateSeatsArray = (selectedSeats, allReservedSeats) => {
}; };
export const SeatLayout = ({ export const SeatLayout = ({
initialReservedSeats = [], selectedSeats: initialSelected = [],
allReservedSeats = [], reservedSeats = [],
currentReservedSeats = [],
limit, limit,
onSelectionChange, onSelectionChange,
onSubmit,
}) => { }) => {
const [selectedSeats, setSelectedSeats] = useState(initialReservedSeats); const [localSelectedSeats, setLocalSelectedSeats] = useState(currentReservedSeats);
useEffect(() => { useEffect(() => {
onSelectionChange?.(selectedSeats); setLocalSelectedSeats(currentReservedSeats);
}, [selectedSeats, onSelectionChange]); }, [currentReservedSeats]);
// useEffect(() => {
// setSelectedSeats(initialReservedSeats);
// }, [initialReservedSeats]);
const seatClickHandler = (seat) => { useEffect(() => {
if (seat.status === 'available' || seat.status === 'selected') { onSelectionChange?.(localSelectedSeats);
setSelectedSeats((prevSelected) => { }, [localSelectedSeats, onSelectionChange]);
let newSelected;
let actionType;
if (prevSelected.includes(seat.seatNo)) { const seatClickHandler = (seat) => {
newSelected = prevSelected.filter((s) => s !== seat.seatNo); const { seatNo, status } = seat;
actionType = 'unclicked'; if (status === 'reserved') return;
} else if (prevSelected.length < limit) {
newSelected = [...prevSelected, seat.seatNo];
actionType = 'clicked';
} else {
return prevSelected;
}
onSubmit?.(actionType); const hasExisting = currentReservedSeats.length > 0;
return newSelected; const maxLimit = hasExisting ? Infinity : Number(limit);
});
}
};
setLocalSelectedSeats((prev) => {
if (prev.includes(seatNo)) {
return prev.filter((s) => s !== seatNo);
} else if (prev.length < maxLimit) {
return [...prev, seatNo];
}
return prev;
});
};
const renderedSeats = populateSeatsArray(
localSelectedSeats,
reservedSeats,
currentReservedSeats
);
const renderedSeats = populateSeatsArray(selectedSeats, allReservedSeats);
console.log(selectedSeats);
return ( return (
<div className={styles.container}> <div className={styles.container}>
{renderedSeats.map((row, i) => (
{renderedSeats.map((rowSeats, i) => ( <div key={i} className={styles.seatcontainer}>
<div key={i} className={styles.seatcontainer}> {row.map((seat, j) => (
{rowSeats.map((seat, j) => ( <Seat key={j} {...seat} onClick={() => seatClickHandler(seat)} />
<Seat key={j} onClick={() => seatClickHandler(seat)} {...seat} /> ))}
))} </div>
</div> ))}
))}
</div> </div>
); );
}; };
SeatLayout.propTypes = { SeatLayout.propTypes = {
allReservedSeats: PropTypes.arrayOf(PropTypes.string), selectedSeats: PropTypes.arrayOf(PropTypes.string),
initialReservedSeats: PropTypes.arrayOf(PropTypes.string), reservedSeats: PropTypes.arrayOf(PropTypes.string),
limit: PropTypes.number, currentReservedSeats: PropTypes.arrayOf(PropTypes.string),
onSelectionChange: PropTypes.func, limit: PropTypes.number.isRequired,
onSelectionChange: PropTypes.func.isRequired,
}; };
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
align-items: center; align-items: center;
margin-bottom: 5rem; margin-bottom: 5rem;
width: 100%; width: 100%;
overflow-x: auto; /* overflow-x: auto; */
} }
.seatcontainer { .seatcontainer {
......
import { SeatLayout } from "./SeatLayout"; import React from 'react';
import { SeatLayout } from './SeatLayout';
import { action } from '@storybook/addon-actions';
const reservedSeatsSample = ['A1', 'B2', 'C3'];
const currentUserReservedSeats = ['A3', 'B4'];
const logSelectionChange = action('Clicked/Unclicked');
export default { export default {
title: 'Toplevel/SeatLayout', title: 'Toplevel/SeatLayout',
component: SeatLayout, component: SeatLayout,
parameters:{ parameters: {
backgrounds: { backgrounds: {
default: 'blue-bg', default: 'blue-bg',
values: [ values: [{ name: 'blue-bg', value: '#3444c5' }],
{ name: 'blue-bg', value: '#3444c5' }, // blue background
],
}, },
layout:"centered" layout: 'centered',
}, },
argTypes: { argTypes: {
seatType: { selectedSeats: {
control: { control: { type: 'array' },
type: 'radio', description: 'Seats currently selected by the user',
}, },
options: ['available', 'reserved', 'selected'] reservedSeats: {
control: { type: 'array' },
description: 'Seats reserved by others',
}, },
limit: { currentReservedSeats: {
control: { type: 'number', min: 1, max: 10 }, control: { type: 'array' },
description: 'Seats reserved by the logged-in user',
}, },
onSubmit: { action: 'clicked/unclicked' }, // limit is used internally, not exposed in the controls
onSelectionChange: { action: 'selectedseats' }, },
};
const modeConfig = {
available: {
selectedSeats: [],
reservedSeats: [],
currentReservedSeats: [],
limit: 3,
},
reserved: {
selectedSeats: [],
reservedSeats: reservedSeatsSample,
currentReservedSeats: [],
limit: 2,
},
selected: {
selectedSeats: [],
reservedSeats: [],
currentReservedSeats: currentUserReservedSeats,
limit: 1,
}, },
}; };
export const Default= ({ seatType, limit, onSubmit, onSelectionChange }) => { export const Default = (args) => {
let allReservedSeats = []; const config = modeConfig[args.mode] || modeConfig.available;
let initialReservedSeats = [];
if (seatType === 'reserved') {
allReservedSeats = ['B1', 'B2', 'B3'];
} else if (seatType === 'selected') {
initialReservedSeats = ['C1', 'C2','C3'];
}
return ( return (
<SeatLayout <SeatLayout
key={seatType} selectedSeats={args.selectedSeats.length ? args.selectedSeats : config.selectedSeats}
allReservedSeats={allReservedSeats} reservedSeats={args.reservedSeats.length ? args.reservedSeats : config.reservedSeats}
initialReservedSeats={initialReservedSeats} currentReservedSeats={
limit={limit} args.currentReservedSeats.length
onSubmit={onSubmit} ? args.currentReservedSeats
onSelectionChange={onSelectionChange} : config.currentReservedSeats
}
limit={config.limit}
onSelectionChange={logSelectionChange}
/> />
); );
}; };
Default.args = { Default.args = {
seats: [ mode: 'available',
[0, "A1", "A2", "A3", 0, "A4", "A5", "A6", 0], selectedSeats: [],
["B1", "B2", "B3", "B4", 0, "B5", "B6", "B7", "B8"], reservedSeats: [],
["C1", "C2", "C3", "C4", 0, "C5", "C6", "C7", "C8"], currentReservedSeats: [],
["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],
],
seatType: 'available',
limit: 3,
}; };
...@@ -9,11 +9,13 @@ import "react-toastify/dist/ReactToastify.css"; ...@@ -9,11 +9,13 @@ import "react-toastify/dist/ReactToastify.css";
import { toast, ToastContainer } from "react-toastify"; import { toast, ToastContainer } from "react-toastify";
import { Modal } from "../../Base/Modal/Modal"; import { Modal } from "../../Base/Modal/Modal";
import { SeatSelectForm } from "../../Form/SeatSelectForm/SeatSelectForm"; import { SeatSelectForm } from "../../Form/SeatSelectForm/SeatSelectForm";
import { Screen } from "../../Base/Screen/Screen"; import { Screen } from "../../Shared/Screen/Screen";
import { AiOutlineLogout } from "react-icons/ai"; import { AiOutlineLogout } from "react-icons/ai";
export const Selectseat = ({onLogout}) => {
export const Selectseat = ({ onLogout }) => {
const [selectedSeats, setSelectedSeats] = useState([]); const [selectedSeats, setSelectedSeats] = useState([]);
const [allReservedSeats, setAllReservedSeats] = useState([]); const [selectedData, setselectedData] = useState([]);
const [reservedSeats, setreservedSeats] = useState([]);
const [seatCount, setSeatCount] = useState(1); const [seatCount, setSeatCount] = useState(1);
const [availableSeats, setAvailableSeats] = useState(0); const [availableSeats, setAvailableSeats] = useState(0);
const [showSelect, setShowSelect] = useState(true); const [showSelect, setShowSelect] = useState(true);
...@@ -22,114 +24,124 @@ export const Selectseat = ({onLogout}) => { ...@@ -22,114 +24,124 @@ export const Selectseat = ({onLogout}) => {
getAllUsers() getAllUsers()
.then(res => { .then(res => {
const users = res.data; const users = res.data;
const reserved = users const totalSeats = 60;
.flatMap(u => u.reservedSeats || []) const loggedUser = getCurrentUser();
.filter(seat => seat);
setAllReservedSeats(reserved); const allReserved = users.flatMap(u => u.reservedSeats || []);
const totalSeats=60; const currentUser = users.find(u => u.phoneNumber === loggedUser.phoneNumber);
const userSeats = currentUser?.reservedSeats || [];
const othersReserved = allReserved.filter(seat => !userSeats.includes(seat));
setselectedData(userSeats);
setreservedSeats(othersReserved);
const available = totalSeats - allReserved.length;
setAvailableSeats(available);
const available=totalSeats-reserved.length; if (userSeats.length > 0) {
setAvailableSeats(available); setSeatCount(userSeats.length);
}) setShowSelect(false); // skip modal for existing users
}
})
.catch(err => { .catch(err => {
console.error("Error fetching users", err); console.error("Error fetching users", err);
toast.error("Failed to load seat data."); toast.error("Failed to load seat data.");
}); });
}, []); }, []);
const confirmHandler = () => { const confirmHandler = () => {
const user = getCurrentUser(); const user = getCurrentUser();
if (!user || !user.phoneNumber?.trim()) { if (!user || !user.phoneNumber?.trim()) {
toast.warn("User not logged in"); toast.warn("User not logged in");
return; return;
} }
if (selectedSeats.length < seatCount) { if (selectedData.length === 0 && selectedSeats.length < seatCount) {
toast.info(`Please select ${seatCount} seats`); toast.info(`Please select ${seatCount} seat(s)`);
return; return;
} }
getAllUsers() getAllUsers()
.then(res => { .then(res => {
const users = res.data; const users = res.data;
const currentUser = users.find(u => u.phoneNumber === user.phoneNumber); const currentUser = users.find(u => u.phoneNumber === user.phoneNumber);
if (!currentUser) {
toast.error("User not found");
return;
}
if (!currentUser) {
const latestReserved = users toast.error("User not found");
.flatMap(u => u.reservedSeats || []); return;
}
const conflictSeats = selectedSeats.filter(seat => const latestReserved = users
latestReserved.includes(seat) .flatMap(u => u.reservedSeats || [])
); .filter(seat => !currentUser.reservedSeats.includes(seat));
if (conflictSeats.length > 0) { const conflictSeats = selectedSeats.filter(seat =>
toast.error( latestReserved.includes(seat)
`The following seat(s) were just taken: ${conflictSeats.join(", ")}`
); );
setSelectedSeats([]);
return; if (conflictSeats.length > 0) {
} toast.error(`The following seat(s) were just taken: ${conflictSeats.join(", ")}`);
return;
const updatedSeats = Array.from( }
new Set([...(currentUser.reservedSeats || []), ...selectedSeats])
); return updateUserReservedSeats(currentUser.id, selectedSeats);
})
return updateUserReservedSeats(currentUser.id, updatedSeats); .then(res => {
}) if (res) {
.then(res => { toast.success("Seats updated!");
if (res) { setSelectedSeats([]);
toast.success("Seats confirmed!"); setTimeout(() => {
setSelectedSeats([]); window.location.replace("/success");
}, 1000);
}
})
.catch(err => {
console.error("Error confirming seats", err);
toast.error("Failed to confirm seats");
setTimeout(() => { setTimeout(() => {
window.location.replace("/success"); window.location.replace("/error");
}, 1000); }, 1000);
} });
}) };
.catch(err => {
console.error("Error confirming seats", err);
toast.error("Failed to confirm seats");
setTimeout(() => {
window.location.replace("/error");
}, 1000);
});
};
const handleModal = () => { const handleModal = () => {
setShowSelect(false); setShowSelect(false);
}; };
const seatOptions = Array.from({ length: availableSeats }).map((_, i) => ({ const seatOptions = Array.from({ length: availableSeats }).map((_, i) => ({
value: i + 1, value: i + 1,
label: `${i + 1} ${i + 1 === 1 ? "seat" : "seats"}` label: `${i + 1} ${i + 1 === 1 ? "seat" : "seats"}`
})); }));
return ( return (
<> <>
<div className={styles.logoutButton}> <div className={styles.logoutButton}>
<AiOutlineLogout onClick={onLogout}/> <AiOutlineLogout onClick={onLogout} />
</div> </div>
<div className={styles.container}> <div className={styles.container}>
{/* <div className={styles.logoutButton}><Button variant="secondary" label="Logout" size="sm" onClick={onLogout}/></div> */} <h2 className={styles.text}>Choose Seats</h2>
<h2 className={styles.text}>Choose Seats</h2> <Screen />
<Screen/>
<div className={styles.seatcontainer}> <div className={styles.seatcontainer}>
<SeatLayout <SeatLayout
allReservedSeats={allReservedSeats} selectedSeats={selectedSeats}
onSelectionChange={setSelectedSeats} reservedSeats={reservedSeats}
currentReservedSeats={selectedData}
limit={seatCount} limit={seatCount}
onSelectionChange={setSelectedSeats}
/> />
</div> </div>
<div className={styles.button}> <div className={styles.button}>
<Button variant="secondary" label="Confirm" size='md' onClick={confirmHandler} disabled={selectedSeats.length!=seatCount}/> <Button
variant="secondary"
label="Confirm"
size="md"
onClick={confirmHandler}
disabled={selectedData.length === 0 && selectedSeats.length !== seatCount}
/>
</div> </div>
<div className={styles.legend}> <div className={styles.legend}>
...@@ -155,6 +167,6 @@ export const Selectseat = ({onLogout}) => { ...@@ -155,6 +167,6 @@ export const Selectseat = ({onLogout}) => {
)} )}
<ToastContainer position="top-right" autoClose={3000} /> <ToastContainer position="top-right" autoClose={3000} />
</> </>
); );
}; };
...@@ -29,7 +29,8 @@ body { ...@@ -29,7 +29,8 @@ body {
position: relative; position: relative;
bottom: 4rem; bottom: 4rem;
padding: 0 1rem; padding: 0 1rem;
max-width: 12rem;
margin: 0 auto;
} }
/* /*
.shadow { .shadow {
...@@ -40,6 +41,10 @@ body { ...@@ -40,6 +41,10 @@ body {
padding: 0 1rem; padding: 0 1rem;
} */ } */
/* labels:{
signUp:'sdfs'
/} */
.seatcontainer { .seatcontainer {
position: relative; position: relative;
bottom:4rem; bottom:4rem;
......
// Auth.stories.jsx
import { Auth } from "../Auth/Auth.jsx";
import {Login} from '../../Components/Form/Login/Login.jsx'
import { SignUp } from "../../Components/Form/SignUp/SignUp.jsx";
export default {
title: "Auth/Auth",
component: Auth,
};
const DummyLogin = ({ onLogin }) => (
<div>
<Login/>
</div>
);
const DummySignUp = ({ onSubmit }) => (
<div>
<SignUp/>
</div>
);
export const Default = () => (
<Auth
LoginComponent={DummyLogin}
SignUpComponent={DummySignUp}
onLoginSubmit={(data) => console.log("Login Submit:", data)}
onSignUpSubmit={() => console.log("SignUp Submit")}
/>
);
import { Login} from "../Components/Form/Login/Login.jsx" import { Login} from "../Components/Form/Login/Login.jsx"
import { SignUp } from "../Components/Form/SignUp/SignUp"; import { SignUp } from "../Components/Form/SignUp/SignUp";
import { toast } from "react-toastify"; import { toast } from "react-toastify";
import { Auth } from "./Auth/Auth.jsx"; import { Auth } from "../Components/Auth/Auth.jsx";
import { getUserByPhone,postUser } from "../Api/userApi.js";
export const AuthPage = () => { export const AuthPage = () => {
const handleLogin = async (phoneNumber) => { const handleLogin = async (phoneNumber) => {
try { try {
...@@ -21,11 +22,40 @@ export const AuthPage = () => { ...@@ -21,11 +22,40 @@ export const AuthPage = () => {
} }
}; };
const handleSignUp = async (formData) => {
try {
if (!formData || !formData.phoneNumber) {
toast.error("Invalid form data");
return;
}
const response = await getUserByPhone(formData.phoneNumber);
if (response.data.length > 0) {
toast.warn("Phone number already registered.");
return;
}
const newUser = { ...formData, reservedSeats: [] };
const postRes = await postUser(newUser);
if (postRes.status === 201) {
localStorage.setItem("user", JSON.stringify(postRes.data));
toast.success("Account created successfully!");
} else {
toast.error("Failed to create account. Try again.");
}
} catch (error) {
console.error("Sign up error:", error);
toast.error("Network error or server not available.");
}
};
return ( return (
<Auth <Auth
LoginComponent={Login} LoginComponent={Login}
SignUpComponent={SignUp} SignUpComponent={SignUp}
onLoginSubmit={handleLogin} onLoginSubmit={handleLogin}
onSignUpSubmit={handleSignUp}
/> />
); );
}; };
...@@ -18,6 +18,9 @@ ...@@ -18,6 +18,9 @@
"phoneNumber": "9361775481", "phoneNumber": "9361775481",
"name": "mani", "name": "mani",
"reservedSeats": [ "reservedSeats": [
"B3",
"B2",
"B1",
"C4" "C4"
] ]
}, },
...@@ -26,6 +29,106 @@ ...@@ -26,6 +29,106 @@
"name": "mani", "name": "mani",
"email": "mani@gmail.com", "email": "mani@gmail.com",
"phoneNumber": "9934782103", "phoneNumber": "9934782103",
"reservedSeats": [
"B4",
"D4",
"E3",
"A3",
"A2",
"A1",
"H1",
"H2"
]
},
{
"id": "2c1f",
"name": "mani",
"email": "mani@gmail.com",
"phoneNumber": "9312345678",
"reservedSeats": []
},
{
"id": "845c",
"name": "abinesh",
"email": "abi@gmail.com",
"phoneNumber": "1234587690",
"reservedSeats": []
},
{
"id": "8ebe",
"name": "asdfsa",
"email": "adsasdf@gmail.om",
"phoneNumber": "12341234234",
"reservedSeats": []
},
{
"id": "b80a",
"name": "ganesh",
"email": "ganesh@gmail.com",
"phoneNumber": "9786543219",
"reservedSeats": []
},
{
"id": "9659",
"name": "dinesh",
"email": "din@gmail.com",
"phoneNumber": "7895432173",
"reservedSeats": []
},
{
"id": "d7df",
"name": "janani",
"email": "janani@gmail.com",
"phoneNumber": "9587923467",
"reservedSeats": []
},
{
"id": "29ef",
"name": "viji",
"email": "viji@gmail.com",
"phoneNumber": "7689543218",
"reservedSeats": []
},
{
"id": "a364",
"name": "vijay",
"email": "vijay@gmail.com",
"phoneNumber": "6789543212",
"reservedSeats": []
},
{
"id": "f999",
"name": "kain",
"email": "kain@gmail.com",
"phoneNumber": "7584932105",
"reservedSeats": []
},
{
"id": "5dcd",
"name": "ganesh",
"email": "ganesh@gmail.com",
"phoneNumber": "8790654321",
"reservedSeats": []
},
{
"id": "5306",
"name": "janani",
"email": "janani@gmail.com",
"phoneNumber": "9867543210",
"reservedSeats": []
},
{
"id": "aaff",
"name": "maniv",
"email": "maniv@gmail.com",
"phoneNumber": "9687543721",
"reservedSeats": []
},
{
"id": "8ec3",
"name": "dfgfdg",
"email": "dsbfsdbf@gmail.com",
"phoneNumber": "4782391204",
"reservedSeats": [] "reservedSeats": []
} }
] ]
......
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