@qite/tide-booking-component
Version:
React Booking wizard & Booking product component for Tide
517 lines (448 loc) • 16.3 kB
text/typescript
import {
BookingPackageFlight,
BookingPackageFlightMetaDataLine,
} from "@qite/tide-client/build/types";
import { differenceInMinutes, isEqual, parseISO } from "date-fns";
import {
FlightDirectionFilter,
FlightFilterOption,
FlightFilterOptions,
GroupedFlightDetails,
GroupedFlights,
} from "../../types";
/*interface FlightGroup {
code: string;
startDate: Date,
endDate: Date;
options: BookingPackageFlight[];
}*/
export const buildGroupedFlights = (
outwardFlights: BookingPackageFlight[] | undefined,
returnFlights: BookingPackageFlight[] | undefined
) => {
if (!outwardFlights || !returnFlights) return [] as GroupedFlights[];
// let outwardGroups = groupFlights(outwardFlights);
// let returnGroups = groupFlights(returnFlights);
const pairs: {
outward: BookingPackageFlight;
return: BookingPackageFlight;
}[] = [];
outwardFlights.forEach((outwardFlight) => {
if (outwardFlight.externalGuid) {
const returnFlight = returnFlights.find(
(x) => x.externalGuid === outwardFlight.externalGuid
)!;
pairs.push({ outward: outwardFlight, return: returnFlight });
} else {
const outwardCode = outwardFlight.code.substring(0, 7);
const returnCode = outwardCode.split(" ").reduce((a, b) => `${b} ${a}`);
returnFlights
.filter((x) => x.code.startsWith(returnCode))
.forEach((returnFlight) => {
pairs.push({ outward: outwardFlight, return: returnFlight });
});
}
});
const results = pairs.map((x) => {
const outwardFlightDetails = getFlightDetails(x.outward);
const returnFlightDetails = getFlightDetails(x.return);
return {
isSelected: x.outward.isSelected && x.return.isSelected,
price: x.outward.price + x.return.price,
outward: outwardFlightDetails,
return: returnFlightDetails,
selectedOutward: x.outward,
selectedReturn: x.return,
} as GroupedFlights;
});
return results;
};
export const buildFilterOptions = (
outwardFlights: BookingPackageFlight[] | undefined,
returnFlights: BookingPackageFlight[] | undefined,
translations: any
) => {
if (!outwardFlights || !returnFlights) return undefined;
const airports: FlightFilterOption[] = [];
const airlines: FlightFilterOption[] = [];
const numberOfStops: FlightFilterOption[] = [];
const outwardDeparturePeriods: FlightFilterOption[] = [];
const returnDeparturePeriods: FlightFilterOption[] = [];
let lowestDepartureTravelDuration = 9999;
let highestDepartureTravelDuration = 0;
let lowestDepartureChangeDuration = 9999;
let highestDepartureChangeDuration = 0;
outwardFlights.forEach((flight) => {
const airlineCode = flight.code.split("/")[1];
if (flight.flightMetaData.flightLines?.length) {
const firstLine = flight.flightMetaData.flightLines[0];
if (!airports.some((x) => x.value === firstLine.departureAirport)) {
airports.push({
value: firstLine?.departureAirport,
label: firstLine.departureAirportDescription,
count: 0,
isSelected: false,
});
}
}
if (!airlines.some((x) => x.value === airlineCode)) {
airlines.push({
value: airlineCode,
label: flight.airlineDescription,
count: 0,
isSelected: false,
});
}
const stopCount = flight.flightMetaData.flightLines.length - 1;
if (!numberOfStops.some((x) => x.value === stopCount + "")) {
numberOfStops.push({
value: stopCount + "",
label:
stopCount === 0
? translations.FLIGHTS_FORM.DIRECT_FLIGHT
: stopCount == 1
? `${stopCount} ${translations.FLIGHTS_FORM.STOP}`
: `${stopCount} ${translations.FLIGHTS_FORM.STOPS}`,
count: 0,
isSelected: false,
});
}
const departureTime = flight.flightMetaData.flightLines[0].departureTime;
const timeBracket = determineTimeBracket(departureTime);
if (!outwardDeparturePeriods.some((x) => x.value === timeBracket)) {
outwardDeparturePeriods.push({
value: timeBracket,
label: getBracketTranslation(timeBracket, translations),
count: 0,
isSelected: false,
});
}
const travelDurationInMinutes = minutesFromTicks(
flight.flightMetaData.durationInTicks
);
if (travelDurationInMinutes > highestDepartureTravelDuration)
highestDepartureTravelDuration = travelDurationInMinutes;
if (travelDurationInMinutes < lowestDepartureTravelDuration)
lowestDepartureTravelDuration = travelDurationInMinutes;
const changeDurationInMinutes = getTotalChangeDuration(flight);
if (changeDurationInMinutes > highestDepartureChangeDuration)
highestDepartureChangeDuration = changeDurationInMinutes;
if (changeDurationInMinutes < lowestDepartureChangeDuration)
lowestDepartureChangeDuration = changeDurationInMinutes;
});
let lowestReturnTravelDuration = 9999;
let highestReturnTravelDuration = 0;
let lowestReturnChangeDuration = 9999;
let highestReturnChangeDuration = 0;
returnFlights.forEach((flight) => {
const durationInMinutes = minutesFromTicks(
flight.flightMetaData.durationInTicks
);
if (durationInMinutes > highestReturnTravelDuration)
highestReturnTravelDuration = durationInMinutes;
if (durationInMinutes < lowestReturnTravelDuration)
lowestReturnTravelDuration = durationInMinutes;
const changeDurationInMinutes = getTotalChangeDuration(flight);
if (changeDurationInMinutes > highestReturnChangeDuration)
highestReturnChangeDuration = changeDurationInMinutes;
if (changeDurationInMinutes < lowestReturnChangeDuration)
lowestReturnChangeDuration = changeDurationInMinutes;
const departureTime = flight.flightMetaData.flightLines[0].departureTime;
const timeBracket = determineTimeBracket(departureTime);
if (!returnDeparturePeriods.some((x) => x.value === timeBracket)) {
returnDeparturePeriods.push({
value: timeBracket,
label: getBracketTranslation(timeBracket, translations),
count: 0,
isSelected: false,
});
}
});
return {
airports: airports,
airlines: airlines,
numberOfStops: numberOfStops,
outward: {
departurePeriod: outwardDeparturePeriods,
travelDuration: {
min: lowestDepartureTravelDuration,
max: highestDepartureTravelDuration,
selectedMin: lowestDepartureTravelDuration,
selectedMax: highestDepartureTravelDuration,
},
changeDuration: {
min: lowestDepartureChangeDuration,
max: highestDepartureChangeDuration,
selectedMin: lowestDepartureChangeDuration,
selectedMax: highestDepartureChangeDuration,
},
},
return: {
departurePeriod: returnDeparturePeriods,
travelDuration: {
min: lowestReturnTravelDuration,
max: highestReturnTravelDuration,
selectedMin: lowestReturnTravelDuration,
selectedMax: highestReturnTravelDuration,
},
changeDuration: {
min: lowestReturnChangeDuration,
max: highestReturnChangeDuration,
selectedMin: lowestReturnChangeDuration,
selectedMax: highestReturnChangeDuration,
},
},
} as FlightFilterOptions;
};
export const filterGroupedFlights = (
groups: GroupedFlights[],
filterOptions: FlightFilterOptions | undefined
) => {
if (!groups.length || !filterOptions) return [];
let filteredGroups = groups;
if (filterOptions.airlines.some((x) => x.isSelected)) {
const selectedAirlineCodes = filterOptions.airlines.filter(
(x) => x.isSelected
);
filteredGroups = filteredGroups.filter((x) =>
selectedAirlineCodes.some((y) => y.value === x.outward.airlineCode)
);
}
if (filterOptions.airports.some((x) => x.isSelected)) {
const selectedAirlineCodes = filterOptions.airports.filter(
(x) => x.isSelected
);
filteredGroups = filteredGroups.filter((x) =>
selectedAirlineCodes.some(
(y) => y.value === x.outward.departureAirportCode
)
);
}
if (filterOptions.numberOfStops.some((x) => x.isSelected)) {
const selectedNumberOfStops = filterOptions.numberOfStops.filter(
(x) => x.isSelected
);
filteredGroups = filteredGroups.filter((x) =>
selectedNumberOfStops.some(
(y) => parseInt(y.value) === x.outward.flightLines.length - 1
)
);
}
filteredGroups = filterGroupedFlightByDirection(
filteredGroups,
true,
filterOptions.outward
);
filteredGroups = filterGroupedFlightByDirection(
filteredGroups,
false,
filterOptions.return
);
return filteredGroups;
};
const filterGroupedFlightByDirection = (
groups: GroupedFlights[],
isOutward: boolean,
directionFilter: FlightDirectionFilter
) => {
let filteredGroups = groups;
if (directionFilter.departurePeriod.some((x) => x.isSelected)) {
const selectedDeparturePeriods = directionFilter.departurePeriod.filter(
(x) => x.isSelected
);
filteredGroups = filteredGroups.filter((x) =>
selectedDeparturePeriods.some(
(y) =>
y.value ===
determineTimeBracket((isOutward ? x.outward : x.return).departureTime)
)
);
}
filteredGroups = filteredGroups.filter(
(x) =>
directionFilter.travelDuration.selectedMin <=
(isOutward ? x.outward : x.return).travelDurationMinutes &&
(isOutward ? x.outward : x.return).travelDurationMinutes <=
directionFilter.travelDuration.selectedMax
);
return filteredGroups.filter(
(x) =>
directionFilter.changeDuration.selectedMin <=
(isOutward ? x.outward : x.return).changeDurationMinutes &&
(isOutward ? x.outward : x.return).changeDurationMinutes <=
directionFilter.changeDuration.selectedMax
);
};
export const formatMinutes = (minutes: number) => {
var hh = Math.floor(minutes / 60);
var mm = Math.floor(minutes % 60);
return pad(hh, 2) + ":" + pad(mm, 2);
};
const getFlightDetails = (flight: BookingPackageFlight) => {
const firstLine = flight.flightMetaData.flightLines[0];
const lastLine =
flight.flightMetaData.flightLines[
flight.flightMetaData.flightLines.length - 1
];
const airlineCode = flight.code.split("/")[1];
const waitDurations = getWaitDurations(flight.flightMetaData.flightLines);
return {
airline: flight.airlineDescription,
airlineCode: airlineCode,
departureDate: firstLine.departureDate,
departureTime: firstLine.departureTime,
departureAirportCode: firstLine.departureAirport,
departureAirport: firstLine.departureAirportDescription,
arrivalDate: lastLine.arrivalDate,
arrivalTime: lastLine.arrivalTime,
arrivalAirport: lastLine.arrivalAirportDescription,
travelDuration: formatDuration(flight.flightMetaData.durationInTicks),
travelDurationMinutes: minutesFromTicks(
flight.flightMetaData.durationInTicks
),
changeDurationMinutes: getTotalChangeDuration(flight),
numberOfStops: flight.flightMetaData.flightLines.length - 1,
isNextDay: isNextDay(firstLine.departureDate, lastLine.arrivalDate),
travelClass: firstLine.travelClass,
flightLines: flight.flightMetaData.flightLines.map((x, i) => ({
airline: x.operatingAirlineDescription,
departureDate: x.departureDate,
departureTime: x.departureTime,
departureAirport: x.departureAirportDescription,
arrivalDate: x.arrivalDate,
arrivalTime: x.arrivalTime,
arrivalAirport: x.arrivalAirportDescription,
number: `${x.airlineCode} ${x.number}`,
travelDuration: formatDuration(x.durationInTicks),
waitDuration:
waitDurations.length - 1 <= i ? waitDurations[i] : undefined,
})),
} as GroupedFlightDetails;
};
const isNextDay = (startDateString: string, endDateString: string) => {
const startDate = parseISO(startDateString);
const endDate = parseISO(endDateString);
return !isEqual(startDate, endDate);
};
const getWaitDurations = (lines: BookingPackageFlightMetaDataLine[]) => {
if (lines.length <= 1) return [];
let arrivalDate = lines[0].arrivalDate;
let arrivalTime = lines[0].arrivalTime;
const waitDurations: string[] = [];
for (var i = 1; i < lines.length; i++) {
const line = lines[i];
const waitDuration = getWaitDuration(
arrivalDate,
arrivalTime,
line.departureDate,
line.departureTime
);
waitDurations.push(waitDuration);
arrivalDate = line.arrivalDate;
arrivalTime = line.arrivalTime;
}
return waitDurations;
};
const getWaitDuration = (
arrivalDateString: string,
arrivalTime: string,
departureDateString: string,
departureTime: string
) => {
const minutes = getWaitDurationInMinutes(
arrivalDateString,
arrivalTime,
departureDateString,
departureTime
);
var hh = Math.floor(minutes / 60);
var mm = Math.floor(minutes % 60);
return pad(hh, 2) + ":" + pad(mm, 2);
};
const getWaitDurationInMinutes = (
arrivalDateString: string,
arrivalTime: string,
departureDateString: string,
departureTime: string
) => {
const arrivalDate = parseISO(arrivalDateString);
const arrivalTimeParts = arrivalTime.split(":");
arrivalDate.setHours(parseInt(arrivalTimeParts[0]));
arrivalDate.setMinutes(parseInt(arrivalTimeParts[1]));
const departureDate = parseISO(departureDateString);
const departureTimeParts = departureTime.split(":");
departureDate.setHours(parseInt(departureTimeParts[0]));
departureDate.setMinutes(parseInt(departureTimeParts[1]));
return differenceInMinutes(departureDate, arrivalDate);
};
/*const groupFlights = (flights: BookingPackageFlight[]) => {
let flightsPool = [...flights];
let groups = [] as FlightGroup[];
for (var i = 0; i < flightsPool.length; i++) {
const flight = flightsPool[i];
const relatedFlights = flightsPool.filter(x => x != flight
&& x.code === flight.code
&& isDateEqual(x.startDateTime, flight.startDateTime)
&& isDateEqual(x.endDateTime, flight.endDateTime)
);
flightsPool = flightsPool.filter(x => x != flight
&& relatedFlights.some(y => y != x));
groups.push({
code: flight.code,
startDate: parseISO(flight.startDateTime),
endDate: parseISO(flight.endDateTime),
options: [flight, ...relatedFlights]
});
}
}
const isDateEqual = (first: string, second: string) => {
const firstDate = parseISO(first);
const secondDate = parseISO(second);
return isEqual(firstDate, secondDate);
}*/
const minutesFromTicks = (ticks: number) => {
const totalSeconds = ticks / 10_000_000;
return Math.floor(totalSeconds / 60);
};
const formatDuration = (ticks: number) => {
if (!ticks) return "";
const totalSeconds = ticks / 10_000_000;
var hh = Math.floor(totalSeconds / 3600);
var mm = Math.floor((totalSeconds % 3600) / 60);
return pad(hh, 2) + ":" + pad(mm, 2);
};
const pad = (input: number, width: number) => {
const n = input + "";
return n.length >= width ? n : new Array(width - n.length + 1).join("0") + n;
};
const determineTimeBracket = (input: string) => {
const time = parseInt(input.replace(":", ""));
if (time <= 500) return "0000-0500";
if (time > 500 && time <= 1200) return "0500-1200";
if (time > 1200 && time <= 1800) return "1201-1800";
return "1800-2400";
};
const getBracketTranslation = (input: string, translations: any) => {
if (input === "0000-0500") return translations.FLIGHTS_FORM.NIGHT_DEPARTURE;
if (input === "0500-1200") return translations.FLIGHTS_FORM.MORNING_DEPARTURE;
if (input === "1200-1800")
return translations.FLIGHTS_FORM.AFTERNOON_DEPARTURE;
return translations.FLIGHTS_FORM.EVENING_DEPARTURE;
};
const getTotalChangeDuration = (flight: BookingPackageFlight) => {
const lines = flight.flightMetaData.flightLines;
if (lines.length <= 1) return 0;
let arrivalDate = lines[0].arrivalDate;
let arrivalTime = lines[0].arrivalTime;
let waitDuration = 0;
for (var i = 1; i < lines.length; i++) {
const line = lines[i];
waitDuration += getWaitDurationInMinutes(
arrivalDate,
arrivalTime,
line.departureDate,
line.departureTime
);
}
return waitDuration;
};