import React, { useContext, useMemo } from 'react'; import { formatDate, getDateOnlyTime, getTranslations } from '../../../shared/utils/localization-util'; import { SearchResultsRootState } from '../../store/search-results-store'; import { useSelector } from 'react-redux'; import SearchResultsConfigurationContext from '../../search-results-configuration-context'; import { ACCOMMODATION_SERVICE_TYPE, FLIGHT_SERVICE_TYPE } from '../../utils/query-utils'; import { first, last, sum } from 'lodash'; import { SearchSeed } from '../../types'; import { getTravelersText } from '../../../booking-wizard/features/sidebar/sidebar-util'; import { RoomTraveler } from '../../../booking-wizard/types'; import { BookingPackageFlightMetaData, BookingPriceDetail, PackagingAccommodationResponse, PackagingEntryLine, PortalQsmType } from '@qite/tide-client'; import Spinner from '../spinner/spinner'; import SharedSidebar from '../../../shared/booking/shared-sidebar'; import { getImageSrcFromHtml } from '../../../shared/utils/booking-summary'; interface WLSidebarProps { activeSearchSeed: SearchSeed | null; packagingAccoResult: PackagingAccommodationResponse | null; } const mapToSidebarFlightMetaData = (entryLine: PackagingEntryLine) => { return { flightLines: entryLine.flightInformation?.flightLines.map((f) => ({ number: f.flightNumber, departureDate: f.departureDate, departureAirport: f.departureAirportCode, departureAirportDescription: f.departureAirportDescription, departureTime: f.departureTime, arrivalDate: f.arrivalDate, arrivalAirport: f.arrivalAirportCode, arrivalAirportDescription: f.arrivalAirportDescription, arrivalTime: f.arrivalTime, flightClass: '', travelClass: '', airline: f.airlineDescription, airlineCode: f.airlineCode, operatingAirlineCode: f.operatingAirlineCode, operatingAirlineDescription: f.operatingAirlineDescription, durationInTicks: f.durationInTicks })), luggageIncluded: false, // Not present in editablePackagingEntry, set default bagageAllowed: false, // Not present in editablePackagingEntry, set default bagage: '', // Not present in editablePackagingEntry, set default mealIncluded: false, // Not present in editablePackagingEntry, set default meal: '', // Not present in editablePackagingEntry, set default durationInTicks: entryLine.flightInformation?.flightLines.reduce((s, { durationInTicks }) => s + (durationInTicks ?? 0), 0) } as BookingPackageFlightMetaData; }; const selectSeparatePackagePriceDetails = (priceDetails: BookingPriceDetail[]) => { const result: BookingPriceDetail[] = []; const filteredPriceDetails = priceDetails.filter((priceDetail) => priceDetail.isSeparate); filteredPriceDetails.forEach((priceDetail) => { const priceDetailToMerge = result.find( (x) => x.productCode === priceDetail.productCode && x.accommodationCode === priceDetail.accommodationCode && x.productType === priceDetail.productType ); if (priceDetailToMerge) { priceDetailToMerge.total += priceDetail.total; priceDetailToMerge.price += priceDetail.price; } else { result.push(Object.assign({}, priceDetail)); } }); return result; }; const WLSidebar: React.FC = ({ activeSearchSeed, packagingAccoResult }) => { const context = useContext(SearchResultsConfigurationContext); const { editablePackagingEntry, priceDetails } = useSelector((state: SearchResultsRootState) => state.searchResults); const translations = getTranslations(context?.languageCode ?? 'en-GB'); const sortedLines = useMemo(() => { return [...(editablePackagingEntry?.lines ?? [])].sort((a, b) => { const dateA = getDateOnlyTime(a.from); const dateB = getDateOnlyTime(b.from); if (dateA !== dateB) { return dateA - dateB; } return (a.order ?? Infinity) - (b.order ?? Infinity); }); }, [editablePackagingEntry]); const accoImage = useMemo(() => { return getImageSrcFromHtml(packagingAccoResult?.contents); }, [packagingAccoResult?.contents]); if (!context || !editablePackagingEntry) { return null; } const firstEntryLine = first(sortedLines); const accommodationLines = editablePackagingEntry.lines.filter((line) => line.serviceType === ACCOMMODATION_SERVICE_TYPE); const accommodationLine = first(accommodationLines) ?? firstEntryLine; const location = context.searchConfiguration.qsmType === PortalQsmType.Accommodation ? packagingAccoResult?.name ?? accommodationLine?.location?.name ?? accommodationLine?.oord?.name ?? accommodationLine?.region?.name ?? accommodationLine?.country?.name ?? firstEntryLine?.location?.name : accommodationLine?.location?.name ?? accommodationLine?.oord?.name ?? accommodationLine?.region?.name ?? accommodationLine?.country?.name ?? firstEntryLine?.location?.name; const rooms = activeSearchSeed?.rooms.map((room) => { const adults = room.pax .filter((p) => p.age && p.age >= 18) .map((p) => { return { id: p.id, age: p.age } as RoomTraveler; }); const children = room.pax .filter((p) => p.age && p.age < 18) .map((p) => { return { id: p.id, age: p.age } as RoomTraveler; }); return { adults, children }; }) || []; const travelerRooms = getTravelersText(rooms, translations); const flightSegments = sortedLines.filter( (line) => line.serviceType === FLIGHT_SERVICE_TYPE && line.flightInformation && Array.isArray(line.flightInformation.flightLines) ); const outboundFlight = first(flightSegments); const returnFlight = flightSegments.length > 1 ? last(flightSegments) : undefined; const outboundFlightMetaData = outboundFlight ? mapToSidebarFlightMetaData(outboundFlight) : undefined; const returnFlightMetaData = returnFlight ? mapToSidebarFlightMetaData(returnFlight) : undefined; const basePrice = sum(priceDetails?.details.filter((pd) => pd.isInPackage).map((pd) => pd.price * pd.amount) ?? []); const separateExtraPriceDetails = priceDetails?.details.filter((pd) => !pd.isInPackage && pd.isSeparate) ?? []; const totalPrice = sum([basePrice, ...separateExtraPriceDetails.map((priceDetail) => priceDetail.price * priceDetail.amount)]); const includedCosts = selectSeparatePackagePriceDetails(priceDetails?.details.filter((pd) => pd.isInPackage) ?? []); return ( } departureFlightMetaData={outboundFlightMetaData} returnFlightMetaData={returnFlightMetaData} includedServiceTypes={editablePackagingEntry.lines.map((line) => line.serviceType)} packagingAccommodations={accommodationLines} basePrice={basePrice} commission={priceDetails?.commission} totalPrice={totalPrice} includedCosts={includedCosts} extraCosts={separateExtraPriceDetails} deposit={priceDetails?.deposit} isUnavailable={false} agent={context.agentId} /> ); }; export default WLSidebar;