import JsonURL from '@jsonurl/jsonurl'; import { Gender } from '@qite/tide-client'; import { BookingPackageAddress, BookingPackageBookRequest, BookingPackagePax, BookingPackageRequest } from '@qite/tide-client/build/types'; import { createSelector } from '@reduxjs/toolkit'; import { format, parseISO } from 'date-fns'; import { omit } from 'lodash'; import { getTranslations } from '../../../shared/utils/localization-util'; import { RootState } from '../../store'; import { FlightInfo, Room, Traveler } from '../../types'; import { selectNotifications } from '../price-details/price-details-slice'; import { selectAgentId, selectTravelersFormValues } from '../travelers-form/travelers-form-slice'; export const selectHasMounted = (state: RootState) => state.booking.hasMounted; export const selectIsFetching = (state: RootState) => state.booking.isFetching; export const selectCurrentStep = (state: RootState) => state.booking.currentStep; export const selectGeneratePaymentUrl = (state: RootState) => state.booking.generatePaymentUrl; export const selectSkipPaymentWithAgent = (state: RootState) => state.booking.skipPaymentWithAgent; export const selectIsFetchingProductOptions = (state: RootState) => state.booking.isBusy; export const selectDepartureFlight = (state: RootState) => state.booking.package?.outwardFlights?.find((x) => x.isSelected); export const selectReturnFlight = (state: RootState) => state.booking.package?.returnFlights?.find((x) => x.isSelected); export const selectPackageRooms = (state: RootState) => state.booking.package?.options.find((x) => x.isSelected)?.rooms; export const selectIncludedServiceTypes = (state: RootState) => selectActiveOption(state)?.includedServiceTypes; export const selectAvailabilities = (state: RootState) => state.booking.package?.options.find((x) => x.isSelected)?.availabilities; export const selectPackageTags = (state: RootState) => state.booking.package?.tags; export const selectIsOnRequest = (state: RootState) => state.booking.package?.options.find((x) => x.isSelected)?.isOnRequest; export const selectPackageOptionUnits = (state: RootState) => state.booking.package?.options.find((x) => x.isSelected)?.optionUnits; export const selectPackageOptionPax = (state: RootState) => state.booking.package?.options.find((x) => x.isSelected)?.optionPax; export const selectPackageGroups = (state: RootState) => state.booking.package?.options.find((x) => x.isSelected)?.groups; export const selectPackageDetails = (state: RootState) => state.booking.package; export const selectPackageFlights = (state: RootState) => ({ outward: state.booking.package?.outwardFlights, return: state.booking.package?.returnFlights }); export const selectFlightMetaData = (state: RootState) => state.booking.package?.flightInfo.metaData; export const selectActiveOption = (state: RootState) => state.booking.package?.options.find((x) => x.isSelected); export const selectPackageAirlineGroups = createSelector( selectActiveOption, selectDepartureFlight, selectReturnFlight, (option, departureFlight, returnFlight) => option?.airlineGroups.filter((x) => x.flightIds.includes(departureFlight?.entryLineGuid ?? '') || x.flightIds.includes(returnFlight?.entryLineGuid ?? '')) ); export const selectPackageAirportGroups = createSelector( selectActiveOption, selectDepartureFlight, selectReturnFlight, (option, departureFlight, returnFlight) => option?.airportGroups.filter((x) => x.flightIds.includes(departureFlight?.entryLineGuid ?? '') || x.flightIds.includes(returnFlight?.entryLineGuid ?? '')) ); export const selectRoomOptionDepartureFlightsMetaData = createSelector(selectActiveOption, selectFlightMetaData, (ativeOption, flightMetaData) => { const roomOptions = ativeOption?.rooms.map((r) => r.options.filter((o) => o.isSelected).shift()); return roomOptions?.map((ro) => flightMetaData?.find((f) => f.entryLineGuid === ro?.entryLineGuid)?.flightMetaData).filter((f) => f !== undefined) ?? []; }); export const selectRoomOptionReturnFlightsMetaData = createSelector(selectActiveOption, selectFlightMetaData, (ativeOption, flightMetaData) => { const selectedRoomOptions = ativeOption?.rooms.map((r) => r.options.filter((o) => o.isSelected)); // check if there are return flights selected if (selectedRoomOptions?.some((ro) => ro.length > 1)) { const roomOptions = ativeOption?.rooms.map((r) => r.options .filter((o) => o.isSelected) .reverse() .shift() ); return roomOptions?.map((ro) => flightMetaData?.find((f) => f.entryLineGuid === ro?.entryLineGuid)?.flightMetaData).filter((f) => f !== undefined) ?? []; } return undefined; }); export const selectApiSettings = (state: RootState) => state.apiSettings; export const selectIsUnavailable = (state: RootState) => state.booking.isUnavailable; export const selectRequestRooms = (state: RootState) => state.booking.package?.options.find((x) => x.isSelected)?.requestRooms; export const selectOfficeId = (state: RootState) => state.booking.officeId; export const selectLanguageCode = (state: RootState) => state.booking.languageCode; export const selectCurrencyCode = (state: RootState) => state.booking.package?.currencyCode ?? 'EUR'; export const selectDefaultStaticTranslations = (state: RootState) => getTranslations(state.booking.languageCode) as Record; export const selectLanguage = (state: RootState) => state.booking.languageCode; export const selectAllDynamicTranslations = (state: RootState) => state.booking.translations; export const selectDynamicTranslations = createSelector([selectAllDynamicTranslations, selectLanguage], (allDynamicTranslations, language) => { return allDynamicTranslations?.find((x) => x.language == language)?.value ?? {}; }); export const selectTranslations = createSelector( [selectDefaultStaticTranslations, selectDynamicTranslations], (defaultStaticTranslations, dynamicTranslations) => { const merged = {} as Record; for (const key of Array.from(new Set([...Object.keys(defaultStaticTranslations), ...Object.keys(dynamicTranslations)]))) { merged[key] = { ...(defaultStaticTranslations[key] || {}), ...(dynamicTranslations[key] || {}) }; } return merged; } ); export const selectBookingOptions = (state: RootState) => state.booking.bookingOptions; export const selectBookingType = (state: RootState) => state.booking.bookingType; export const selectTagIds = (state: RootState) => state.booking.tagIds; export const selectAgentAdressId = (state: RootState) => state.booking.agentAdressId; export const selectProductAttributes = (state: RootState) => state.booking.productAttributes; export const selectBookingAttributes = (state: RootState) => state.booking.bookingAttributes; export const selectBookingNumber = (state: RootState) => state.booking.bookingNumber; export const selectIsOption = (state: RootState) => state.booking.isOption; export const selectBookingRooms = (state: RootState) => state.booking.bookingAttributes?.rooms; export const selectBookingRemarks = (state: RootState) => state.booking.remarks; export const selectVoucherCodes = (state: RootState) => state.booking.voucherCodes; export const selectCalculateDeposit = (state: RootState) => state.booking.calculateDeposit; export const selectShowCommission = (state: RootState) => state.booking.showCommission; export const selectIsRetry = (state: RootState) => state.booking.isRetry; export const selectStartDate = (state: RootState) => state.booking.package?.options.find((x) => x.isSelected)?.fromDate; export const selectAgents = (state: RootState) => state.booking.agents; export const selectCountries = (state: RootState) => state.booking.countries; export const selectProductCode = (state: RootState) => state.booking.productAttributes?.productCode; export const selectAccommodationCodes = (state: RootState) => { const accommodationCodes: string[] = []; state.booking.package?.options.forEach((o) => { o.rooms.forEach((r) => r.options.forEach((ro) => { if (!accommodationCodes.some((y) => y === ro.accommodationCode)) { accommodationCodes.push(ro.accommodationCode); } }) ); }); return accommodationCodes; }; export const selectAccommodationViews = (state: RootState) => state.booking.accommodationViews; export const selectBookingQuery = (state: RootState) => { const bookingAttributes = state.booking.bookingAttributes; if (!bookingAttributes) { return undefined; } const params: Record = {}; Object.entries(bookingAttributes).forEach(([key, value]) => { if (key === 'startDate' || key === 'endDate') { value = format(parseISO(value), 'yyyy-MM-dd'); } if (key === 'rooms') { value = JsonURL.stringify( (value as Room[]).map((room) => omit(room, ['children'])), { AQF: true } ); } if (key === 'allotmentIds' && !value.length) { value = undefined; } if (key === 'flight' && value) { var flightInfo = value as FlightInfo; value = JsonURL.stringify(flightInfo, { AQF: true }); } if (value) { params[key] = value; } }); return params; }; export const selectBookingQueryString = createSelector(selectBookingQuery, (params) => { if (!params) { return undefined; } return Object.keys(params) .filter((key) => typeof params[key] !== 'undefined') .map((key) => `${key}=${params[key]}`) .join('&'); }); export const selectMainBookerId = createSelector(selectTravelersFormValues, (formValues) => formValues?.mainBookerId); export const selectBookingPackagePax = createSelector(selectTravelersFormValues, (formValues) => { var pax: BookingPackagePax[] = []; formValues?.rooms.forEach((r) => r.adults.forEach((x) => { const adultPax = buildPax(x, formValues?.mainBookerId); if (adultPax.isMainBooker) { adultPax.mobilePhone = formValues?.phone; adultPax.email = formValues?.email; } pax.push(adultPax); }) ); formValues?.rooms.forEach((r) => r.children.forEach((x) => { pax.push(buildPax(x)); }) ); return pax; }); export const selectBookingAddress = createSelector(selectTravelersFormValues, selectBookingPackagePax, selectBookingType, (formValues, pax, bookingType) => { const mainBooker = pax.find((x) => x.isMainBooker); if (!mainBooker || bookingType == 'b2b') return undefined; return { name: `${mainBooker.firstName} ${mainBooker.lastName}`, street: formValues?.street, number: formValues?.houseNumber, box: formValues?.box, postalCode: formValues?.zipCode, location: formValues?.place, country: formValues?.country, mobilePhone: formValues?.phone, email: formValues?.email } as BookingPackageAddress; }); export const selectBookingPackageRequest = createSelector(selectOfficeId, selectAgentId, selectAgentAdressId, (officeId, agentId, agentAdressId) => { const agencyId = (agentId ?? agentAdressId ?? 0) > 0 ? agentId ?? agentAdressId : null; return { officeId: officeId, agentId: agencyId, payload: null } as BookingPackageRequest; }); export const selectBookingPackageBookRequest = createSelector( selectBookingPackageRequest, selectBookingOptions, selectBookingType, selectBookingPackagePax, selectBookingAddress, selectPackageDetails, selectCalculateDeposit, selectShowCommission, selectAgentId, selectGeneratePaymentUrl, selectSkipPaymentWithAgent, selectNotifications, selectTagIds, selectBookingRemarks, selectVoucherCodes, ( bookingPackageRequest: BookingPackageRequest, bookingOptions, bookingType, pax, address, packageDetails, calculateDeposit, showCommission, agentId, generatePaymentUrl, skipPaymentWithAgent, notifications, tagIds, remarks, voucherCodes ) => { if (!packageDetails) return null; let returnPaymentUrl = false; if (generatePaymentUrl && (!skipPaymentWithAgent || (agentId ?? 0) == 0)) { returnPaymentUrl = true; } var entryStatus = 0; var customEntryStatusId = undefined; switch (bookingType) { case 'b2b': if (bookingOptions.b2b.tagIds && bookingOptions.b2b.tagIds.length > 0) { tagIds = tagIds?.concat(bookingOptions.b2b.tagIds); } if (bookingOptions.b2b.entryStatus) { entryStatus = bookingOptions.b2b.entryStatus; } if (bookingOptions.b2b.customEntryStatusId) { customEntryStatusId = bookingOptions.b2b.customEntryStatusId; } break; case 'b2b2c': if (bookingOptions.b2b2c.tagIds && bookingOptions.b2b2c.tagIds.length > 0) { tagIds = tagIds?.concat(bookingOptions.b2b2c.tagIds); } if (bookingOptions.b2b2c.entryStatus) { entryStatus = bookingOptions.b2b2c.entryStatus; } if (bookingOptions.b2b2c.customEntryStatusId) { customEntryStatusId = bookingOptions.b2b2c.customEntryStatusId; } break; default: if (bookingOptions.b2c.tagIds && bookingOptions.b2c.tagIds.length > 0) { tagIds = tagIds?.concat(bookingOptions.b2c.tagIds); } if (bookingOptions.b2c.entryStatus) { entryStatus = bookingOptions.b2c.entryStatus; } if (bookingOptions.b2c.customEntryStatusId) { customEntryStatusId = bookingOptions.b2c.customEntryStatusId; } break; } bookingPackageRequest.payload = { package: packageDetails, status: entryStatus, customStatusId: customEntryStatusId, address: address, pax: pax?.length != 0 ? pax : packageDetails.options[0].requestRooms.flatMap((x) => x.pax), nonTravelPax: [], calculateDeposit: calculateDeposit, showCommission: showCommission, returnPaymentUrl: returnPaymentUrl, notifications: notifications, tagIds: tagIds, remarks: remarks, voucherCodes: voucherCodes, customerRequests: [] }; return bookingPackageRequest; } ); export const selectTravelersFirstStep = (state: any) => state.booking.travelersFirstStep; const buildPax = (traveler: Traveler, mainBookerId?: number) => { return { id: traveler.id, gender: parseGender(traveler.gender), firstName: traveler.firstName, lastName: traveler.lastName, dateOfBirth: traveler.birthDate, age: traveler.birthDate ? null : traveler.age, isMainBooker: traveler.id == mainBookerId } as BookingPackagePax; }; const parseGender = (gender: string): number => { switch (gender) { case 'm': return Gender.male; case 'f': return Gender.female; case 'x': default: return Gender.other; } };