UNPKG

@qite/tide-booking-component

Version:

React Booking wizard & Booking product component for Tide

539 lines (452 loc) 17.1 kB
import { BookingPackageItem, PackagingAccommodationResponse, PackagingFlightResponse } from '@qite/tide-client'; import { Filter, SortByType, TideTag } from '../types'; import { filter, flatMap, orderBy } from 'lodash'; import { getArrivalSegment, getDepartureRangeName, getDepartureSegment, getNumberOfStops } from './flight-utils'; import { DepartureRange } from '../../shared/types'; import { durationInTicksInMinutes, rangeFromDateTimeInMinutes } from '../../shared/utils/localization-util'; export const enrichFiltersWithResults = (results: BookingPackageItem[], filters: Filter[] | undefined, tags: TideTag[]): Filter[] => { if (!results || results.length === 0 || !filters) { return filters ?? []; } return filters.map((filter) => { let updatedFilter = { ...filter }; if (filter.property === 'price' && (filter.min == null || filter.max == null)) { const prices = results.map((r) => r.price ?? 0).filter((p) => p > 0); if (prices.length > 0) { updatedFilter.min = Math.floor(Math.min(...prices)); updatedFilter.max = Math.ceil(Math.max(...prices)); } } if (filter.property === 'accommodation') { const map = new Map<string, { name?: string; code: string }>(); results.forEach((r) => { if (r.accommodationCode) { map.set(r.accommodationCode, { name: r.accommodationName, code: r.accommodationCode }); } }); updatedFilter.options = Array.from(map.values()).map((accommodation) => ({ label: accommodation.name ?? accommodation.code, value: accommodation.code, isChecked: false })); } if (filter.property === 'regime') { const map = new Map<string, { name?: string; code: string }>(); results.forEach((r) => { if (r.regimeCode) { map.set(r.regimeCode, { name: r.regimeName, code: r.regimeCode }); } }); updatedFilter.options = Array.from(map.values()).map((regime) => ({ label: regime.name ?? regime.code, value: regime.code, isChecked: false })); } if (filter.property === 'theme') { const map = new Map<number, { name: string; id: number }>(); results.forEach((r) => { r.tagIds?.forEach((tagId) => { const tag = tags.find((t) => t.id === tagId); if (tag && tag.id != null && tag.name != null) { map.set(tag.id, { name: tag.name, id: tag.id }); } }); }); updatedFilter.options = Array.from(map.values()).map((theme) => ({ label: theme.name, value: theme.id, isChecked: false })); } return updatedFilter; }); }; export const enrichFiltersWithPackageAccoResults = (results: PackagingAccommodationResponse[], tags: TideTag[]): Filter[] => { const filters: Filter[] = []; if (!results || results.length === 0) { return filters; } const regimeFilter: Filter = { property: 'regime', label: 'Regime', type: 'checkbox', options: [], isFrontendFilter: true }; const map = new Map<string, { name?: string; code: string }>(); results.forEach((r) => { const rooms = flatMap(r.rooms); if (rooms) { rooms.map((room) => { room.options.map((option) => { if (option.regimeCode) { map.set(option.regimeCode, { name: option.regimeName, code: option.regimeCode }); } }); }); } }); regimeFilter.options = Array.from(map.values()).map((regime) => ({ label: regime.name ?? regime.code, value: regime.code, isChecked: false })); filters.push(regimeFilter); const priceFilter: Filter = { property: 'price', label: 'Prijs', type: 'slider', isFrontendFilter: true }; const prices = results.map((r) => r.price ?? 0).filter((p) => p > 0); priceFilter.min = Math.floor(Math.min(...prices)); priceFilter.max = Math.ceil(Math.max(...prices)); filters.push(priceFilter); const starsFilter: Filter = { property: 'rating', label: 'Stars', type: 'star-rating', options: [], isFrontendFilter: true }; const ratingMap = new Map<string, { name?: string; value: number }>(); results.forEach((r) => { const stars = r.stars; if (stars) { ratingMap.set(stars.toString(), { name: stars.toString(), value: stars }); } }); starsFilter.options = Array.from(ratingMap.values()).map((rating) => ({ label: rating.name ?? rating.value.toString(), value: rating.value, isChecked: false })); filters.push(starsFilter); return filters; }; export const enrichFiltersWithPackageFlightResults = (results: PackagingFlightResponse[], tags: TideTag[], translations: any): Filter[] => { const filters: Filter[] = []; if (!results || results.length === 0) { return filters; } // Airlines const airlinesFilter: Filter = { label: 'Airlines', property: 'airline', type: 'checkbox', isFrontendFilter: true, options: [] }; const airlinesFilterMap = new Map<string, { name?: string; code: string }>(); results.map((r) => { const airlineCode = r.airlineCode; const airlineName = r.airlineName; if (airlineCode && airlineName) { airlinesFilterMap.set(airlineCode, { name: airlineName, code: airlineCode }); } }); airlinesFilter.options = Array.from(airlinesFilterMap.values()).map((airline) => ({ label: airline.name ?? airline.code, value: airline.code, isChecked: false })); filters.push(airlinesFilter); // Number of stops const stopsFilter: Filter = { label: 'Number of Stops', property: 'numberOfStops', type: 'checkbox', isFrontendFilter: true, options: [] }; const stopsMap = new Map<number, { numberOfStops: number }>(); results.map((result) => { let numberOfStops = getNumberOfStops(result.outward); if (!stopsMap.has(numberOfStops)) { stopsMap.set(numberOfStops, { numberOfStops }); } }); stopsFilter.options = Array.from(stopsMap.values()).map((stop) => ({ label: `${stop.numberOfStops == 0 ? 'direct' : stop.numberOfStops + ` Stop${stop.numberOfStops !== 1 ? 's' : ''}`}`, value: stop.numberOfStops, isChecked: false })); filters.push(stopsFilter); // Departure range const departureRangeFilter: Filter = { label: 'Departure Range', property: 'departureRange', type: 'checkbox', isFrontendFilter: true, options: [] }; const departureRangeMap = new Map<DepartureRange, DepartureRange>(); results.map((result) => { const departureRange = rangeFromDateTimeInMinutes(getDepartureSegment(result.outward)?.departureDateTime); if (!departureRangeMap.has(departureRange)) { departureRangeMap.set(departureRange, departureRange); } }); departureRangeFilter.options = orderBy(Array.from(departureRangeMap.values()), ['id'], ['asc']).map((range) => ({ label: getDepartureRangeName(translations, range), value: range, isChecked: false })); filters.push(departureRangeFilter); // Departure Airport const departureAirportFilter: Filter = { label: 'Departure Airport', property: 'departureAirport', type: 'checkbox', isFrontendFilter: true, options: [] }; const departureAirportsMap = new Map<string, { name?: string; code: string }>(); results.map((result) => { const departureSegment = getDepartureSegment(result.outward); const departureAirport = departureSegment?.departureAirportCode; if (departureAirport && !departureAirportsMap.has(departureAirport)) { departureAirportsMap.set(departureAirport, { name: departureSegment?.departureAirportName + ' (' + departureAirport + ')', code: departureAirport }); } }); departureAirportFilter.options = Array.from(departureAirportsMap.values()).map((airport) => ({ label: airport.name ?? airport.code, value: airport.code, isChecked: false })); filters.push(departureAirportFilter); // Arrival Airport const arrivalAirportFilter: Filter = { label: 'Arrival Airport', property: 'arrivalAirport', type: 'checkbox', isFrontendFilter: true, options: [] }; const arrivalAirportsMap = new Map<string, { name?: string; code: string }>(); results.map((result) => { const arrivalSegment = getArrivalSegment(result.outward); const arrivalAirport = arrivalSegment?.arrivalAirportCode; if (arrivalAirport && !arrivalAirportsMap.has(arrivalAirport)) { arrivalAirportsMap.set(arrivalAirport, { name: arrivalSegment?.arrivalAirportName + ' (' + arrivalAirport + ')', code: arrivalAirport }); } }); arrivalAirportFilter.options = Array.from(arrivalAirportsMap.values()).map((airport) => ({ label: airport.name ?? airport.code, value: airport.code, isChecked: false })); filters.push(arrivalAirportFilter); // Price const priceFilter: Filter = { label: 'Price', property: 'price', type: 'slider', isFrontendFilter: true }; const prices = results.map((r) => r.price ?? 0).filter((p) => p > 0); if (prices.length > 0) { priceFilter.min = Math.floor(Math.min(...prices)); priceFilter.max = Math.ceil(Math.max(...prices)); } filters.push(priceFilter); // Travel duration const travelDurationFilter: Filter = { label: 'Travel Duration', property: 'travelDuration', type: 'slider', isFrontendFilter: true }; const minTravelTimeDuration = Math.min(...results.map((result) => result.outward.durationInTicks)); const maxTravelTimeDuration = Math.max(...results.map((result) => result.outward.durationInTicks)); const minTravelTimeValue = durationInTicksInMinutes(minTravelTimeDuration); const maxTravelTimeValue = durationInTicksInMinutes(maxTravelTimeDuration); if (minTravelTimeValue != null && maxTravelTimeValue != null) { travelDurationFilter.min = minTravelTimeValue; travelDurationFilter.max = maxTravelTimeValue; } filters.push(travelDurationFilter); return filters; }; export const applyFilters = (results: BookingPackageItem[], filters: Filter[], sortBy: SortByType | null) => { const filtered = results.filter((r) => { return filters.every((filter) => { if (!filter.isFrontendFilter) return true; // ACCOMMODATION if (filter.property === 'accommodation') { const selected = filter.options?.filter((o) => o.isChecked).map((o) => o.value); if (!selected || selected.length === 0) return true; return selected.includes(r.accommodationCode); } // REGIME if (filter.property === 'regime') { const selected = filter.options?.filter((o) => o.isChecked).map((o) => o.value); if (!selected || selected.length === 0) return true; if (!r.regimeCode) return false; return selected.includes(r.regimeCode); } // PRICE if (filter.property === 'price') { if (filter.selectedMin != null && r.price < filter.selectedMin) return false; if (filter.selectedMax != null && r.price > filter.selectedMax) return false; return true; } // THEME if (filter.property === 'theme') { const selected = filter.options?.filter((o) => o.isChecked).map((o) => o.value); if (!selected || selected.length === 0) return true; return r.tagIds?.some((tagId) => selected.includes(tagId)); } return true; }); }); // SORTING if (!sortBy || sortBy.label === 'default') { return filtered; } return filtered.sort((a, b) => { if (sortBy.label === 'price') { return sortBy.direction === 'asc' ? a.price - b.price : b.price - a.price; } return 0; }); }; export const applyFiltersToPackageAccoResults = (results: PackagingAccommodationResponse[], filters: Filter[], sortBy: SortByType | null) => { const filtered = results.filter((r) => { return filters.every((filter) => { if (!filter.isFrontendFilter) return true; // ACCOMMODATION if (filter.property === 'accommodation') { const selected = filter.options?.filter((o) => o.isChecked).map((o) => o.value); if (!selected || selected.length === 0) return true; const roomOptions = r.rooms.flatMap((room) => room.options); return roomOptions.some((option) => selected.includes(option.accommodationCode)); } // REGIME if (filter.property === 'regime') { const selected = filter.options?.filter((o) => o.isChecked).map((o) => o.value); if (!selected || selected.length === 0) return true; const roomOptions = r.rooms.flatMap((room) => room.options); return roomOptions.some((option) => selected.includes(option.regimeCode)); } // PRICE if (filter.property === 'price') { if (filter.selectedMin != null && r.price < filter.selectedMin) return false; if (filter.selectedMax != null && r.price > filter.selectedMax) return false; return true; } // RATING if (filter.property === 'rating') { if (r.stars == null) return false; if (filter.selectedRating != null && r.stars < filter.selectedRating) return false; return true; } return true; }); }); // SORTING if (!sortBy || sortBy.label === 'default') { return filtered; } return filtered.sort((a, b) => { if (sortBy.label === 'price') { return sortBy.direction === 'asc' ? a.price - b.price : b.price - a.price; } return 0; }); }; export const applyFiltersToPackageFlightResults = (results: PackagingFlightResponse[], filters: Filter[], sortBy: SortByType | null) => { const filtered = results.filter((result) => { return filters.every((filter) => { if (!filter.isFrontendFilter) return true; // Airline if (filter.property === 'airline') { const selected = filter.options?.filter((o) => o.isChecked).map((o) => o.value); if (!selected || selected.length === 0) return true; return selected.includes(result.airlineCode); } // Stops if (filter.property === 'numberOfStops') { const selected = filter.options?.filter((o) => o.isChecked).map((o) => o.value); if (!selected || selected.length === 0) return true; return selected.includes(getNumberOfStops(result.outward)) && selected.includes(getNumberOfStops(result.return)); } // Departure range if (filter.property === 'departureRange') { const selected = filter.options?.filter((o) => o.isChecked).map((o) => o.value); if (!selected || selected.length === 0) return true; return selected.includes(rangeFromDateTimeInMinutes(getDepartureSegment(result.outward)?.departureDateTime)); } // Departure airport if (filter.property === 'departureAirport') { const selected = filter.options?.filter((o) => o.isChecked).map((o) => o.value); if (!selected || selected.length === 0) return true; const departureAirportCode = getDepartureSegment(result.outward)?.departureAirportCode; if (!departureAirportCode) return false; return selected.includes(departureAirportCode); } // Arrival airport if (filter.property === 'arrivalAirport') { const selected = filter.options?.filter((o) => o.isChecked).map((o) => o.value); if (!selected || selected.length === 0) return true; const arrivalAirportCode = getArrivalSegment(result.outward)?.arrivalAirportCode; if (!arrivalAirportCode) return false; return selected.includes(arrivalAirportCode); } // PRICE if (filter.property === 'price') { if (filter.selectedMin != null && result.price < filter.selectedMin) return false; if (filter.selectedMax != null && result.price > filter.selectedMax) return false; return true; } // Travel times if (filter.property === 'travelDuration') { if (filter.selectedMin != null && durationInTicksInMinutes(result.outward?.durationInTicks) < filter.selectedMin) return false; if (filter.selectedMax != null && durationInTicksInMinutes(result.outward?.durationInTicks) > filter.selectedMax) return false; return true; } return true; }); }); // SORTING if (!sortBy || sortBy.label === 'default') { return filtered; } if (sortBy.label === 'departureTime') { return orderBy( results, [(result) => getDepartureSegment(result.outward)?.departureDateTime], [sortBy.direction] // or "desc" ); } else if (sortBy.label === 'durationInTicks') { return orderBy( results, [(result) => durationInTicksInMinutes(result.outward?.durationInTicks ?? 0)], [sortBy.direction] // or "desc" ); } else { return orderBy(results, [sortBy.label], [sortBy.direction]); } };