@qite/tide-booking-component
Version:
React Booking wizard & Booking product component for Tide
419 lines (345 loc) • 13.1 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 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 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 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 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 selectTranslations = (state: RootState) => {
const defaultStaticTranslations = getTranslations(state.booking.languageCode) as Record<string, any>;
const dynamicTranslations = (state.booking.translations && state.booking.translations.find(x => x.language == state.booking.languageCode)?.value) ?? {};
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 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 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 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,
selectAgentId,
selectGeneratePaymentUrl,
selectSkipPaymentWithAgent,
selectNotifications,
selectTagIds,
selectBookingRemarks,
selectVoucherCodes,
(
bookingPackageRequest: BookingPackageRequest<BookingPackageBookRequest>,
bookingOptions,
bookingType,
pax,
address,
packageDetails,
calculateDeposit,
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,
returnPaymentUrl: returnPaymentUrl,
notifications: notifications,
tagIds: tagIds,
remarks: remarks,
voucherCodes: voucherCodes,
customerRequests: [],
};
return bookingPackageRequest;
}
);
const buildPax = (traveler: Traveler, mainBookerId?: number) => {
return {
id: traveler.id,
gender: parseGender(traveler.gender),
firstName: traveler.firstName,
lastName: traveler.lastName,
dateOfBirth: traveler.birthDate,
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;
}
};