diff --git a/.env b/.env index 28e4f237c3b58cb9062b03189e4c31dbb7d58033..b493772cf891af9bd4c23f4127f3a65ecfc41eb3 100644 --- a/.env +++ b/.env @@ -1,5 +1,5 @@ # Image settings -IMAGE_TAG=0.0.1 +IMAGE_TAG=latest MYSQL_IMAGE_TAG=8.0 @@ -10,12 +10,6 @@ DB_USER=user DB_PASSWORD=user DB_CHARSET=utf8mb4 -# Keeping these for dev purposes, will remove at the end -#DB_PORT=3308 -#DB_NAME=AspNetCoreDb -#DB_USER=root -#DB_PASSWORD= -#DB_CHARSET=utf8mb4 # Service ports USER_MICROSERVICE_PORT=5089 diff --git a/BookingMicroservice/Controllers/BookingController.cs b/BookingMicroservice/Controllers/BookingController.cs index 0a2584ea2955302d68389d4bf7053104fea6df70..97e4acff3093e39df73d2e9aa1defacc88e486cd 100644 --- a/BookingMicroservice/Controllers/BookingController.cs +++ b/BookingMicroservice/Controllers/BookingController.cs @@ -20,14 +20,11 @@ namespace BookingMicroservice.Controllers private readonly IBookingService bookingService; private readonly IStripeService stripeService; - private readonly IConfiguration configuration; - - public BookingController(IReservationComplianceService reservationComplianceService, IBookingService bookingService, IStripeService stripeService, IConfiguration configuration) + public BookingController(IReservationComplianceService reservationComplianceService, IBookingService bookingService, IStripeService stripeService) { this.reservationComplianceService = reservationComplianceService; this.bookingService = bookingService; this.stripeService = stripeService; - this.configuration = configuration; } [Authorize] @@ -144,7 +141,7 @@ namespace BookingMicroservice.Controllers [Authorize] [HttpPost("create-payment-intent")] - public async Task<IActionResult> CreatePaymentIntent([FromBody] PaymentIntentCreateRequest request) + public IActionResult CreatePaymentIntent([FromBody] PaymentIntentCreateRequest request) { var paymentIntentService = new PaymentIntentService(); var paymentIntent = paymentIntentService.Create(new PaymentIntentCreateOptions diff --git a/BookingMicroservice/Services/ReservationComplianceService.cs b/BookingMicroservice/Services/ReservationComplianceService.cs index ab80ec322556c4d10f6655ac543503f29668be55..b922d428efacc55ef70f6ec8b1828cb1c2ea8a6c 100644 --- a/BookingMicroservice/Services/ReservationComplianceService.cs +++ b/BookingMicroservice/Services/ReservationComplianceService.cs @@ -22,7 +22,9 @@ namespace BookingMicroservice.Services public async Task<Booking?> TryCreateBookingAsync(int flightId, int userId, BookingClass bookingClass, int? seatId) { - HttpResponseMessage capacityResponse = await flightServiceClient.GetFlightCapacityAsync(flightId, (int)bookingClass); + int bookingClassValue = BookingClass.BUSINESS == bookingClass ? 0 : 1; + + HttpResponseMessage capacityResponse = await flightServiceClient.GetFlightCapacityAsync(flightId, bookingClassValue); if (!capacityResponse.IsSuccessStatusCode) throw new InvalidOperationException("Could not retrieve flight capacity."); diff --git a/Database/InitTables.sql b/Database/1-InitTables.sql similarity index 100% rename from Database/InitTables.sql rename to Database/1-InitTables.sql diff --git a/Database/2-DefaultValues.sql b/Database/2-DefaultValues.sql new file mode 100644 index 0000000000000000000000000000000000000000..59531e561e831a680ad71fcad808191876b07d90 --- /dev/null +++ b/Database/2-DefaultValues.sql @@ -0,0 +1,78 @@ +INSERT INTO Users (Username, Email, PasswordHash, Type) VALUES +('SkyHigh Airlines', 'skyHigh@gmail.com', 'AQAAAAIAAYagAAAAEL9Xj4ZjCgTc/w0XbnjHE63KPvhYmJdTZV9k570Bw0QuPsrupU8EGS7kT7hUoGZdbg==', 1); + +INSERT INTO Flights (AirlineId, Origin, Destination, DepartureTime, ArrivalTime, EconomyCapacity, BusinessCapacity, EconomyPrice, BusinessPrice) VALUES +(1, 'London Heathrow', 'Liverpool', '2024-07-15 08:00:00', '2024-07-15 12:00:00', 24, 8, 200.00, 500.00), +(1, 'London Heathrow', 'Glasgow', '2024-07-15 08:00:00', '2024-07-15 12:00:00', 24, 8, 150.00, 350.00); + + +INSERT INTO Seats (FlightId, SeatNumber, ClassType, IsAvailable) VALUES +(1, '1A', 0, 1), +(1, '1B', 0, 1), +(1, '1C', 0, 1), +(1, '1D', 0, 1), +(1, '2A', 0, 1), +(1, '2B', 0, 1), +(1, '2C', 0, 1), +(1, '2D', 0, 1), +(2, '1A', 0, 1), +(2, '1B', 0, 1), +(2, '1C', 0, 1), +(2, '1D', 0, 1), +(2, '2A', 0, 1), +(2, '2B', 0, 1), +(2, '2C', 0, 1), +(2, '2D', 0, 1); + +INSERT INTO Seats (FlightId, SeatNumber, ClassType, IsAvailable) VALUES +(1, '3A', 1, 1), +(1, '3B', 1, 1), +(1, '3C', 1, 1), +(1, '3D', 1, 1), +(1, '3E', 1, 1), +(1, '3F', 1, 1), +(1, '4A', 1, 1), +(1, '4B', 1, 1), +(1, '4C', 1, 1), +(1, '4D', 1, 1), +(1, '4E', 1, 1), +(1, '4F', 1, 1), +(1, '5A', 1, 1), +(1, '5B', 1, 1), +(1, '5C', 1, 1), +(1, '5D', 1, 1), +(1, '5E', 1, 1), +(1, '5F', 1, 1), +(1, '6A', 1, 1), +(1, '6B', 1, 1), +(1, '6C', 1, 1), +(1, '6D', 1, 1), +(1, '6E', 1, 1), +(1, '6F', 1, 1), +(2, '3A', 1, 1), +(2, '3B', 1, 1), +(2, '3C', 1, 1), +(2, '3D', 1, 1), +(2, '3E', 1, 1), +(2, '3F', 1, 1), +(2, '4A', 1, 1), +(2, '4B', 1, 1), +(2, '4C', 1, 1), +(2, '4D', 1, 1), +(2, '4E', 1, 1), +(2, '4F', 1, 1), +(2, '5A', 1, 1), +(2, '5B', 1, 1), +(2, '5C', 1, 1), +(2, '5D', 1, 1), +(2, '5E', 1, 1), +(2, '5F', 1, 1), +(2, '6A', 1, 1), +(2, '6B', 1, 1), +(2, '6C', 1, 1), +(2, '6D', 1, 1), +(2, '6E', 1, 1), +(2, '6F', 1, 1); + + + diff --git a/FlightMicroservice/Controllers/FlightController.cs b/FlightMicroservice/Controllers/FlightController.cs index 5ab973f5631279baeb86782eba3b05e8ee1e20e6..0e2803db0bc7b39ca258ec11b95aac0580b96b7c 100644 --- a/FlightMicroservice/Controllers/FlightController.cs +++ b/FlightMicroservice/Controllers/FlightController.cs @@ -56,7 +56,7 @@ namespace FlightMicroservice.Controllers } [HttpGet("{flightId}/capacity")] - public ActionResult GetFlightCapacity([FromRoute] int flightId, [FromQuery] ClassType classType) + public ActionResult GetFlightCapacity([FromRoute] int flightId, [FromQuery] ClassType classType = ClassType.BUSINESS) { Flight? flight = flightService.GetFlight(flightId); if (flight == null) diff --git a/GatewayAPI/Program.cs b/GatewayAPI/Program.cs index 0ed1620f865b10aeecf2071d9b334f1e7e479425..219afdc004c80be47abb0e2dbed17cdaa8a01366 100644 --- a/GatewayAPI/Program.cs +++ b/GatewayAPI/Program.cs @@ -9,17 +9,8 @@ builder.Services.AddControllers(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); -// Add CORS service -builder.Services.AddCors(options => -{ - options.AddPolicy("AllowAirlineClient", builder => - { - builder.WithOrigins("http://localhost:4200") - .AllowAnyMethod() - .AllowAnyHeader() - .AllowCredentials(); - }); -}); +// Add CORS Policy +builder.Services.AddCorsPolicy(builder.Configuration); // Add Http Typed Clients for each Microservice builder.Services.AddHttpClients(builder.Configuration); diff --git a/GatewayAPI/ServiceCollectionExtensions.cs b/GatewayAPI/ServiceCollectionExtensions.cs index 25d509a6376142baed07f611fd0fa05134283fee..a0b0c410294345b44ea43003e1e8c7f729de9923 100644 --- a/GatewayAPI/ServiceCollectionExtensions.cs +++ b/GatewayAPI/ServiceCollectionExtensions.cs @@ -29,5 +29,22 @@ namespace GatewayAPI return services; } + + public static IServiceCollection AddCorsPolicy(this IServiceCollection services, ConfigurationManager configurationManager) + { + string baseUrl = configurationManager["ClientService:BaseUrl"] ?? throw new InvalidOperationException("ClientService BaseUrl is not configured."); + services.AddCors(options => + { + options.AddPolicy("AllowAirlineClient", builder => + { + builder.WithOrigins(baseUrl) + .AllowAnyMethod() + .AllowAnyHeader() + .AllowCredentials(); + }); + }); + + return services; + } } } diff --git a/GatewayAPI/appsettings.json b/GatewayAPI/appsettings.json index a5a7960c6015131a93db0f6f52bdbb4192f56f45..ef8baaf50b96554c929495cf6ece7dfd18414bd7 100644 --- a/GatewayAPI/appsettings.json +++ b/GatewayAPI/appsettings.json @@ -14,5 +14,8 @@ }, "BookingMicroservice": { "BaseUrl": "http://localhost:5207" + }, + "ClientService": { + "BaseUrl": "http://localhost:4200" } } diff --git a/client/src/components/CustomerBookings/CustomerBookings.tsx b/client/src/components/CustomerBookings/CustomerBookings.tsx index 62078c26f9d61f09870a677a3a7dac0a6d04f105..b7116ce14108ebc2bd5302018d46418d0a985308 100644 --- a/client/src/components/CustomerBookings/CustomerBookings.tsx +++ b/client/src/components/CustomerBookings/CustomerBookings.tsx @@ -13,7 +13,7 @@ const CustomerBookings = () => { return ( <div className="page-view"> <div className="customer-booking-view"> - <h3>List of {flightType} bookings</h3> + <h3>List of bookings</h3> {bookings.map((booking) => ( <CustomerBookingCard flightId={booking.flightId} diff --git a/client/src/components/FlightCreationForm/FlightCreationForm.tsx b/client/src/components/FlightCreationForm/FlightCreationForm.tsx index faf45435eb3202a84e4a2b1aa16a340c14dd0b4a..11c323487e9c39765cb00d5f5d0562b31c065cf5 100644 --- a/client/src/components/FlightCreationForm/FlightCreationForm.tsx +++ b/client/src/components/FlightCreationForm/FlightCreationForm.tsx @@ -49,6 +49,16 @@ function FlightCreationForm() { return; } + const today = new Date(); + today.setHours(0, 0, 0, 0); // Reset time to midnight for an accurate date-only comparison + const departureDate = new Date(formValue.departure); + departureDate.setHours(0, 0, 0, 0); // Ensure time components do not affect comparison + + if (departureDate <= today) { + setError('Departure date must be at least for tomorrow.'); + return; + } + setError(''); setDisabled(true); diff --git a/client/src/components/Header/Header.tsx b/client/src/components/Header/Header.tsx index 377f39effaaa9e32ba7d064cc905f3a90771da62..e5efbdbf6ed833565bf8e1dbf7277421928c5ca2 100644 --- a/client/src/components/Header/Header.tsx +++ b/client/src/components/Header/Header.tsx @@ -26,7 +26,7 @@ function Header() { {isAuth ? <div> <NavLink to={userToDashboard(user)} className={activeClass} >Dashboard</NavLink> - {user?.type === 0 && <NavLink to={'customer/bookings'} className={activeClass}>Upcoming Bookings</NavLink>} + {user?.type === 0 && <NavLink to={'customer/bookings'} className={activeClass}>Bookings List</NavLink>} {user?.type === 0 && <NavLink to={'booking/query'} className={activeClass}>Book a Flight</NavLink>} {user?.type === 1 && <NavLink to={`flights?id=${user.id}`} className={activeClass}>Flight List</NavLink>} <NavLink to={'logout'} className={activeClass}>Logout</NavLink> diff --git a/client/src/components/PaymentForm/PaymentForm.tsx b/client/src/components/PaymentForm/PaymentForm.tsx index c70666afa78d8aef8e4887d2a1116775e63e0992..242657914e9d951ca91126320d79bcea86f03c23 100644 --- a/client/src/components/PaymentForm/PaymentForm.tsx +++ b/client/src/components/PaymentForm/PaymentForm.tsx @@ -77,7 +77,7 @@ const PaymentForm = () => { } } catch (error) { console.error('Payment confirmation failed:', error); - setMessage("An error occurred while confirming the payment. Error: ${error.message}"); + setMessage(`An error occurred while confirming the payment.`); } finally { setLoading(false); } diff --git a/client/src/services/BookingView/BookingView.ts b/client/src/services/BookingView/BookingView.ts index 509ede07860189b2ae5bb54c23cd5ac5c62320db..c11be3ebfe54af1c0c3176b1cdd09002917df148 100644 --- a/client/src/services/BookingView/BookingView.ts +++ b/client/src/services/BookingView/BookingView.ts @@ -31,19 +31,17 @@ export async function GetBookingInformation({ params: Params; }): Promise<IFullBookingInfo> { const url = `Booking/${params.id}`; - try { - const bookingCall = Api.get(`${url}`, { withCredentials: true }); - const seatsCall = Api.get(`Flight/${params.id}/seats`, { withCredentials: true }); - - const [booking, seats] = await Promise.all([bookingCall, seatsCall]); - return { - booking: booking.data, - seats: seats.data - }; - } catch (error) { - throw error; - } + const bookingResponse = await Api.get(url, { withCredentials: true }); + const flightId = bookingResponse.data.flightId; + + const seatsResponse = await Api.get(`Flight/${flightId}/seats`, { withCredentials: true }); + + return { + booking: bookingResponse.data, + seats: seatsResponse.data + }; + } export async function GetBookingFlight( diff --git a/client/src/services/CustomerBookings/CustomerBookings.ts b/client/src/services/CustomerBookings/CustomerBookings.ts index c01b71f3d1c1e563ea7e48a82310ccf0337d114f..e10fdfbf83ae103e3b7de13946d4e36db7167ccf 100644 --- a/client/src/services/CustomerBookings/CustomerBookings.ts +++ b/client/src/services/CustomerBookings/CustomerBookings.ts @@ -1,4 +1,5 @@ import Api from "../../helpers/Api"; +import UserStorage from "../../helpers/UserStorage"; import { IFlight, ISeat } from "../Dashboard/CustomerDashboard"; export interface IBookingInfo { @@ -17,10 +18,12 @@ export interface IBookingData { export async function GetAllBookings(): Promise<IBookingInfo[]> { const url = `Booking`; try { - const booking = await Api.get(`${url}`, { withCredentials: true }); + const id = UserStorage.getUserId(); + const booking = await Api.get(`${url}?userId=${id}`, { withCredentials: true }); return booking.data as IBookingInfo[]; } catch (error) { throw error; } } + diff --git a/docker-compose.yml b/docker-compose.yml index 1826cc11fca902bbd9de45380c217e0efca0c9be..bcaa8a540188666d28fb42cf2e07d759b792a47e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -55,6 +55,7 @@ services: - UserMicroservice__BaseUrl=http://usermicroservice:8080 - FlightMicroservice__BaseUrl=http://flightmicroservice:8080 - BookingMicroservice__BaseUrl=http://bookingmicroservice:8080 + - ClientService__BaseUrl=http://localhost:${CLIENT_PORT} client: build: