diff --git a/client/src/components/Dashboard/FlightCard/FlightCard.tsx b/client/src/components/Dashboard/FlightCard/FlightCard.tsx index 5de50b741fb10ec4215fc824c5c71bea628e538b..1d19058b029459608146abd8ba41387047833666 100644 --- a/client/src/components/Dashboard/FlightCard/FlightCard.tsx +++ b/client/src/components/Dashboard/FlightCard/FlightCard.tsx @@ -5,16 +5,19 @@ import './FlightCard.scss'; interface IFlightCard { flight: IFlight; + extraInfo?: boolean; } -function FlightCard({ flight }: IFlightCard) { +function FlightCard({ flight, extraInfo }: IFlightCard) { return ( <> <div className='flight-card'> <span>{flight.id}</span> + {extraInfo && <span>Departure Time: {new Date(flight.departureTime).toLocaleString()}</span>} + {extraInfo && <span>Arrival Time: {new Date(flight.arrivalTime).toLocaleString()}</span>} <span className='flight-path'>{airportCode(flight.origin)} - {airportCode(flight.destination)}</span> <span>{flight.origin} - {flight.destination}</span> - <Link to={'/flights/' + flight.id}>View Flight</Link> + <Link to={'/flight/' + flight.id}>View Flight</Link> </div> </> ); diff --git a/client/src/components/Flight/Flight.scss b/client/src/components/Flight/Flight.scss index 6f20f8b9045fb3ec74536c5aebe04c5afa75b453..800b625a7b62583ae7d79888a43fd108ac00dacc 100644 --- a/client/src/components/Flight/Flight.scss +++ b/client/src/components/Flight/Flight.scss @@ -7,8 +7,10 @@ } .flight-data-card { - width: 25vw; + width: 30vw; min-width: 350px; + max-height: 100%; + overflow-y: scroll; } .flight-data-title { @@ -24,3 +26,16 @@ .flight-data-value { font-size: 1.1rem; } + +.seat-lists { + display: flex; + justify-content: space-around; +} + +.seat-available { + color: green; +} + +.seat-unavailable { + color: red; +} diff --git a/client/src/components/Flight/Flight.tsx b/client/src/components/Flight/Flight.tsx index 6c4902125370b430cc606c946b9c040a415f17f9..ea6ca3b316a4ffb96ea59e436752eeb6751171a2 100644 --- a/client/src/components/Flight/Flight.tsx +++ b/client/src/components/Flight/Flight.tsx @@ -1,9 +1,29 @@ import { useLoaderData } from 'react-router-dom'; -import { IFlight } from '../../services/Dashboard/CustomerDashboard'; +import { IFlightData } from '../../services/Flight/Flight'; +import { ISeat } from '../../services/Dashboard/CustomerDashboard'; import './Flight.scss'; function Flight() { - const data = useLoaderData() as IFlight; + const data = useLoaderData() as IFlightData; + const flight = data.flight; + + const calculateRows = (seats: ISeat[]) => { + let curr = 0; + let counter = 0; + seats.forEach((seat) => { + const num = +seat.seatNumber.match(/^(\d+)([A-Z]+)$/)![1]; + if (curr !== num) { + counter++; + curr = num; + } + }); + return counter; + }; + + const businessSeats = data.seats.$values.filter((seat) => seat.classType === 0); + const businessRows = calculateRows(businessSeats); + const economySeats = data.seats.$values.filter((seat) => seat.classType === 1); + const economyRows = calculateRows(economySeats); return ( <> @@ -13,47 +33,93 @@ function Flight() { <div className='flight-data-item'> <span className='flight-data-label'>ID: </span> - <span className='flight-data-value'>{data.id}</span> + <span className='flight-data-value'>{flight.id}</span> </div> <div className='flight-data-item'> <span className='flight-data-label'>Origin: </span> - <span className='flight-data-value'>{data.origin}</span> + <span className='flight-data-value'>{flight.origin}</span> </div> <div className='flight-data-item'> <span className='flight-data-label'>Destination: </span> - <span className='flight-data-value'>{data.destination}</span> + <span className='flight-data-value'>{flight.destination}</span> </div> <div className='flight-data-item'> <span className='flight-data-label'>Departure Time: </span> - <span className='flight-data-value'>{new Date(data.departureTime).toLocaleString()}</span> + <span className='flight-data-value'>{new Date(flight.departureTime).toLocaleString()}</span> </div> <div className='flight-data-item'> <span className='flight-data-label'>Arrival Time: </span> - <span className='flight-data-value'>{new Date(data.arrivalTime).toLocaleString()}</span> + <span className='flight-data-value'>{new Date(flight.arrivalTime).toLocaleString()}</span> </div> <div className='flight-data-item'> <span className='flight-data-label'>Economy Capacity: </span> - <span className='flight-data-value'>{data.economyCapacity}</span> + <span className='flight-data-value'>{flight.economyCapacity}</span> </div> <div className='flight-data-item'> <span className='flight-data-label'>Business Capacity: </span> - <span className='flight-data-value'>{data.businessCapacity}</span> + <span className='flight-data-value'>{flight.businessCapacity}</span> </div> <div className='flight-data-item'> <span className='flight-data-label'>Economy Price: </span> - <span className='flight-data-value'>{data.economyPrice}</span> + <span className='flight-data-value'>{flight.economyPrice}</span> </div> <div className='flight-data-item'> <span className='flight-data-label'>Business Price: </span> - <span className='flight-data-value'>{data.businessPrice}</span> + <span className='flight-data-value'>{flight.businessPrice}</span> + </div> + + <div className='flight-data-item seat-lists'> + <div className='flight-data-item'> + <span className='flight-data-label'>Business Seats:</span> + <table className='flight-data-value'> + <tbody> + {[...Array(businessRows)].map((_, rowIndex) => ( + <tr key={rowIndex}> + {[...Array(4)].map((_, cellIndex) => { + const seatNumber = `${rowIndex + 1}${String.fromCharCode(65 + cellIndex)}`; + const seat = businessSeats.find(seat => seat.seatNumber === seatNumber); + const isAvailable = seat ? seat.isAvailable : false; + return ( + <td key={cellIndex} className={isAvailable ? 'seat-available' : 'seat-unavailable'}> + {seatNumber} + </td> + ); + })} + </tr> + ))} + </tbody> + </table> + </div> + + <div className='flight-data-item'> + <span className='flight-data-label'>Economy Seats:</span> + <table className='flight-data-value'> + <tbody> + {[...Array(economyRows)].map((_, rowIndex) => ( + <tr key={rowIndex}> + {[...Array(6)].map((_, cellIndex) => { + const seatNumber = `${rowIndex + businessRows + 1}${String.fromCharCode(65 + cellIndex)}`; + const seat = economySeats.find(seat => seat.seatNumber === seatNumber); + const isAvailable = seat ? seat.isAvailable : false; + return ( + <td key={cellIndex} className={isAvailable ? 'seat-available' : 'seat-unavailable'}> + {seatNumber} + </td> + ); + })} + </tr> + ))} + </tbody> + </table> + </div> </div> </div> </div> diff --git a/client/src/components/FlightList/FlightList.scss b/client/src/components/FlightList/FlightList.scss new file mode 100644 index 0000000000000000000000000000000000000000..90206bcd15fd1e55e6734e50a4735e5309f0fc51 --- /dev/null +++ b/client/src/components/FlightList/FlightList.scss @@ -0,0 +1,16 @@ +.full-flight-list { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; +} + +.full-flight-list-card { + display: flex; + align-items: center; + width: 20vw; + min-width: 350px; + max-height: 100%; + overflow-y: scroll; +} diff --git a/client/src/components/FlightList/FlightList.tsx b/client/src/components/FlightList/FlightList.tsx new file mode 100644 index 0000000000000000000000000000000000000000..819bfe3c900c52a4b17a81fa4ada963f99f692f6 --- /dev/null +++ b/client/src/components/FlightList/FlightList.tsx @@ -0,0 +1,31 @@ +import { useLoaderData } from 'react-router-dom'; +import { IFlight } from '../../services/Dashboard/CustomerDashboard'; +import FlightCard from '../Dashboard/FlightCard/FlightCard'; +import './FlightList.scss'; + +interface IFlightList { + $values: IFlight[]; +} + +function FlightList() { + const data = useLoaderData() as IFlightList | null; + const flights = data?.$values ?? []; + + return ( + <> + <div className='full-flight-list'> + <div className='card full-flight-list-card'> + { + flights.map((flight) => { + return ( + <FlightCard flight={flight} extraInfo={true} /> + ); + }) + } + </div> + </div> + </> + ); +} + +export default FlightList; diff --git a/client/src/components/Header/Header.tsx b/client/src/components/Header/Header.tsx index bbeca5560d3ca0867a2e46cbb75440e176c0cf2a..3fc4d51a4b15ef34897d22b2d69f33240ac5621b 100644 --- a/client/src/components/Header/Header.tsx +++ b/client/src/components/Header/Header.tsx @@ -27,6 +27,7 @@ function Header() { <div> <NavLink to={userToDashboard(user)} className={activeClass} >Dashboard</NavLink> {user?.type === 0 && <NavLink to={'booking/query'} className={activeClass}>Book a Flight</NavLink>} + {user?.type === 1 && <NavLink to={'flights'} className={activeClass}>Flight List</NavLink>} <NavLink to={'logout'} className={activeClass}>Logout</NavLink> </div> : <div> diff --git a/client/src/main.tsx b/client/src/main.tsx index 194da1fc83edf214ff0b16d18feeef909a70d38f..9904a9ee69eda4a8ddce9aa805745da420bd32b9 100644 --- a/client/src/main.tsx +++ b/client/src/main.tsx @@ -9,6 +9,7 @@ import ProtectedRoute from './components/ProtectedRoute/ProtectedRoute.tsx'; import InverseProtectedRoute from './components/ProtectedRoute/InverseProtectedRoute.tsx'; import Dashboard from './components/Dashboard/Dashboard.tsx'; import Flight from './components/Flight/Flight.tsx'; +import FlightList from './components/FlightList/FlightList.tsx'; import BookingQuery from './components/BookingQuery/BookingQuery.tsx'; import BookingList from './components/BookingList/BookingList.tsx'; import { AuthoriseUser } from './services/Authorise/Authorise.ts'; @@ -16,6 +17,7 @@ import { LogoutUser } from './services/Logout/Logout.ts'; import { GetCustomerDashboardData } from './services/Dashboard/CustomerDashboard.ts'; import { GetAirlineDashboardData } from './services/Dashboard/AirlineDashboard.ts'; import { GetFlightData } from './services/Flight/Flight.ts'; +import { GetFlightList } from './services/FlightList/FlightList.ts'; import { GetBookingList } from './services/BookingList/BookingList.ts'; import './index.scss'; @@ -61,6 +63,11 @@ const router = createBrowserRouter([ loader: GetFlightData, element: <Flight></Flight> }, + { + path: 'flights', + loader: GetFlightList, + element: <FlightList></FlightList> + }, { path: 'booking/query', element: <BookingQuery></BookingQuery> diff --git a/client/src/services/Dashboard/CustomerDashboard.ts b/client/src/services/Dashboard/CustomerDashboard.ts index 83a17d151e807dd59766cc3b7d2958b5e4191df2..c05a16f6b5ba461863973a17a342293dbb07b4f2 100644 --- a/client/src/services/Dashboard/CustomerDashboard.ts +++ b/client/src/services/Dashboard/CustomerDashboard.ts @@ -1,5 +1,13 @@ -export interface ISeats { +export interface ISeat { + id: number; + classType: number; + seatNumber: string; + isAvailable: boolean; +} +export interface ISeats { + $id: string; + $values: ISeat[]; } export interface IFlight { diff --git a/client/src/services/Flight/Flight.ts b/client/src/services/Flight/Flight.ts index 062e1ae90b9e5d35c2269cccb59c8c716feb651a..18ebb531017804ad977854ec2dd4d687ee22b77e 100644 --- a/client/src/services/Flight/Flight.ts +++ b/client/src/services/Flight/Flight.ts @@ -1,11 +1,21 @@ import { Params } from 'react-router-dom'; import Api from '../../helpers/Api'; -import { IFlight } from '../Dashboard/CustomerDashboard'; +import { IFlight, ISeats } from '../Dashboard/CustomerDashboard'; -export async function GetFlightData({ params }: { params: Params }): Promise<IFlight> { +export interface IFlightData { + flight: IFlight; + seats: ISeats; +} + +export async function GetFlightData({ params }: { params: Params }): Promise<IFlightData> { try { - const result = await Api.get(`Flight/${params.id}`, { withCredentials: true }); - return result.data; + const flight = Api.get(`Flight/${params.id}`, { withCredentials: true }); + const seats = Api.get(`Flight/${params.id}/seats`, { withCredentials: true }); + const [fdata, sdata] = await Promise.all([flight, seats]); + return { + flight: fdata.data, + seats: sdata.data + }; } catch (error ){ throw error; } diff --git a/client/src/services/FlightList/FlightList.ts b/client/src/services/FlightList/FlightList.ts new file mode 100644 index 0000000000000000000000000000000000000000..38e3a8e08ed319a918b14fb533193071b1f9ee09 --- /dev/null +++ b/client/src/services/FlightList/FlightList.ts @@ -0,0 +1,11 @@ +import { Params } from 'react-router-dom'; +import Api from '../../helpers/Api'; + +export async function GetFlightList({ params }: { params: Params }) { + try { + const result = await Api.get(`Flight?airlineId=${params.id}`, { withCredentials: true }); + return result.data; + } catch (error) { + return null; + } +}