diff --git a/apps/react/src/app/features/online-board/pages/flight-details-page.scss b/apps/react/src/app/features/online-board/pages/flight-details-page.scss new file mode 100644 index 000000000..ad9f64938 --- /dev/null +++ b/apps/react/src/app/features/online-board/pages/flight-details-page.scss @@ -0,0 +1,180 @@ +.flight-details-page { + width: 100%; +} + +.flight-details-page__content { + display: flex; + flex-direction: column; + gap: 16px; +} + +.flight-details-page__mini-list { + padding: 16px; + + h3 { + margin: 0 0 12px 0; + font-size: 18px; + font-weight: 600; + color: #1976d2; + } + + p { + margin: 0 0 8px 0; + font-size: 14px; + color: #666; + + &:last-child { + margin-bottom: 0; + } + } +} + +.flight-details-page__header { + padding: 20px; +} + +.flight-details-page__header-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); + gap: 20px; + + @media (max-width: 768px) { + grid-template-columns: repeat(2, 1fr); + } + + @media (max-width: 480px) { + grid-template-columns: 1fr; + } +} + +.flight-details-page__header-item { + display: flex; + flex-direction: column; + gap: 8px; +} + +.flight-details-page__header-label { + font-size: 12px; + color: #999; + text-transform: uppercase; + font-weight: 500; +} + +.flight-details-page__header-value { + font-size: 18px; + color: #333; + font-weight: 600; +} + +.flight-details-page__details { + padding: 0; +} + +.flight-details-page__crew { + padding: 20px; + + h3 { + margin: 0 0 16px 0; + font-size: 16px; + font-weight: 600; + color: #333; + } +} + +.flight-details-page__crew-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); + gap: 16px; + + @media (max-width: 768px) { + grid-template-columns: repeat(2, 1fr); + } + + @media (max-width: 480px) { + grid-template-columns: 1fr; + } + + div { + display: flex; + flex-direction: column; + gap: 4px; + } +} + +.flight-details-page__crew-label { + font-size: 11px; + color: #999; + text-transform: uppercase; + font-weight: 500; +} + +.flight-details-page__crew-value { + font-size: 14px; + color: #333; + font-weight: 500; +} + +.flight-details-page__seats { + padding: 20px; + + h3 { + margin: 0 0 16px 0; + font-size: 16px; + font-weight: 600; + color: #333; + } +} + +.flight-details-page__seats-info { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); + gap: 16px; +} + +.flight-details-page__seats-item { + display: flex; + flex-direction: column; + gap: 4px; + padding: 12px; + background: #f5f5f5; + border-radius: 4px; +} + +.flight-details-page__seats-label { + font-size: 11px; + color: #999; + text-transform: uppercase; + font-weight: 500; +} + +.flight-details-page__seats-value { + font-size: 16px; + color: #333; + font-weight: 600; +} + +.flight-details-page__amenities { + padding: 20px; + + h3 { + margin: 0 0 16px 0; + font-size: 16px; + font-weight: 600; + color: #333; + } +} + +.flight-details-page__amenities-list { + display: flex; + flex-wrap: wrap; + gap: 8px; +} + +.flight-details-page__amenity-tag { + padding: 6px 12px; + background: #e3f2fd; + color: #1565c0; + border-radius: 4px; + font-size: 13px; + font-weight: 500; +} diff --git a/apps/react/src/app/features/online-board/pages/flight-details-page.tsx b/apps/react/src/app/features/online-board/pages/flight-details-page.tsx new file mode 100644 index 000000000..880fcc0bb --- /dev/null +++ b/apps/react/src/app/features/online-board/pages/flight-details-page.tsx @@ -0,0 +1,202 @@ +import React, { useState, useEffect } from 'react' +import { useParams, useNavigate } from 'react-router-dom' +import axios from 'axios' +import { Button } from 'primereact/button' +import { PageLayout } from '@app/components/page-layout' +import { PageTabs } from '@app/components/page-tabs' +import { PageLoader } from '@app/components/page-loader' +import { PageEmptyList } from '@app/components/page-empty-list' +import { Card } from '@app/components/card' +import { useTranslation } from 'react-i18next' +import { BoardFlightBody } from '../components/board-flight-body' +import './flight-details-page.scss' + +interface FlightDetails { + id: string + flightNumber: string + status: string + operator?: string + aircraft?: string + departure: any + arrival: any + boarding?: any + deboarding?: any + codesharing?: string[] + amenities?: string[] + crew?: { + captain?: string + firstOfficer?: string + flightAttendants?: number + } + seats?: { + total: number + available: number + } +} + +export const FlightDetailsPage: React.FC = () => { + const { params: encodedParams } = useParams<{ params: string }>() + const navigate = useNavigate() + const { t } = useTranslation() + const [flight, setFlight] = useState(null) + const [loading, setLoading] = useState(false) + const [error, setError] = useState(null) + + useEffect(() => { + if (!encodedParams) return + + const fetchFlightDetails = async () => { + try { + setLoading(true) + setError(null) + + const decoded = JSON.parse(atob(encodedParams)) + const flightId = decoded.id + + // Fetch flight details from API + const response = await axios.get(`/api/flights/${flightId}`) + setFlight(response.data) + } catch (err) { + console.error('Failed to load flight details:', err) + setError('Failed to load flight information') + } finally { + setLoading(false) + } + } + + fetchFlightDetails() + }, [encodedParams]) + + const handleBack = () => { + navigate(-1) + } + + if (loading) { + return + } + + if (error || !flight) { + return ( +
+ + +
+ ) + } + + return ( +
+ + + + } + contentLeft={ + +

{flight.flightNumber}

+

{flight.departure.airport} → {flight.arrival.airport}

+

{flight.status}

+
+ } + > +
+ {/* Flight Header Info */} + +
+
+ {t('SHARED.FLIGHT')} + {flight.flightNumber} +
+
+ {t('SHARED.STATUS')} + {flight.status} +
+ {flight.aircraft && ( +
+ {t('SHARED.AIRCRAFT')} + {flight.aircraft} +
+ )} + {flight.operator && ( +
+ {t('SHARED.OPERATOR')} + {flight.operator} +
+ )} +
+
+ + {/* Flight Details */} + + + + + {/* Crew Information */} + {flight.crew && ( + +

{t('BOARD.CREW_INFORMATION')}

+
+ {flight.crew.captain && ( +
+ {t('BOARD.CAPTAIN')} + {flight.crew.captain} +
+ )} + {flight.crew.firstOfficer && ( +
+ {t('BOARD.FIRST_OFFICER')} + {flight.crew.firstOfficer} +
+ )} + {flight.crew.flightAttendants && ( +
+ {t('BOARD.FLIGHT_ATTENDANTS')} + {flight.crew.flightAttendants} +
+ )} +
+
+ )} + + {/* Seat Information */} + {flight.seats && ( + +

{t('BOARD.SEATS')}

+
+
+ {t('BOARD.TOTAL_SEATS')} + {flight.seats.total} +
+
+ {t('BOARD.AVAILABLE_SEATS')} + {flight.seats.available} +
+
+
+ )} + + {/* Amenities */} + {flight.amenities && flight.amenities.length > 0 && ( + +

{t('BOARD.AMENITIES')}

+
+ {flight.amenities.map((amenity, idx) => ( + + {amenity} + + ))} +
+
+ )} +
+
+
+ ) +} diff --git a/apps/react/src/app/features/online-board/pages/index.ts b/apps/react/src/app/features/online-board/pages/index.ts index ac6d77e88..2cb5b8b2e 100644 --- a/apps/react/src/app/features/online-board/pages/index.ts +++ b/apps/react/src/app/features/online-board/pages/index.ts @@ -1,2 +1,3 @@ export { OnlineBoardStartPage } from './online-board-start-page' export { OnlineBoardSearchPage } from './online-board-search-page' +export { FlightDetailsPage } from './flight-details-page'