diff --git a/client/src/components/BookingList/BookingCard/BookingCard.tsx b/client/src/components/BookingList/BookingCard/BookingCard.tsx index 29b598fd6ed675b6fc8894e9e6f1e4128b02733f..751b0e08e2f7b7d173059af89c001d19c71fc435 100644 --- a/client/src/components/BookingList/BookingCard/BookingCard.tsx +++ b/client/src/components/BookingList/BookingCard/BookingCard.tsx @@ -1,44 +1,59 @@ -import { useLocation } from 'react-router-dom'; -import { IFlight } from '../../../services/Dashboard/CustomerDashboard'; -import './BookingCard.scss'; +import { useLocation } from "react-router-dom"; +import { IFlight } from "../../../services/Dashboard/CustomerDashboard"; +import "./BookingCard.scss"; +import { + BookingOrder, + bookFlight, +} from "../../../services/BookingList/BookingOrder"; interface IBookingCard { - flight: IFlight + flight: IFlight; } function BookingCard({ flight }: IBookingCard) { const location = useLocation(); - const isEconomy = location.search.split('seatType=')[1] === 'economy'; + const isEconomy = location.search.split("seatType=")[1] === "economy"; - const handleClick = () => { - console.log('ready to redirect:', flight.id); + const handleClick = async (event: any) => { + event.preventDefault(); + //show loader + const bookingInfo: BookingOrder = { + flightId: flight.id, + bookingClass: isEconomy ? 1 : 2, + }; + await bookFlight(bookingInfo); + alert("Flight booked successfully, navigating to payments page..."); }; return ( <> - <div className='booking-card'> - <div className='booking-card-details'> - <div className='item'> + <div className="booking-card"> + <div className="booking-card-details"> + <div className="item"> <span>Departure Time:</span> <span>{flight.departureTime}</span> </div> - <div className='item'> + <div className="item"> <span>Arrival Time:</span> <span>{flight.arrivalTime}</span> </div> </div> - <div className='price'> - <div className='price-label'> - <span>{isEconomy ? 'Economy' : 'Business'} Price:</span> - <span>£{isEconomy ? flight.economyPrice : flight.businessPrice}</span> + <div className="price"> + <div className="price-label"> + <span>{isEconomy ? "Economy" : "Business"} Price:</span> + <span> + £{isEconomy ? flight.economyPrice : flight.businessPrice} + </span> </div> - <button type='button' onClick={handleClick}>Purchase</button> + <button type="button" onClick={handleClick}> + Purchase + </button> </div> </div> </> ); } -export default BookingCard; \ No newline at end of file +export default BookingCard; diff --git a/client/src/components/BookingView/BookingFlightCard.tsx b/client/src/components/BookingView/BookingFlightCard.tsx new file mode 100644 index 0000000000000000000000000000000000000000..80f23a71a8d5a1ca86a0b5a402912f5a69693f66 --- /dev/null +++ b/client/src/components/BookingView/BookingFlightCard.tsx @@ -0,0 +1,27 @@ +import { useEffect, useState } from "react"; +import { GetBookingFlight } from "../../services/BookingView/BookingView"; +import "./BookingView.scss"; +import { IFlight } from "../../services/Dashboard/CustomerDashboard"; +import { formatLongDateTime } from "../../helpers/DateTimeHelpers"; + +const BookingFlightCard = ({ flightId }: { flightId: number }) => { + const [flightData, setFlightData] = useState<IFlight | undefined>(); + + useEffect(() => { + GetBookingFlight(flightId).then((data) => { + setFlightData(data); + }); + }, [flightId]); + + return ( + <div className="card booking-view-card"> + <h3>Flight Information</h3> + <div>Origin: {flightData?.origin}</div> + <div>Destination: {flightData?.destination}</div> + <div>Depature: {formatLongDateTime(flightData?.departureTime)}</div> + <strong>Arrival: {formatLongDateTime(flightData?.arrivalTime)}</strong> + </div> + ); +}; + +export default BookingFlightCard; diff --git a/client/src/components/BookingView/BookingSeatCard.tsx b/client/src/components/BookingView/BookingSeatCard.tsx new file mode 100644 index 0000000000000000000000000000000000000000..2693fcfd873ef39923f59ffaf45f1ae5c4a3bd86 --- /dev/null +++ b/client/src/components/BookingView/BookingSeatCard.tsx @@ -0,0 +1,25 @@ +import { useEffect, useState } from "react"; +import { GetBookingSeat } from "../../services/BookingView/BookingView"; +import { ISeat } from "../../services/Dashboard/CustomerDashboard"; + +const BookingSeatCard = ({ seatId }: { seatId?: number }) => { + const [seatData, setSeatData] = useState<ISeat | undefined>(); + + useEffect(() => { + if (!seatId) { + return; + } + GetBookingSeat(seatId).then((data) => { + setSeatData(data); + }); + }, [seatId]); + + return ( + <div className="card booking-view-card"> + <h3>Seat Information</h3> + <div>Seat number: {seatData ? seatData.seatNumber : "N/A"}</div> + </div> + ); +}; + +export default BookingSeatCard; diff --git a/client/src/components/BookingView/BookingView.scss b/client/src/components/BookingView/BookingView.scss new file mode 100644 index 0000000000000000000000000000000000000000..81b2252909fd315e4130ed5b1260b4397c692b48 --- /dev/null +++ b/client/src/components/BookingView/BookingView.scss @@ -0,0 +1,13 @@ +.booking-view { + display: flex; + align-items: center; + justify-content: space-evenly; + width: 100%; + height: 100%; +} + +.booking-view-card { + width: 20vw; + min-width: 350px; + min-height: 200px; +} diff --git a/client/src/components/BookingView/BookingView.tsx b/client/src/components/BookingView/BookingView.tsx new file mode 100644 index 0000000000000000000000000000000000000000..0923ff7ac1ec2584193dbe4b5490b21c80349af6 --- /dev/null +++ b/client/src/components/BookingView/BookingView.tsx @@ -0,0 +1,18 @@ +import { useLoaderData } from "react-router-dom"; +import BookingFlightCard from "./BookingFlightCard"; +import BookingSeatCard from "./BookingSeatCard"; +import "./BookingView.scss"; +import { IBookingInfo } from "../../services/BookingView/BookingView"; + +const BookingView = () => { + const flightData = useLoaderData() as IBookingInfo | undefined; + + return ( + <div className="booking-view"> + {flightData && <BookingFlightCard flightId={flightData?.flightId} />} + {flightData && <BookingSeatCard seatId={flightData?.seatId} />} + </div> + ); +}; + +export default BookingView; diff --git a/client/src/components/CustomerBookings/CustomerBookingCard.tsx b/client/src/components/CustomerBookings/CustomerBookingCard.tsx new file mode 100644 index 0000000000000000000000000000000000000000..0369afeaf0e117a689d254077f5bd2b32176f936 --- /dev/null +++ b/client/src/components/CustomerBookings/CustomerBookingCard.tsx @@ -0,0 +1,67 @@ +import { useEffect, useState } from "react"; +import { formatLongDateTime } from "../../helpers/DateTimeHelpers"; +import { GetBookingFlight } from "../../services/BookingView/BookingView"; +import { IFlight } from "../../services/Dashboard/CustomerDashboard"; +import { useNavigate } from "react-router-dom"; + +const CustomerBookingCard = ({ + flightId, + flightType, + bookingId, +}: { + flightId: number; + flightType: string; + bookingId: number; +}) => { + const [flightData, setFlightData] = useState<IFlight | undefined>(); + const navigate = useNavigate(); + + const onClick = () => { + navigate(`/booking/${bookingId}`); + }; + useEffect(() => { + GetBookingFlight(flightId).then((data) => { + setFlightData(data); + }); + }, [flightId]); + return ( + <div> + {flightData ? ( + <div> + {new Date(flightData.departureTime).getTime() >= + new Date().getTime() && + flightType === "upcoming" && ( + <div className="card customer-booking-view-card"> + <div>Origin: {flightData?.origin}</div> + <div>Destination: {flightData?.destination}</div> + <div> + Depature: {formatLongDateTime(flightData?.departureTime)} + </div> + <button type="submit" className="view-more" onClick={onClick}> + View details + </button> + </div> + )} + + {new Date(flightData.departureTime).getTime() < + new Date().getTime() && + flightType === "previous" && ( + <div className="card customer-booking-view-card"> + <div>Origin: {flightData?.origin}</div> + <div>Destination: {flightData?.destination}</div> + <div> + Depature: {formatLongDateTime(flightData?.departureTime)} + </div> + <button type="submit" className="view-more" onClick={onClick}> + View details + </button> + </div> + )} + </div> + ) : ( + "" + )} + </div> + ); +}; +export default CustomerBookingCard; diff --git a/client/src/components/CustomerBookings/CustomerBookings.scss b/client/src/components/CustomerBookings/CustomerBookings.scss new file mode 100644 index 0000000000000000000000000000000000000000..37857b62a3b543b8b49b9c83c80f9ecdfdd2b21e --- /dev/null +++ b/client/src/components/CustomerBookings/CustomerBookings.scss @@ -0,0 +1,19 @@ +.customer-booking-view { + display: grid; + align-items: center; + width: 100%; + height: 100%; + justify-content: space-evenly; +} + +.customer-booking-view-card { + width: 20vw; + min-width: 350px; + min-height: 100px; + margin-top: 20px; +} + +.page-view { + margin-top: 30px; + margin-bottom: 30px; +} diff --git a/client/src/components/CustomerBookings/CustomerBookings.tsx b/client/src/components/CustomerBookings/CustomerBookings.tsx new file mode 100644 index 0000000000000000000000000000000000000000..df74fe7e72c711b92bcf73f49102a164d7f5dcac --- /dev/null +++ b/client/src/components/CustomerBookings/CustomerBookings.tsx @@ -0,0 +1,29 @@ +import { useLoaderData } from "react-router-dom"; +import "./CustomerBookings.scss"; +import { IBookingInfo } from "../../services/BookingView/BookingView"; +import CustomerBookingCard from "./CustomerBookingCard"; + +const CustomerBookings = () => { + const bookings = useLoaderData() as IBookingInfo[]; + let flightType = location.search.split("flightType=")[1]?.split("&")[0]; + if (typeof flightType === "undefined") { + flightType = "upcoming"; + } + + return ( + <div className="page-view"> + <div className="customer-booking-view"> + <h3>List of {flightType} bookings</h3> + {bookings.map((booking) => ( + <CustomerBookingCard + flightId={booking.flightId} + flightType={flightType} + bookingId={booking.id} + /> + ))} + </div> + </div> + ); +}; + +export default CustomerBookings; diff --git a/client/src/components/Dashboard/Flights/Flights.tsx b/client/src/components/Dashboard/Flights/Flights.tsx index 20a824586fb6cb3f60a58c04614413bd58160548..0fcf82d075d0a09f4ab6d948a7ea946938104756 100644 --- a/client/src/components/Dashboard/Flights/Flights.tsx +++ b/client/src/components/Dashboard/Flights/Flights.tsx @@ -1,71 +1,96 @@ -import { useLoaderData } from 'react-router'; -import { ICustomerDashboardData } from '../../../services/Dashboard/CustomerDashboard'; -import { IAirlineDashboardData } from '../../../services/Dashboard/AirlineDashboard'; -import FlightCard from '../FlightCard/FlightCard'; -import './Flights.scss'; -import { Link } from 'react-router-dom'; +import { useLoaderData } from "react-router"; +import { ICustomerDashboardData } from "../../../services/Dashboard/CustomerDashboard"; +import { IAirlineDashboardData } from "../../../services/Dashboard/AirlineDashboard"; +import FlightCard from "../FlightCard/FlightCard"; +import "./Flights.scss"; +import { Link, useNavigate } from "react-router-dom"; function Flights() { - const data = useLoaderData() as ICustomerDashboardData | IAirlineDashboardData; - if (data.type === 'customer') { + const data = useLoaderData() as + | ICustomerDashboardData + | IAirlineDashboardData; + const navigate = useNavigate(); + + const onClick = (flightType: string) => { + navigate(`/customer/bookings?flightType=${flightType}`); + }; + if (data.type === "customer") { return ( <> - <div className='flights'> - <div className='flex-row'> - <span className='flights-title'>Upcoming Flights</span> - <Link to={"/"}> - <button type='button' className='view-more'>View more</button> - </Link> + <div className="flights"> + <div className="flex-row"> + <span className="flights-title">Upcoming Flights</span> + <button + type="button" + className="view-more" + onClick={() => onClick("upcoming")} + > + View more + </button> </div> - <div className='flight-list'> - {data.upcomingFlights.length > 0 - ? data.upcomingFlights.slice(0,3).map((item) => { - // Only pass the flight data to the FlightCard - return <FlightCard key={item.flight.id} flight={item.flight}></FlightCard> - }) - : <div>No Upcoming Flights</div>} + <div className="flight-list"> + {data.upcomingFlights.length > 0 ? ( + data.upcomingFlights.slice(0, 3).map((item) => { + // Only pass the flight data to the FlightCard + return <FlightCard key={item.id} flight={item}></FlightCard>; + }) + ) : ( + <div>No Upcoming Flights</div> + )} </div> </div> - - <div className='flights'> - <div className='flex-row'> - <span className='flights-title'>Flights History</span> - <button type='button' className='view-more'>View more</button> + + <div className="flights"> + <div className="flex-row"> + <span className="flights-title">Flights History</span> + <button + type="submit" + className="view-more" + onClick={() => onClick("previous")} + > + View more + </button> </div> - <div className='flight-list'> - {data.flightsHistory.length > 0 - ? data.flightsHistory.slice(0,3).map((item) => { - // Only pass the flight data to the FlightCard - return <FlightCard key={item.flight.id} flight={item.flight}></FlightCard> - }) - : <div>No Flights History</div>} + <div className="flight-list"> + {data.flightsHistory.length > 0 ? ( + data.flightsHistory.slice(0, 3).map((item) => { + // Only pass the flight data to the FlightCard + return <FlightCard key={item.id} flight={item}></FlightCard>; + }) + ) : ( + <div>No Flights History</div> + )} </div> </div> </> ); - } - else { + } else { return ( <> - <div className='flights'> - <div className='flex-row'> - <span className='flights-title'>Flights List</span> + <div className="flights"> + <div className="flex-row"> + <span className="flights-title">Flights List</span> <Link to="/register-flight"> - <button type='button' className='view-more'>Add flight</button> + <button type="button" className="view-more"> + Add flight + </button> </Link> </div> - <div className='flight-list'> - {data.flightList.length > 0 - ? data.flightList.slice(0,3).map((flight) => { - return <FlightCard key={flight.id} flight={flight}></FlightCard> - }) - : <div>No Flights have been created yet.</div>} + <div className="flight-list"> + {data.flightList.length > 0 ? ( + data.flightList.slice(0, 3).map((flight) => { + return ( + <FlightCard key={flight.id} flight={flight}></FlightCard> + ); + }) + ) : ( + <div>No Flights have been created yet.</div> + )} </div> </div> </> ); } - } export default Flights; diff --git a/client/src/components/Register/Register.tsx b/client/src/components/Register/Register.tsx index 5b85c96545c6439751bc1e763b86e8b09052a177..e44fae177b10afb86bd5cb0e00579f1cd90017c5 100644 --- a/client/src/components/Register/Register.tsx +++ b/client/src/components/Register/Register.tsx @@ -1,11 +1,11 @@ -import { useState } from 'react'; -import { useNavigate } from 'react-router-dom'; -import { useForm } from 'react-hook-form'; -import { AxiosError } from 'axios'; -import { registerUser } from '../../services/Register/Register'; -import { useAuth } from '../../hooks/useAuth'; -import './Register.scss'; -import { userToDashboard } from '../../helpers/UserType'; +import { useState } from "react"; +import { useNavigate } from "react-router-dom"; +import { useForm } from "react-hook-form"; +import { AxiosError } from "axios"; +import { registerUser } from "../../services/Register/Register"; +import { useAuth } from "../../hooks/useAuth"; +import "./Register.scss"; +import { userToDashboard } from "../../helpers/UserType"; import UserStorage from "../../helpers/UserStorage"; export interface IRegisterForm { @@ -19,85 +19,101 @@ export interface IRegisterForm { export function Register() { const { giveAuth, updateUser } = useAuth(); const navigate = useNavigate(); - const [error, setError] = useState(''); - const { register, handleSubmit } = useForm<IRegisterForm>({mode: 'onChange'}); + const [error, setError] = useState(""); + const { register, handleSubmit } = useForm<IRegisterForm>({ + mode: "onChange", + }); const onSubmit = async (formValue: IRegisterForm) => { if (formValue.password.length < 7) { - setError('password length must be greater than 6 characters'); + setError("password length must be greater than 6 characters"); return; } if (formValue.password !== formValue.confirmPassword) { - setError('password and confirm password must match'); + setError("password and confirm password must match"); return; } - setError(''); + setError(""); try { const result = await registerUser(formValue); giveAuth(); const userId = result.data.id; - UserStorage.storeUserId(userId) + UserStorage.storeUserId(userId); updateUser(result.data); navigate(`/${userToDashboard(result.data)}`); } catch (error) { const errorMessage = (error as AxiosError).response?.data; - if (typeof errorMessage == 'string') { + if (typeof errorMessage == "string") { setError(errorMessage); } else { - setError('An unexpected error has occurred'); + setError("An unexpected error has occurred"); } } }; return ( <> - <div className='register'> + <div className="register"> <form onSubmit={handleSubmit(onSubmit)}> - <div className='card register-card'> - <div className='form-group'> + <div className="card register-card"> + <div className="form-group"> <label>Full Name</label> - <input type='text' placeholder='Full name' {...register('name', { required: true })} /> + <input + type="text" + placeholder="Full name" + {...register("name", { required: true })} + /> </div> - - <div className='form-group'> + + <div className="form-group"> <label>Email Address</label> - <input type='email' placeholder='Enter email' {...register('email', { required: true })} /> + <input + type="email" + placeholder="Enter email" + {...register("email", { required: true })} + /> </div> - <div className='form-group'> + <div className="form-group"> <label>Password</label> - <input type='password' placeholder='Enter password' {...register('password', { required: true })} /> + <input + type="password" + placeholder="Enter password" + {...register("password", { required: true })} + /> </div> - <div className='form-group'> + <div className="form-group"> <label>Confirm Password</label> - <input type='password' placeholder='Confirm password' {...register('confirmPassword', { required: true })} /> + <input + type="password" + placeholder="Confirm password" + {...register("confirmPassword", { required: true })} + /> </div> - <div className='form-group'> + <div className="form-group"> <label>Customer Type</label> - <select {...register('customerType', { required: true })}> + <select {...register("customerType", { required: true })}> <option value="customer">Customer</option> <option value="airline">Airline</option> </select> </div> - <div className='form-group'> - <button type='submit'>Submit</button> + <div className="form-group"> + <button type="submit">Submit</button> </div> - <div className='form-group'> - {error && <span>{error}</span>} - </div> + <div className="form-group">{error && <span>{error}</span>}</div> </div> </form> </div> </> - ); + ); } export default Register; diff --git a/client/src/helpers/DateTimeHelpers.ts b/client/src/helpers/DateTimeHelpers.ts new file mode 100644 index 0000000000000000000000000000000000000000..c268c90caf659a847f4c3c904bf8493ace3b9f14 --- /dev/null +++ b/client/src/helpers/DateTimeHelpers.ts @@ -0,0 +1,18 @@ +export const formatLongDateTime = (dateTimeInput?: string) => { + if ( + dateTimeInput === null || + typeof dateTimeInput === "undefined" || + dateTimeInput === "" + ) { + return ""; + } else { + return new Date(dateTimeInput).toLocaleString("en-GB", { + hour12: true, + month: "short", + year: "numeric", + day: "2-digit", + hour: "2-digit", + minute: "2-digit", + }); //toLocaleString([], { hour12: true }); + } +}; diff --git a/client/src/main.tsx b/client/src/main.tsx index 1989c3ac260a199058faef4752e097c990105822..432d751df32800f4202e0108812dd36ce09ebe78 100644 --- a/client/src/main.tsx +++ b/client/src/main.tsx @@ -1,30 +1,34 @@ -import React from 'react'; -import ReactDOM from 'react-dom/client'; -import { createBrowserRouter, RouterProvider } from 'react-router-dom'; -import App from './App.tsx'; -import Login from './components/Login/Login.tsx'; -import Register from './components/Register/Register.tsx'; -import Logout from './components/Logout/Logout.tsx'; -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'; -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'; -import FlightCreationForm from './components/FlightCreationForm/FlightCreationForm.tsx'; +import React from "react"; +import ReactDOM from "react-dom/client"; +import { createBrowserRouter, RouterProvider } from "react-router-dom"; +import App from "./App.tsx"; +import Login from "./components/Login/Login.tsx"; +import Register from "./components/Register/Register.tsx"; +import Logout from "./components/Logout/Logout.tsx"; +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"; +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"; +import FlightCreationForm from "./components/FlightCreationForm/FlightCreationForm.tsx"; +import { GetBookingInformation } from "./services/BookingView/BookingView.ts"; +import { GetAllBookings } from "./services/CustomerBookings/CustomerBookings.ts"; +import CustomerBookings from "./components/CustomerBookings/CustomerBookings.tsx"; +import BookingView from "./components/BookingView/BookingView.tsx"; const router = createBrowserRouter([ { - path: '/', + path: "/", loader: AuthoriseUser, element: <App></App>, children: [ @@ -32,64 +36,74 @@ const router = createBrowserRouter([ element: <InverseProtectedRoute></InverseProtectedRoute>, children: [ { - path: 'login', - element: <Login></Login> + path: "login", + element: <Login></Login>, }, { - path: 'register', - element: <Register></Register> - } - ] + path: "register", + element: <Register></Register>, + }, + ], }, { element: <ProtectedRoute></ProtectedRoute>, children: [ { - path: 'logout', + path: "logout", loader: LogoutUser, - element: <Logout></Logout> + element: <Logout></Logout>, }, { - path: 'customer-dashboard', + path: "customer-dashboard", loader: GetCustomerDashboardData, - element: <Dashboard></Dashboard> + element: <Dashboard></Dashboard>, }, { - path: 'airline-dashboard', + path: "airline-dashboard", loader: GetAirlineDashboardData, - element: <Dashboard></Dashboard> + element: <Dashboard></Dashboard>, }, { - path: 'flight/:id', + path: "flight/:id", loader: GetFlightData, - element: <Flight></Flight> + element: <Flight></Flight>, }, { - path: 'flights', + path: "flights", loader: GetFlightList, - element: <FlightList></FlightList> + element: <FlightList></FlightList>, }, { - path: 'booking/query', - element: <BookingQuery></BookingQuery> + path: "booking/query", + element: <BookingQuery></BookingQuery>, }, { - path: 'booking/list', + path: "booking/list", loader: GetBookingList, - element: <BookingList></BookingList> + element: <BookingList></BookingList>, + }, + { + path: "register-flight", + element: <FlightCreationForm></FlightCreationForm>, }, { - path: 'register-flight', - element: <FlightCreationForm></FlightCreationForm> - } - ] - } - ] - } + path: "booking/:id", + loader: GetBookingInformation, + element: <BookingView></BookingView>, + }, + { + path: "customer/bookings", + loader: GetAllBookings, + element: <CustomerBookings></CustomerBookings>, + }, + ], + }, + ], + }, ]); -ReactDOM.createRoot(document.getElementById('root')!).render( +ReactDOM.createRoot(document.getElementById("root")!).render( <React.StrictMode> <RouterProvider router={router}></RouterProvider> - </React.StrictMode>, -) + </React.StrictMode> +); diff --git a/client/src/services/BookingList/BookingList.ts b/client/src/services/BookingList/BookingList.ts index 5d593cb6fd7e7dc1a50e619fa4a2d450afb92967..6c6d2e9393ccb8231028ddf589f25c2a9324dbeb 100644 --- a/client/src/services/BookingList/BookingList.ts +++ b/client/src/services/BookingList/BookingList.ts @@ -1,19 +1,30 @@ -import { getSearchParam } from '../../helpers/SearchParams'; -import { IFlight } from '../Dashboard/CustomerDashboard'; +import Api from "../../helpers/Api"; +import { getSearchParam } from "../../helpers/SearchParams"; +import { IFlight } from "../Dashboard/CustomerDashboard"; export interface IBookingList { - flights: IFlight[] + flights: IFlight[]; } -export async function GetBookingList({ request }: { request: Request}): Promise<IBookingList> { - const origin = getSearchParam(request.url, 'origin'); - const destination = getSearchParam(request.url, 'destination'); - const date = getSearchParam(request.url, 'date'); - const seatType = getSearchParam(request.url, 'seatType'); +export async function GetBookingList({ + request, +}: { + request: Request; +}): Promise<IBookingList> { + const origin = getSearchParam(request.url, "origin"); + const destination = getSearchParam(request.url, "destination"); + const date = getSearchParam(request.url, "date"); + const seatType = getSearchParam(request.url, "seatType"); - console.log('ready to call API with:', origin, destination, date, seatType); + const fullUrl = `Flight?origin=${origin}&destination=${destination}&departureDate=${date}&seatType=${seatType}`; - return { - flights: [] - }; + try { + const flight = await Api.get(`${fullUrl}`, { withCredentials: true }); + + return { + flights: flight.data.$values, + }; + } catch (error) { + throw error; + } } diff --git a/client/src/services/BookingList/BookingOrder.ts b/client/src/services/BookingList/BookingOrder.ts new file mode 100644 index 0000000000000000000000000000000000000000..a46318ce8af122c34284cdf93b5e910f624ae80c --- /dev/null +++ b/client/src/services/BookingList/BookingOrder.ts @@ -0,0 +1,15 @@ +import { AxiosResponse } from "axios"; +import Api from "../../helpers/Api"; +import { IFlight } from "../Dashboard/CustomerDashboard"; + +export interface BookingOrder { + flightId: number; + bookingClass: number; + seatId?: number; +} + +export async function bookFlight( + form: BookingOrder +): Promise<AxiosResponse<IFlight>> { + return Api.post("Booking", form, { withCredentials: true }); +} diff --git a/client/src/services/BookingView/BookingView.ts b/client/src/services/BookingView/BookingView.ts new file mode 100644 index 0000000000000000000000000000000000000000..2aa5ef16b883f9b70b156ce5d0ca02e6ad36cfd0 --- /dev/null +++ b/client/src/services/BookingView/BookingView.ts @@ -0,0 +1,79 @@ +import { Params } from "react-router-dom"; +import Api from "../../helpers/Api"; +import { IFlight, ISeat } from "../Dashboard/CustomerDashboard"; + +export interface IBookingInfo { + id: number; + flightId: number; + userId: number; + bookingClass: number; + seatId?: number; +} + +export interface IBookingData { + flight: IFlight; + seat: ISeat; +} + +export async function GetBookingInformation({ + params, +}: { + params: Params; +}): Promise<IBookingInfo> { + const url = `Booking/${params.id}`; + try { + const booking = await Api.get(`${url}`, { withCredentials: true }); + + return booking.data as IBookingInfo; + } catch (error) { + throw error; + } +} + +export async function GetBookingFlight( + flightId?: number +): Promise<IFlight | undefined> { + if (!flightId) { + return undefined; + } + try { + const flights = await Api.get(`Flight/${flightId}`, { + withCredentials: true, + }); + + return flights.data as IFlight; + } catch (error) { + throw error; + } +} + +export async function GetBookingSeat(seatId: number): Promise<ISeat> { + try { + const seat = await Api.get(`Seat/${seatId}`, { + withCredentials: true, + }); + + return seat.data as ISeat; + } catch (error) { + throw error; + } +} + +export async function GetFullBookingData( + flightId: number, + seatId: number +): Promise<IBookingData> { + try { + const flight = Api.get(`Flight/${flightId}`, { withCredentials: true }); + const seats = Api.get(`Seat/${seatId}/seats`, { + withCredentials: true, + }); + const [fdata, sdata] = await Promise.all([flight, seats]); + return { + flight: fdata.data, + seat: sdata.data, + }; + } catch (error) { + throw error; + } +} diff --git a/client/src/services/CustomerBookings/CustomerBookings.ts b/client/src/services/CustomerBookings/CustomerBookings.ts new file mode 100644 index 0000000000000000000000000000000000000000..c01b71f3d1c1e563ea7e48a82310ccf0337d114f --- /dev/null +++ b/client/src/services/CustomerBookings/CustomerBookings.ts @@ -0,0 +1,26 @@ +import Api from "../../helpers/Api"; +import { IFlight, ISeat } from "../Dashboard/CustomerDashboard"; + +export interface IBookingInfo { + id: number; + flightId: number; + userId: number; + bookingClass: number; + seatId?: number; +} + +export interface IBookingData { + flight: IFlight; + seat: ISeat; +} + +export async function GetAllBookings(): Promise<IBookingInfo[]> { + const url = `Booking`; + try { + const booking = await Api.get(`${url}`, { withCredentials: true }); + + return booking.data as IBookingInfo[]; + } catch (error) { + throw error; + } +} diff --git a/client/src/services/Dashboard/AirlineDashboard.ts b/client/src/services/Dashboard/AirlineDashboard.ts index b0e1dda10fcbd384fa1f04b9c69cee1bcdb3abdf..b08849969a690ca3e6ce76cbb31a1ca228552fda 100644 --- a/client/src/services/Dashboard/AirlineDashboard.ts +++ b/client/src/services/Dashboard/AirlineDashboard.ts @@ -23,24 +23,30 @@ export interface IFlight { businessCapacity: number; economyPrice: number; businessPrice: number; - seats: ISeats + seats: ISeats; } export interface IAirlineDashboardData { - type: 'airline' + type: "airline"; flightList: IFlight[]; } -export async function GetAirlineDashboardData({request}: {request: Request}): Promise<IAirlineDashboardData> { +export async function GetAirlineDashboardData({ + request, +}: { + request: Request; +}): Promise<IAirlineDashboardData> { try { const id = UserStorage.getUserId(); - const response = await Api.get(`Flight?airlineId=${id}`, { withCredentials: true }); + const response = await Api.get(`Flight?airlineId=${id}`, { + withCredentials: true, + }); const flights = response.data.$values; return { - type: 'airline', - flightList: flights + type: "airline", + flightList: flights, }; } catch (error) { throw error; } -} \ No newline at end of file +}