import React, { useContext, useMemo, useRef, useEffect } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import ItemPicker from '../../../qsm/components/item-picker'; import { SearchResultsRootState } from '../../../search-results/store/search-results-store'; import SearchResultsConfigurationContext from '../../../search-results/search-results-configuration-context'; import { getTranslations } from '../../utils/localization-util'; import { setBookingPackageDetails } from '../../../search-results/store/search-results-slice'; import { BookingPackageOption } from '@qite/tide-client'; import { PickerItem } from '../../types'; import { first } from 'lodash'; import Spinner from '../../../search-results/components/spinner/spinner'; type GroupTourFlyInProps = { isLoading: boolean; isOpen: boolean; setIsOpen: (open: boolean) => void; }; type GroupedAccommodation = { accommodationCode: string; accommodationName: string; regimes: PickerItem[]; }; type RoomOption = BookingPackageOption['rooms'][number]['options'][number]; const formatPrice = (price?: number, currencyCode = 'EUR') => { if (typeof price !== 'number') return ''; return new Intl.NumberFormat('nl-BE', { style: 'currency', currency: currencyCode }).format(price); }; const GroupTourFlyIn: React.FC = ({ isLoading, isOpen, setIsOpen }) => { const dispatch = useDispatch(); const context = useContext(SearchResultsConfigurationContext); const language = context?.languageCode ?? 'en-GB'; const translations = getTranslations(language); if (isLoading) { return <>{context?.customSpinner ?? }; } const { bookingPackageDetails } = useSelector((state: SearchResultsRootState) => state.searchResults); const selectedBookingPackageDetails = useMemo(() => { return bookingPackageDetails?.options?.find((x) => x.isSelected); }, [bookingPackageDetails]); const packageKey = `${bookingPackageDetails?.transactionId}-${selectedBookingPackageDetails?.id}`; const initialSelectedOptionsRef = useRef>>({}); useEffect(() => { if (!packageKey || !selectedBookingPackageDetails?.rooms) return; if (!initialSelectedOptionsRef.current[packageKey]) { initialSelectedOptionsRef.current[packageKey] = {}; } selectedBookingPackageDetails.rooms.forEach((room, roomIndex) => { if (!initialSelectedOptionsRef.current[packageKey][roomIndex]) { initialSelectedOptionsRef.current[packageKey][roomIndex] = room.options.find((option) => option.isSelected); } }); }, [packageKey, selectedBookingPackageDetails]); const groupedRooms = useMemo(() => { if (!selectedBookingPackageDetails?.rooms) return []; return selectedBookingPackageDetails.rooms.map((room) => { const groupedMap = new Map(); room.options.forEach((option) => { const key = option.accommodationCode; if (!groupedMap.has(key)) { groupedMap.set(key, { accommodationCode: option.accommodationCode, accommodationName: option.accommodationName, regimes: [] }); } groupedMap.get(key)?.regimes.push({ id: option.entryLineGuid, label: option.regimeName ?? 'No regime' }); }); return Array.from(groupedMap.values()); }); }, [selectedBookingPackageDetails]); if (!bookingPackageDetails || !selectedBookingPackageDetails) { return null; } const getSelectedOptionForRoom = (roomIndex: number) => { return selectedBookingPackageDetails.rooms?.[roomIndex]?.options?.find((option) => option.isSelected); }; const getSelectedOptionForAccommodation = (roomIndex: number, accommodationCode: string) => { return selectedBookingPackageDetails.rooms?.[roomIndex]?.options?.find((option) => option.accommodationCode === accommodationCode && option.isSelected); }; const getInitialSelectedOptionForRoom = (roomIndex: number) => { if (!packageKey) return undefined; return initialSelectedOptionsRef.current[packageKey]?.[roomIndex]; }; const handlePick = (roomIndex: number, selectedGuid?: string) => { if (!bookingPackageDetails || !selectedBookingPackageDetails) return; const updatedBookingPackageDetails = { ...bookingPackageDetails, options: bookingPackageDetails.options.map((bookingOption) => { if (!bookingOption.isSelected) { return bookingOption; } return { ...bookingOption, rooms: bookingOption.rooms.map((room, currentRoomIndex) => { if (currentRoomIndex !== roomIndex) { return room; } return { ...room, options: room.options.map((roomOption) => ({ ...roomOption, isSelected: roomOption.entryLineGuid === selectedGuid })) }; }) }; }) }; dispatch(setBookingPackageDetails({ details: updatedBookingPackageDetails })); }; const handleConfirm = () => { if (isOpen) { setIsOpen(false); } if (context?.onBook) { context.onBook(bookingPackageDetails); } }; const getPriceDifference = (roomIndex: number, accommodationCode: string, regimeId?: string) => { const currentSelectedOption = getSelectedOptionForRoom(roomIndex); const currentSelectedPrice = currentSelectedOption?.price || 0; let targetOption; if (regimeId) { targetOption = selectedBookingPackageDetails.rooms?.[roomIndex]?.options?.find((option) => option.entryLineGuid === regimeId); } else { targetOption = getSelectedOptionForAccommodation(roomIndex, accommodationCode) ?? selectedBookingPackageDetails.rooms?.[roomIndex]?.options?.find((option) => option.accommodationCode === accommodationCode); } return (targetOption?.price || 0) - currentSelectedPrice; }; const formatPriceDifference = (difference: number, currencyCode: string) => { if (difference === 0) { return null; } const formattedAbsoluteValue = formatPrice(Math.abs(difference), currencyCode); return `${difference > 0 ? '+' : '-'} ${formattedAbsoluteValue}`; }; const getPriceDifferenceClassName = (difference: number) => { if (difference < 0) { return 'flyin__acco__price flyin__acco__price--decrease'; } if (difference > 0) { return 'flyin__acco__price flyin__acco__price--increase'; } return 'flyin__acco__price'; }; const regimeFormatter = (roomIndex: number, accommodation: GroupedAccommodation, regimeId: string, label: string) => { const difference = getPriceDifference(roomIndex, accommodation.accommodationCode, regimeId); return `${label} ${difference !== 0 ? `(${formatPriceDifference(difference, bookingPackageDetails.currencyCode)})` : ''}`; }; const adjustedTotalPrice = useMemo(() => { if (!selectedBookingPackageDetails.rooms || typeof selectedBookingPackageDetails.price !== 'number') { return 0; } const basePrice = selectedBookingPackageDetails.price; const totalDelta = selectedBookingPackageDetails.rooms.reduce((sum, room, roomIndex) => { const initialOption = getInitialSelectedOptionForRoom(roomIndex); const currentOption = room.options.find((option) => option.isSelected); const initialPrice = initialOption?.price || 0; const currentPrice = currentOption?.price || 0; return sum + (currentPrice - initialPrice); }, 0); return basePrice + totalDelta; }, [selectedBookingPackageDetails, packageKey]); return ( <>
{groupedRooms.map((roomAccommodations, roomIndex) => { const selectedRoomOption = getSelectedOptionForRoom(roomIndex); return (

Room {roomIndex + 1}

{roomAccommodations.map((accommodation) => { const selectedOption = getSelectedOptionForAccommodation(roomIndex, accommodation.accommodationCode); const priceDifference = getPriceDifference(roomIndex, accommodation.accommodationCode); return (

{accommodation.accommodationName}

handlePick(roomIndex, selectedGuid)} valueFormatter={(id, label) => regimeFormatter(roomIndex, accommodation, id, label)} />
{formatPriceDifference(priceDifference, bookingPackageDetails.currencyCode)}
); })}
); })}
{translations.SHARED.TOTAL_PRICE}: {formatPrice(adjustedTotalPrice, bookingPackageDetails.currencyCode)}
); }; export default GroupTourFlyIn;