@qite/tide-booking-component
Version:
React Booking wizard & Booking product component for Tide
412 lines (323 loc) • 14.9 kB
text/typescript
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<string, any>;
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<string, any>;
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<string, string> = {};
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<any>;
});
export const selectBookingPackageBookRequest = createSelector(
selectBookingPackageRequest,
selectBookingOptions,
selectBookingType,
selectBookingPackagePax,
selectBookingAddress,
selectPackageDetails,
selectCalculateDeposit,
selectShowCommission,
selectAgentId,
selectGeneratePaymentUrl,
selectSkipPaymentWithAgent,
selectNotifications,
selectTagIds,
selectBookingRemarks,
selectVoucherCodes,
(
bookingPackageRequest: BookingPackageRequest<BookingPackageBookRequest>,
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;
}
};