@qite/tide-components 1.0.3 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -22890,6 +22890,10 @@ const NAME_CHARACTERS_REGEX = /^[\p{L}\s-]+$/u;
22890
22890
  function hasInvalidNameCharacters(name) {
22891
22891
  return !NAME_CHARACTERS_REGEX.test(name);
22892
22892
  }
22893
+ // Rejects values like a 5-digit year, which the native date input allows while typing
22894
+ function isValidBirthDate(birthDateText) {
22895
+ return dateFns.isValid(dateFns.parse(birthDateText, 'yyyy-MM-dd', new Date()));
22896
+ }
22893
22897
  function getAge(birthDateText, startDateText) {
22894
22898
  var birthDate = new Date(birthDateText);
22895
22899
  var startDate = new Date(startDateText);
@@ -22957,7 +22961,7 @@ const validateForm$1 = (values, agentRequired, bookingType, translations, formFi
22957
22961
  }
22958
22962
  }
22959
22963
  if (isFormFieldPresent('birthDate')) {
22960
- if (lodash.isEmpty(adult.birthDate)) {
22964
+ if (lodash.isEmpty(adult.birthDate) || !isValidBirthDate(adult.birthDate)) {
22961
22965
  lodash.set(errors, `rooms[${rIndex}].adults[${index}].birthDate`, formatTravelerField(rIndex + 1, index + 1, translations.TRAVELERS_FORM.BIRTHDATE));
22962
22966
  }
22963
22967
  else if (values.endDate) {
@@ -22990,7 +22994,7 @@ const validateForm$1 = (values, agentRequired, bookingType, translations, formFi
22990
22994
  }
22991
22995
  }
22992
22996
  if (isFormFieldPresent('birthDate')) {
22993
- if (lodash.isEmpty(child.birthDate)) {
22997
+ if (lodash.isEmpty(child.birthDate) || !isValidBirthDate(child.birthDate)) {
22994
22998
  lodash.set(errors, `rooms[${rIndex}].children[${index}].birthDate`, formatTravelerField(rIndex + 1, r.adults.length + index + 1, translations.TRAVELERS_FORM.BIRTHDATE));
22995
22999
  }
22996
23000
  else if (child.isBaby && values.startDate) {
@@ -23215,6 +23219,13 @@ const TypeAheadInput = ({ name, value, placeholder, options, onChange, onSelect,
23215
23219
  function isBabyAge(age, maxBabyAge = BABY_MAX_AGE) {
23216
23220
  return typeof age === 'number' && age <= maxBabyAge;
23217
23221
  }
23222
+ // The native date input can hold values format() throws on (e.g. a 5+ digit year while typing), so never format an unvalidated birthDate
23223
+ function formatBirthDate$1(birthDate) {
23224
+ if (!birthDate)
23225
+ return '';
23226
+ const parsed = dateFns.parse(birthDate, 'yyyy-MM-dd', new Date());
23227
+ return dateFns.isValid(parsed) ? dateFns.format(parsed, 'dd-MM-yyyy') : '';
23228
+ }
23218
23229
  function createTraveler(traveler, followNumber, personTranslation, isCompact, maxBabyAge) {
23219
23230
  if (isCompact) {
23220
23231
  return {
@@ -23522,10 +23533,7 @@ const SharedTravelersForm = ({ formik, translations, travellersSettings, countri
23522
23533
  !useCompactForm && (bookingType !== 'b2b' || travellersSettings?.mainBookerFormFields?.length) ? (React__default["default"].createElement("div", { className: "form__region" },
23523
23534
  React__default["default"].createElement("div", { className: "form__region-header" },
23524
23535
  React__default["default"].createElement("h5", { className: "form__region-heading" }, translations.TRAVELERS_FORM.MAIN_BOOKER),
23525
- React__default["default"].createElement("p", { className: "form__region-label" }, lodash.compact([
23526
- lodash.compact([mainBooker?.firstName, mainBooker?.lastName]).join(' '),
23527
- mainBooker?.birthDate && dateFns.format(dateFns.parse(mainBooker.birthDate, 'yyyy-MM-dd', new Date()), 'dd-MM-yyyy')
23528
- ]).join(', '))),
23536
+ React__default["default"].createElement("p", { className: "form__region-label" }, lodash.compact([lodash.compact([mainBooker?.firstName, mainBooker?.lastName]).join(' '), formatBirthDate$1(mainBooker?.birthDate)]).join(', '))),
23529
23537
  travellersSettings?.mainBookerFormFields?.length ? (React__default["default"].createElement("div", { className: "main-booker-form__grid" }, travellersSettings.mainBookerFormFields.map((field, index) => (React__default["default"].createElement("div", { key: index, className: `control control--${field.type}` }, getControl(field.type, {}, field.type)))))) : (React__default["default"].createElement(React__default["default"].Fragment, null,
23530
23538
  React__default["default"].createElement("div", { className: "form__twocolumn" },
23531
23539
  React__default["default"].createElement("div", { className: "form__twocolumn-column" },
@@ -27370,7 +27378,8 @@ const initialState$1 = {
27370
27378
  currentStep: 0,
27371
27379
  bookingNumber: undefined,
27372
27380
  trajectNodeAvailability: {},
27373
- trajectFlightsResolved: false
27381
+ trajectFlightAvailability: {},
27382
+ trajectEditLineGuid: null
27374
27383
  };
27375
27384
  const searchResultsSlice = toolkit.createSlice({
27376
27385
  name: 'searchResults',
@@ -27592,16 +27601,51 @@ const searchResultsSlice = toolkit.createSlice({
27592
27601
  const guids = new Set(action.payload);
27593
27602
  state.editablePackagingEntry.lines = (state.editablePackagingEntry.lines ?? []).filter((x) => !guids.has(x.guid));
27594
27603
  },
27595
- setTrajectFlightsResolved(state, action) {
27596
- state.trajectFlightsResolved = action.payload;
27604
+ setTrajectFlightLoading(state, action) {
27605
+ const lineGuid = action.payload;
27606
+ state.trajectFlightAvailability[lineGuid] = {
27607
+ lineGuid,
27608
+ status: 'loading',
27609
+ results: state.trajectFlightAvailability[lineGuid]?.results ?? [],
27610
+ selectedGuid: state.trajectFlightAvailability[lineGuid]?.selectedGuid ?? null
27611
+ };
27612
+ },
27613
+ setTrajectFlightAvailability(state, action) {
27614
+ const { lineGuid, results, selectedGuid } = action.payload;
27615
+ state.trajectFlightAvailability[lineGuid] = {
27616
+ lineGuid,
27617
+ status: selectedGuid ? 'resolved' : 'unavailable',
27618
+ results,
27619
+ selectedGuid
27620
+ };
27621
+ },
27622
+ setTrajectFlightSelection(state, action) {
27623
+ const flight = state.trajectFlightAvailability[action.payload.lineGuid];
27624
+ if (!flight)
27625
+ return;
27626
+ flight.selectedGuid = action.payload.guid;
27627
+ flight.status = 'resolved';
27628
+ },
27629
+ setTrajectEditLineGuid(state, action) {
27630
+ state.trajectEditLineGuid = action.payload;
27631
+ },
27632
+ updateTrajectNodeResult(state, action) {
27633
+ const { lineGuid, result } = action.payload;
27634
+ const node = state.trajectNodeAvailability[lineGuid];
27635
+ if (!node)
27636
+ return;
27637
+ node.results = node.results.map((x) => (x.code === result.code ? result : x));
27638
+ node.selectedCode = result.code;
27639
+ node.status = 'resolved';
27597
27640
  },
27598
27641
  resetTrajectAvailability(state) {
27599
27642
  state.trajectNodeAvailability = {};
27600
- state.trajectFlightsResolved = false;
27643
+ state.trajectFlightAvailability = {};
27644
+ state.trajectEditLineGuid = null;
27601
27645
  }
27602
27646
  }
27603
27647
  });
27604
- const { setResults, setFilteredResults, setSelectedSearchResult, setPackagingAccoResults, setFilteredPackagingAccoResults, setFilteredPackagingFlightResults, setPackagingAccoSearchDetails, setSelectedPackagingAccoResult, setPackagingFlightResults, setSelectedPackagingFlight, setSelectedFlight, setSelectedFlightDetails, setBookingPackageDetails, selectFlight, setIsLoading, setFlightsLoading, setInitialFilters, setFilters, resetFilters, setInitialFlightFilters, setFlightFilters, resetFlightFilters, setSortType, setFlightSortType, setActiveTab, setCurrentPage, resetSearchState, setFlyInIsOpen, setEditablePackagingEntry, setTransactionId, setFlyInType, setPriceDetails, setItinerary, setSelectedOutwardKey, setSelectedReturnKey, resetFlightSelection, setExcursionSearchParams, setSelectedExcursionSearchResult, confirmExcursionForDay, removeConfirmedExcursionForDay, clearConfirmedExcursionsForDay, setBookPackagingEntry, setCurrentStep, setBookingNumber, setTrajectNodeLoading, setTrajectNodeAvailability, setTrajectNodeSelection, setTrajectFlightsResolved, resetTrajectAvailability, updateEditableEntryLine, removeEditableEntryLines } = searchResultsSlice.actions;
27648
+ const { setResults, setFilteredResults, setSelectedSearchResult, setPackagingAccoResults, setFilteredPackagingAccoResults, setFilteredPackagingFlightResults, setPackagingAccoSearchDetails, setSelectedPackagingAccoResult, setPackagingFlightResults, setSelectedPackagingFlight, setSelectedFlight, setSelectedFlightDetails, setBookingPackageDetails, selectFlight, setIsLoading, setFlightsLoading, setInitialFilters, setFilters, resetFilters, setInitialFlightFilters, setFlightFilters, resetFlightFilters, setSortType, setFlightSortType, setActiveTab, setCurrentPage, resetSearchState, setFlyInIsOpen, setEditablePackagingEntry, setTransactionId, setFlyInType, setPriceDetails, setItinerary, setSelectedOutwardKey, setSelectedReturnKey, resetFlightSelection, setExcursionSearchParams, setSelectedExcursionSearchResult, confirmExcursionForDay, removeConfirmedExcursionForDay, clearConfirmedExcursionsForDay, setBookPackagingEntry, setCurrentStep, setBookingNumber, setTrajectNodeLoading, setTrajectNodeAvailability, setTrajectNodeSelection, setTrajectFlightLoading, setTrajectFlightAvailability, setTrajectFlightSelection, setTrajectEditLineGuid, updateTrajectNodeResult, resetTrajectAvailability, updateEditableEntryLine, removeEditableEntryLines } = searchResultsSlice.actions;
27605
27649
  var searchResultsReducer = searchResultsSlice.reducer;
27606
27650
 
27607
27651
  const ItemPicker = ({ items, selection, selectedSortByType, label, placeholder, classModifier, onPick, valueFormatter }) => {
@@ -30581,6 +30625,293 @@ const ExcursionDetails = () => {
30581
30625
  React__default["default"].createElement("button", { type: "button", className: "cta cta--primary", onClick: handleConfirm }, translations?.QSM.CONFIRM))));
30582
30626
  };
30583
30627
 
30628
+ const REGULAR_TRAJECT_NODE_TYPE = 0;
30629
+ const isSearchableTrajectNode = (node) => node.serviceType === ACCOMMODATION_SERVICE_TYPE || node.serviceType === EXCURSION_SERVICE_TYPE;
30630
+ const isTrajectFlightNode = (node) => node.serviceType === FLIGHT_SERVICE_TYPE;
30631
+ const getTrajectNodesWithLines = (trajectEntry) => {
30632
+ const linesByGuid = new Map((trajectEntry.entry.lines ?? []).map((line) => [line.guid, line]));
30633
+ return [...(trajectEntry.nodes ?? [])]
30634
+ .sort((a, b) => a.dayOrder - b.dayOrder || a.order - b.order)
30635
+ .map((node) => {
30636
+ const guids = node.lineGuids?.length ? node.lineGuids : [node.lineGuid];
30637
+ const lines = guids.map((guid) => linesByGuid.get(guid)).filter((x) => !!x);
30638
+ return { node, line: lines[0], lines };
30639
+ })
30640
+ .filter((x) => !!x.line);
30641
+ };
30642
+ const buildDestination = (line) => {
30643
+ if (line.location?.id)
30644
+ return { id: line.location.id, isLocation: true };
30645
+ if (line.oord?.id)
30646
+ return { id: line.oord.id, isOord: true };
30647
+ if (line.region?.id)
30648
+ return { id: line.region.id, isRegion: true };
30649
+ if (line.country?.id)
30650
+ return { id: line.country.id, isCountry: true };
30651
+ if (line.latitude && line.longitude)
30652
+ return { latitude: line.latitude, longitude: line.longitude };
30653
+ return { id: 0 };
30654
+ };
30655
+ const buildTrajectNodeRequest = ({ node, line }, context, productCode) => ({
30656
+ transactionId: context.transactionId,
30657
+ officeId: context.officeId,
30658
+ agentId: context.agentId ?? null,
30659
+ catalogueId: context.catalogueId,
30660
+ searchConfigurationId: context.searchConfigurationId,
30661
+ portalId: context.portalId ?? null,
30662
+ vendorConfigurationId: node.externalVendorId ?? null,
30663
+ language: context.language,
30664
+ serviceType: node.serviceType,
30665
+ fromDate: toDateOnlyString(line.from),
30666
+ toDate: toDateOnlyString(line.to),
30667
+ destination: buildDestination(line),
30668
+ productCode,
30669
+ rooms: context.rooms,
30670
+ tagIds: []
30671
+ });
30672
+ const getNodeCandidates = (node) => {
30673
+ const configured = (node.alternatives ?? []).filter((x) => !!x.code && x.code.trim().length > 0);
30674
+ if (configured.length)
30675
+ return configured;
30676
+ const fallbackCode = node.code ?? node.preferredAccommodationCode;
30677
+ if (!fallbackCode || !fallbackCode.trim().length)
30678
+ return [];
30679
+ return [
30680
+ {
30681
+ code: fallbackCode,
30682
+ name: node.name,
30683
+ description: node.description,
30684
+ imageUrl: node.imageUrl,
30685
+ contentSource: node.contentSource,
30686
+ vendor: node.vendor,
30687
+ externalVendorId: node.externalVendorId,
30688
+ isPreferred: true
30689
+ }
30690
+ ];
30691
+ };
30692
+ const findCandidateForResult = (node, result) => getNodeCandidates(node).find((candidate) => matchesCode(result, candidate.code)) ?? null;
30693
+ const matchesCode = (result, code) => {
30694
+ if (result.code === code)
30695
+ return true;
30696
+ return (result.rooms ?? []).some((room) => (room.options ?? []).some((option) => option.accommodationCode === code));
30697
+ };
30698
+ const getPackagingRoomsFromEntry = (entry) => {
30699
+ const paxById = new Map((entry.pax ?? []).map((p) => [p.id, p]));
30700
+ const rooms = (entry.rooms ?? []).map((room) => ({
30701
+ travellers: (room.paxIds ?? []).map((paxId) => {
30702
+ const pax = paxById.get(paxId);
30703
+ return {
30704
+ id: paxId,
30705
+ age: pax?.age ?? null,
30706
+ dateOfBirth: pax?.dateOfBirth ?? null
30707
+ };
30708
+ })
30709
+ }));
30710
+ return rooms.filter((room) => room.travellers.length > 0);
30711
+ };
30712
+ const pickCheapest = (results) => [...results].sort((a, b) => a.price - b.price)[0] ?? null;
30713
+ const applyResultToLines = (lines, result) => lines.map((line, index) => applyResultToLine(line, result, index));
30714
+ const getOptionForRoom = (result, roomIndex) => {
30715
+ const room = (result.rooms ?? [])[roomIndex];
30716
+ if (room)
30717
+ return (room.options ?? []).find((x) => x.isSelected) ?? (room.options ?? [])[0];
30718
+ return (result.rooms ?? []).flatMap((x) => x.options ?? []).find((x) => x.isSelected) ?? (result.rooms ?? [])[0]?.options?.[0];
30719
+ };
30720
+ const applyResultToLine = (line, result, roomIndex = 0) => {
30721
+ const option = getOptionForRoom(result, roomIndex);
30722
+ return {
30723
+ ...line,
30724
+ productName: result.name ?? line.productName,
30725
+ productCode: result.code ?? line.productCode,
30726
+ accommodationCode: option?.accommodationCode ?? line.accommodationCode,
30727
+ accommodationName: option?.accommodationName ?? line.accommodationName,
30728
+ regimeCode: option?.regimeCode ?? line.regimeCode,
30729
+ regimeName: option?.regimeName ?? line.regimeName,
30730
+ latitude: result.latitude ?? line.latitude,
30731
+ longitude: result.longitude ?? line.longitude,
30732
+ isChanged: true
30733
+ };
30734
+ };
30735
+ const toDateOnlyUtcString = (value) => {
30736
+ const date = new Date(value);
30737
+ return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate())).toISOString();
30738
+ };
30739
+ const toTimeOnlyString = (value) => {
30740
+ const date = new Date(value);
30741
+ const hh = String(date.getUTCHours()).padStart(2, '0');
30742
+ const mm = String(date.getUTCMinutes()).padStart(2, '0');
30743
+ const ss = String(date.getUTCSeconds()).padStart(2, '0');
30744
+ return `${hh}:${mm}:${ss}`;
30745
+ };
30746
+ const mapFlightSegmentsToFlightLines = (segments) => segments.map((segment) => ({
30747
+ airlineCode: segment.marketingAirlineCode,
30748
+ airlineDescription: segment.marketingAirlineName,
30749
+ operatingAirlineCode: segment.operatingAirlineCode,
30750
+ operatingAirlineDescription: segment.operatingAirlineName,
30751
+ flightNumber: segment.flightNumber,
30752
+ operatingFlightNumber: segment.operatingFlightNumber ?? null,
30753
+ departureDate: toDateOnlyUtcString(segment.departureDateTime),
30754
+ departureTime: toTimeOnlyString(segment.departureDateTime),
30755
+ departureAirportCode: segment.departureAirportCode,
30756
+ departureAirportDescription: segment.departureAirportName,
30757
+ arrivalDate: toDateOnlyUtcString(segment.arrivalDateTime),
30758
+ arrivalTime: toTimeOnlyString(segment.arrivalDateTime),
30759
+ arrivalAirportCode: segment.arrivalAirportCode,
30760
+ arrivalAirportDescription: segment.arrivalAirportName,
30761
+ durationInTicks: segment.durationInTicks
30762
+ }));
30763
+ const buildTrajectFlightRequest = ({ node, line }, context, pax) => ({
30764
+ transactionId: context.transactionId,
30765
+ officeId: context.officeId,
30766
+ catalogueId: context.catalogueId,
30767
+ agentId: context.agentId ?? null,
30768
+ language: context.language,
30769
+ departureAirportCode: node.departureAirportCode ?? '',
30770
+ arrivalAirportCode: node.arrivalAirportCode ?? '',
30771
+ returnAirportCode: null,
30772
+ luggageIncluded: null,
30773
+ maxStops: null,
30774
+ travelClass: null,
30775
+ vendorConfigurationId: node.externalVendorId ?? null,
30776
+ pax,
30777
+ outward: { date: dateToDateStruct(new Date(line.from)) },
30778
+ return: null
30779
+ });
30780
+ const pickCheapestFlight = (flights) => [...flights].sort((a, b) => a.price - b.price)[0] ?? null;
30781
+ const applyFlightToLine = (line, flight) => {
30782
+ const segments = flight.outward?.segments ?? [];
30783
+ if (!segments.length)
30784
+ return line;
30785
+ const firstSegment = segments[0];
30786
+ const lastSegment = segments[segments.length - 1];
30787
+ return {
30788
+ ...line,
30789
+ from: new Date(firstSegment.departureDateTime).toISOString(),
30790
+ to: new Date(lastSegment.arrivalDateTime).toISOString(),
30791
+ productName: `${firstSegment.departureAirportName} - ${lastSegment.arrivalAirportName} (${firstSegment.marketingAirlineName})`,
30792
+ productCode: `${firstSegment.departureAirportCode} ${lastSegment.arrivalAirportCode}/${firstSegment.marketingAirlineCode}`,
30793
+ accommodationName: firstSegment.metaData?.farePriceClassName ?? line.accommodationName,
30794
+ accommodationCode: firstSegment.metaData?.fareCode ?? line.accommodationCode,
30795
+ flightInformation: {
30796
+ pnr: '',
30797
+ flightLines: mapFlightSegmentsToFlightLines(segments)
30798
+ },
30799
+ isChanged: true
30800
+ };
30801
+ };
30802
+
30803
+ const getLocation = (result) => {
30804
+ const place = result.locationName || result.regionName || result.oordName;
30805
+ if (!place)
30806
+ return result.countryName ?? '';
30807
+ return result.countryName ? `${place}, ${result.countryName}` : place;
30808
+ };
30809
+ const toPlainText = (value) => {
30810
+ if (!value)
30811
+ return '';
30812
+ return he
30813
+ .decode(value.replace(/<[^>]*>/g, ' '))
30814
+ .replace(/\s+/g, ' ')
30815
+ .trim();
30816
+ };
30817
+ const TrajectNodeCard = ({ item, result, isSelected, languageCode, translations, onSelect, onEditOptions, showNights }) => {
30818
+ const { node } = item;
30819
+ const selectedPerRoom = (result.rooms ?? []).map((room) => (room.options ?? []).find((x) => x.isSelected) ?? (room.options ?? [])[0]);
30820
+ const isMultiRoom = selectedPerRoom.length > 1;
30821
+ const price = formatPrice$3(result.price, result.currencyCode, languageCode);
30822
+ const nights = calculateNights(new Date(result.fromDate), new Date(result.toDate));
30823
+ const candidate = findCandidateForResult(node, result);
30824
+ const image = candidate?.imageUrl ?? null;
30825
+ const description = toPlainText(candidate?.description);
30826
+ const title = candidate?.name ?? result.name;
30827
+ const priceBlock = (React__default["default"].createElement("div", { className: "search__result-card__price__wrapper" },
30828
+ React__default["default"].createElement("span", { className: "search__result-card__price__label" }, translations?.SHARED.TOTAL_PRICE),
30829
+ React__default["default"].createElement("span", { className: "search__result-card__price" }, price)));
30830
+ const canEditOptions = isSelected && !!onEditOptions && (result.rooms ?? []).some((room) => (room.options ?? []).length > 1);
30831
+ const selectButton = (React__default["default"].createElement(React__default["default"].Fragment, null,
30832
+ React__default["default"].createElement("button", { type: "button", className: `cta ${isSelected ? 'cta--selected' : 'cta--select'}`, onClick: onSelect }, isSelected ? translations?.SHARED.SELECTED : translations?.SHARED.SELECT),
30833
+ canEditOptions && (React__default["default"].createElement("button", { type: "button", className: "cta cta--secondary", onClick: onEditOptions },
30834
+ translations?.SRP.SELECT,
30835
+ " ",
30836
+ translations?.SRP.ACCOMMODATION))));
30837
+ if (result.contents?.length) {
30838
+ return (React__default["default"].createElement("div", { className: `search__result-card__wrapper search__result-card__wrapper--custom ${isSelected ? 'search__result-card__wrapper--selected' : ''}` },
30839
+ React__default["default"].createElement("div", { className: "search__result-card__top", dangerouslySetInnerHTML: { __html: he.decode(result.contents) } }),
30840
+ React__default["default"].createElement("div", { className: "search__result-card__footer" }, selectButton)));
30841
+ }
30842
+ return (React__default["default"].createElement("div", { className: `search__result-card__wrapper ${isSelected ? 'search__result-card__wrapper--selected' : ''}` },
30843
+ image && (React__default["default"].createElement("div", { className: "search__result-card__img-wrapper" },
30844
+ React__default["default"].createElement("img", { src: image, alt: title, className: "search__result-card__img" }),
30845
+ priceBlock)),
30846
+ React__default["default"].createElement("div", { className: "search__result-card__content" },
30847
+ React__default["default"].createElement("div", { className: "search__result-card__content__wrapper" },
30848
+ React__default["default"].createElement("div", { className: "search__result-card__header" },
30849
+ React__default["default"].createElement("div", { className: "search__result-card__header__wrapper" },
30850
+ !!result.stars && (React__default["default"].createElement("div", { className: "rating" }, [...Array(result.stars)].map((_, index) => (React__default["default"].createElement(Icon, { name: "ui-star", key: `rating-star-${index + 1}`, width: 14, height: 14 }))))),
30851
+ React__default["default"].createElement("h3", { className: "search__result-card__title" }, title)),
30852
+ !image && priceBlock),
30853
+ React__default["default"].createElement("span", { className: "search__result-card__location" },
30854
+ React__default["default"].createElement(Icon, { name: "ui-location", height: 16 }),
30855
+ getLocation(result)),
30856
+ React__default["default"].createElement("div", { className: "search__result-card__options" },
30857
+ showNights && nights > 0 && (React__default["default"].createElement("div", { className: "search__result-card__option" },
30858
+ React__default["default"].createElement(Icon, { name: "ui-moon", height: 16 }),
30859
+ nights,
30860
+ " ",
30861
+ translations?.SRP.NIGHTS)),
30862
+ selectedPerRoom.map((option, roomIndex) => (React__default["default"].createElement(React__default["default"].Fragment, { key: `room-${roomIndex}` },
30863
+ option?.accommodationName && (React__default["default"].createElement("div", { className: "search__result-card__option" },
30864
+ React__default["default"].createElement(Icon, { name: "ui-bed", height: 16 }),
30865
+ isMultiRoom && `${translations?.SHARED.ROOM} ${roomIndex + 1}: `,
30866
+ option.accommodationName)),
30867
+ option?.regimeName && (React__default["default"].createElement("div", { className: "search__result-card__option" },
30868
+ React__default["default"].createElement(Icon, { name: "ui-utensils", height: 16 }),
30869
+ option.regimeName)))))),
30870
+ description && React__default["default"].createElement("p", { className: "search__result-card__description" }, description)),
30871
+ React__default["default"].createElement("div", { className: "search__result-card__footer" }, selectButton))));
30872
+ };
30873
+
30874
+ const TrajectResultsFlyIn = ({ isLoading }) => {
30875
+ const context = React.useContext(SearchResultsConfigurationContext);
30876
+ const dispatch = reactRedux.useDispatch();
30877
+ const translations = getTranslations(context?.languageCode ?? 'en-GB');
30878
+ const { trajectNodeAvailability, trajectEditLineGuid } = reactRedux.useSelector((state) => state.searchResults);
30879
+ const trajectEntry = context?.trajectEntry;
30880
+ const item = React.useMemo(() => {
30881
+ if (!trajectEntry || !trajectEditLineGuid)
30882
+ return null;
30883
+ return getTrajectNodesWithLines(trajectEntry).find((x) => x.line.guid === trajectEditLineGuid) ?? null;
30884
+ }, [trajectEntry, trajectEditLineGuid]);
30885
+ if (!context || !item)
30886
+ return null;
30887
+ const availability = trajectNodeAvailability[item.line.guid];
30888
+ const results = availability?.results ?? [];
30889
+ const ordered = [...results].sort((a, b) => {
30890
+ const aConfigured = findCandidateForResult(item.node, a) ? 0 : 1;
30891
+ const bConfigured = findCandidateForResult(item.node, b) ? 0 : 1;
30892
+ return aConfigured - bConfigured || a.price - b.price;
30893
+ });
30894
+ const handleSelect = (code) => {
30895
+ const result = results.find((x) => x.code === code);
30896
+ if (!result)
30897
+ return;
30898
+ dispatch(setTrajectNodeSelection({ lineGuid: item.line.guid, code }));
30899
+ applyResultToLines(item.lines, result).forEach((line) => dispatch(updateEditableEntryLine(line)));
30900
+ dispatch(setTrajectEditLineGuid(null));
30901
+ dispatch(setFlyInIsOpen(false));
30902
+ };
30903
+ if (isLoading) {
30904
+ return React__default["default"].createElement(React__default["default"].Fragment, null, context.customSpinner ?? React__default["default"].createElement(Spinner, { label: translations.SRP.LOADING_ACCOMMODATIONS }));
30905
+ }
30906
+ return (React__default["default"].createElement("div", { className: "flyin__content" },
30907
+ React__default["default"].createElement("div", { className: "search__result-row" },
30908
+ React__default["default"].createElement("span", { className: "search__result-row-text" },
30909
+ ordered.length,
30910
+ "\u00A0",
30911
+ translations.SRP.TOTAL_RESULTS_LABEL)),
30912
+ ordered.length === 0 ? (React__default["default"].createElement("div", { className: "no-results" }, translations.SRP.NO_RESULTS)) : (React__default["default"].createElement("div", { className: "search__results__cards search__results__cards--compact" }, ordered.map((result) => (React__default["default"].createElement(TrajectNodeCard, { key: result.code, item: item, result: result, isSelected: result.code === availability?.selectedCode, languageCode: context.languageCode, translations: translations, onSelect: () => handleSelect(result.code), showNights: item.node.serviceType === ACCOMMODATION_SERVICE_TYPE })))))));
30913
+ };
30914
+
30584
30915
  const FlyIn = ({ srpType, isOpen, setIsOpen, className = '', onPanelRef, detailsLoading, flyInType, isPackageEditFlow, handleConfirm, sortByTypes, activeSearchSeed, toggleFilters, filtersOpen }) => {
30585
30916
  const dispatch = reactRedux.useDispatch();
30586
30917
  const context = React.useContext(SearchResultsConfigurationContext);
@@ -30626,6 +30957,10 @@ const FlyIn = ({ srpType, isOpen, setIsOpen, className = '', onPanelRef, details
30626
30957
  }
30627
30958
  };
30628
30959
  const handleGoBack = () => {
30960
+ if (context?.trajectEntry) {
30961
+ handleClose();
30962
+ return;
30963
+ }
30629
30964
  if (flyInType === 'acco-details') {
30630
30965
  dispatch(setFlyInType('acco-results'));
30631
30966
  }
@@ -30649,7 +30984,7 @@ const FlyIn = ({ srpType, isOpen, setIsOpen, className = '', onPanelRef, details
30649
30984
  dispatch(setBookPackagingEntry(true));
30650
30985
  }
30651
30986
  };
30652
- return (React__default["default"].createElement("div", { className: `flyin ${isOpen ? 'flyin--active' : ''} ${className} ${isPackageEditFlow || flyInType === 'acco-results' ? 'flyin--large' : ''} ${flyInType === 'excursion-results' || flyInType === 'excursion-details' ? 'flyin--medium' : ''}
30987
+ return (React__default["default"].createElement("div", { className: `flyin ${isOpen ? 'flyin--active' : ''} ${className} ${isPackageEditFlow || flyInType === 'acco-results' ? 'flyin--large' : ''} ${flyInType === 'excursion-results' || flyInType === 'excursion-details' ? 'flyin--medium' : ''}
30653
30988
  ${flyInType === 'flight-outward-results' || flyInType === 'flight-return-results' ? 'flyin--flight' : ''}` },
30654
30989
  React__default["default"].createElement("div", { className: `flyin__panel ${isOpen ? 'flyin__panel--active' : ''}`, ref: panelRef },
30655
30990
  React__default["default"].createElement("div", { className: "flyin__content" },
@@ -30675,7 +31010,10 @@ const FlyIn = ({ srpType, isOpen, setIsOpen, className = '', onPanelRef, details
30675
31010
  React__default["default"].createElement(Icon, { name: "ui-chevron", width: 14, height: 14, "aria-hidden": "true" }),
30676
31011
  "Go Back")))),
30677
31012
  srpType === build.PortalQsmType.Flight && React__default["default"].createElement(FlightsFlyIn, { isOpen: isOpen, setIsOpen: setIsOpen }),
30678
- (srpType === build.PortalQsmType.Accommodation || srpType === build.PortalQsmType.AccommodationAndFlight) && flyInType === 'acco-results' && (React__default["default"].createElement("div", { className: "flyin__content flyin__content--columns" },
31013
+ context?.trajectEntry && flyInType === 'acco-results' && React__default["default"].createElement(TrajectResultsFlyIn, { isLoading: detailsLoading }),
31014
+ !context?.trajectEntry &&
31015
+ (srpType === build.PortalQsmType.Accommodation || srpType === build.PortalQsmType.AccommodationAndFlight) &&
31016
+ flyInType === 'acco-results' && (React__default["default"].createElement("div", { className: "flyin__content flyin__content--columns" },
30679
31017
  React__default["default"].createElement(Filters, { initialFilters: initialFilters, filters: filters, isOpen: filtersOpen, handleSetIsOpen: () => toggleFilters && toggleFilters(),
30680
31018
  // handleApplyFilters={() => setSearchTrigger((prev) => prev + 1)}
30681
31019
  isLoading: isLoading, setFilters: (filters) => dispatch(setFilters(filters)), resetFilters: (filters) => dispatch(resetFilters(filters)) }),
@@ -32948,222 +33286,6 @@ const BookPackagingEntry = ({ activeSearchSeed, isLoading, isConfirmationPage })
32948
33286
  React__default["default"].createElement(WLSidebar, { activeSearchSeed: activeSearchSeed, packagingAccoResult: selectedPackagingAccoResult }))));
32949
33287
  };
32950
33288
 
32951
- const REGULAR_TRAJECT_NODE_TYPE = 0;
32952
- const isSearchableTrajectNode = (node) => node.serviceType === ACCOMMODATION_SERVICE_TYPE || node.serviceType === EXCURSION_SERVICE_TYPE;
32953
- const isTrajectFlightNode = (node) => node.serviceType === FLIGHT_SERVICE_TYPE;
32954
- const getTrajectNodesWithLines = (trajectEntry) => {
32955
- const linesByGuid = new Map((trajectEntry.entry.lines ?? []).map((line) => [line.guid, line]));
32956
- return [...(trajectEntry.nodes ?? [])]
32957
- .sort((a, b) => a.dayOrder - b.dayOrder || a.order - b.order)
32958
- .map((node) => ({ node, line: linesByGuid.get(node.lineGuid) }))
32959
- .filter((x) => !!x.line);
32960
- };
32961
- const buildDestination = (line) => {
32962
- if (line.location?.id)
32963
- return { id: line.location.id, isLocation: true };
32964
- if (line.oord?.id)
32965
- return { id: line.oord.id, isOord: true };
32966
- if (line.region?.id)
32967
- return { id: line.region.id, isRegion: true };
32968
- if (line.country?.id)
32969
- return { id: line.country.id, isCountry: true };
32970
- if (line.latitude && line.longitude)
32971
- return { latitude: line.latitude, longitude: line.longitude };
32972
- return { id: 0 };
32973
- };
32974
- const buildTrajectNodeRequest = ({ node, line }, context, productCode) => ({
32975
- transactionId: context.transactionId,
32976
- officeId: context.officeId,
32977
- agentId: context.agentId ?? null,
32978
- catalogueId: context.catalogueId,
32979
- searchConfigurationId: context.searchConfigurationId,
32980
- portalId: context.portalId ?? null,
32981
- vendorConfigurationId: node.externalVendorId ?? null,
32982
- language: context.language,
32983
- serviceType: node.serviceType,
32984
- fromDate: toDateOnlyString(line.from),
32985
- toDate: toDateOnlyString(line.to),
32986
- destination: buildDestination(line),
32987
- productCode,
32988
- rooms: context.rooms,
32989
- tagIds: []
32990
- });
32991
- const getNodeCandidates = (node) => {
32992
- const configured = (node.alternatives ?? []).filter((x) => !!x.code && x.code.trim().length > 0);
32993
- if (configured.length)
32994
- return configured;
32995
- const fallbackCode = node.code ?? node.preferredAccommodationCode;
32996
- if (!fallbackCode || !fallbackCode.trim().length)
32997
- return [];
32998
- return [
32999
- {
33000
- code: fallbackCode,
33001
- name: node.name,
33002
- description: node.description,
33003
- imageUrl: node.imageUrl,
33004
- contentSource: node.contentSource,
33005
- vendor: node.vendor,
33006
- externalVendorId: node.externalVendorId,
33007
- isPreferred: true
33008
- }
33009
- ];
33010
- };
33011
- const findCandidateForResult = (node, result) => getNodeCandidates(node).find((candidate) => matchesCode(result, candidate.code)) ?? null;
33012
- const matchesCode = (result, code) => {
33013
- if (result.code === code)
33014
- return true;
33015
- return (result.rooms ?? []).some((room) => (room.options ?? []).some((option) => option.accommodationCode === code));
33016
- };
33017
- const getPackagingRoomsFromEntry = (entry) => {
33018
- const paxById = new Map((entry.pax ?? []).map((p) => [p.id, p]));
33019
- const rooms = (entry.rooms ?? []).map((room) => ({
33020
- travellers: (room.paxIds ?? []).map((paxId) => {
33021
- const pax = paxById.get(paxId);
33022
- return {
33023
- id: paxId,
33024
- age: pax?.age ?? null,
33025
- dateOfBirth: pax?.dateOfBirth ?? null
33026
- };
33027
- })
33028
- }));
33029
- return rooms.filter((room) => room.travellers.length > 0);
33030
- };
33031
- const pickCheapest = (results) => [...results].sort((a, b) => a.price - b.price)[0] ?? null;
33032
- const applyResultToLine = (line, result) => {
33033
- const option = (result.rooms ?? []).flatMap((room) => room.options ?? []).find((x) => x.isSelected) ?? (result.rooms ?? [])[0]?.options?.[0];
33034
- return {
33035
- ...line,
33036
- productName: result.name ?? line.productName,
33037
- productCode: result.code ?? line.productCode,
33038
- accommodationCode: option?.accommodationCode ?? line.accommodationCode,
33039
- accommodationName: option?.accommodationName ?? line.accommodationName,
33040
- regimeCode: option?.regimeCode ?? line.regimeCode,
33041
- regimeName: option?.regimeName ?? line.regimeName,
33042
- latitude: result.latitude ?? line.latitude,
33043
- longitude: result.longitude ?? line.longitude,
33044
- isChanged: true
33045
- };
33046
- };
33047
- const toDateOnlyUtcString = (value) => {
33048
- const date = new Date(value);
33049
- return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate())).toISOString();
33050
- };
33051
- const toTimeOnlyString = (value) => {
33052
- const date = new Date(value);
33053
- const hh = String(date.getUTCHours()).padStart(2, '0');
33054
- const mm = String(date.getUTCMinutes()).padStart(2, '0');
33055
- const ss = String(date.getUTCSeconds()).padStart(2, '0');
33056
- return `${hh}:${mm}:${ss}`;
33057
- };
33058
- const mapFlightSegmentsToFlightLines = (segments) => segments.map((segment) => ({
33059
- airlineCode: segment.marketingAirlineCode,
33060
- airlineDescription: segment.marketingAirlineName,
33061
- operatingAirlineCode: segment.operatingAirlineCode,
33062
- operatingAirlineDescription: segment.operatingAirlineName,
33063
- flightNumber: segment.flightNumber,
33064
- operatingFlightNumber: segment.operatingFlightNumber ?? null,
33065
- departureDate: toDateOnlyUtcString(segment.departureDateTime),
33066
- departureTime: toTimeOnlyString(segment.departureDateTime),
33067
- departureAirportCode: segment.departureAirportCode,
33068
- departureAirportDescription: segment.departureAirportName,
33069
- arrivalDate: toDateOnlyUtcString(segment.arrivalDateTime),
33070
- arrivalTime: toTimeOnlyString(segment.arrivalDateTime),
33071
- arrivalAirportCode: segment.arrivalAirportCode,
33072
- arrivalAirportDescription: segment.arrivalAirportName,
33073
- durationInTicks: segment.durationInTicks
33074
- }));
33075
- const applyFlightToLine = (line, flight, isOutbound) => {
33076
- const segments = (isOutbound ? flight.outward?.segments : flight.return?.segments) ?? [];
33077
- if (!segments.length)
33078
- return line;
33079
- const firstSegment = segments[0];
33080
- const lastSegment = segments[segments.length - 1];
33081
- return {
33082
- ...line,
33083
- from: new Date(firstSegment.departureDateTime).toISOString(),
33084
- to: new Date(lastSegment.arrivalDateTime).toISOString(),
33085
- productName: `${firstSegment.departureAirportName} - ${lastSegment.arrivalAirportName} (${firstSegment.marketingAirlineName})`,
33086
- productCode: `${firstSegment.departureAirportCode} ${lastSegment.arrivalAirportCode}/${firstSegment.marketingAirlineCode}`,
33087
- accommodationName: firstSegment.metaData?.farePriceClassName ?? line.accommodationName,
33088
- accommodationCode: firstSegment.metaData?.fareCode ?? line.accommodationCode,
33089
- flightInformation: {
33090
- pnr: '',
33091
- flightLines: mapFlightSegmentsToFlightLines(segments)
33092
- },
33093
- isChanged: true
33094
- };
33095
- };
33096
- const pairTrajectFlights = (nodes) => {
33097
- const flights = nodes.filter((x) => isTrajectFlightNode(x.node));
33098
- return {
33099
- outbound: flights[0] ?? null,
33100
- inbound: flights.length > 1 ? flights[flights.length - 1] : null,
33101
- unsupported: flights.slice(1, Math.max(flights.length - 1, 1))
33102
- };
33103
- };
33104
-
33105
- const getLocation = (result) => {
33106
- const place = result.locationName || result.regionName || result.oordName;
33107
- if (!place)
33108
- return result.countryName ?? '';
33109
- return result.countryName ? `${place}, ${result.countryName}` : place;
33110
- };
33111
- const toPlainText = (value) => {
33112
- if (!value)
33113
- return '';
33114
- return he
33115
- .decode(value.replace(/<[^>]*>/g, ' '))
33116
- .replace(/\s+/g, ' ')
33117
- .trim();
33118
- };
33119
- const TrajectNodeCard = ({ item, result, isSelected, languageCode, translations, onSelect, showNights }) => {
33120
- const { node } = item;
33121
- const selectedOption = lodash.first(result.rooms)?.options?.find((x) => x.isSelected) ?? lodash.first(result.rooms)?.options?.[0];
33122
- const price = formatPrice$3(result.price, result.currencyCode, languageCode);
33123
- const nights = calculateNights(new Date(result.fromDate), new Date(result.toDate));
33124
- const candidate = findCandidateForResult(node, result);
33125
- const image = candidate?.imageUrl ?? null;
33126
- const description = toPlainText(candidate?.description);
33127
- const title = candidate?.name ?? result.name;
33128
- const priceBlock = (React__default["default"].createElement("div", { className: "search__result-card__price__wrapper" },
33129
- React__default["default"].createElement("span", { className: "search__result-card__price__label" }, translations?.SHARED.TOTAL_PRICE),
33130
- React__default["default"].createElement("span", { className: "search__result-card__price" }, price)));
33131
- const selectButton = (React__default["default"].createElement("button", { type: "button", className: `cta ${isSelected ? 'cta--selected' : 'cta--select'}`, onClick: onSelect }, isSelected ? translations?.SHARED.SELECTED : translations?.SHARED.SELECT));
33132
- if (result.contents?.length) {
33133
- return (React__default["default"].createElement("div", { className: `search__result-card__wrapper search__result-card__wrapper--custom ${isSelected ? 'search__result-card__wrapper--selected' : ''}` },
33134
- React__default["default"].createElement("div", { className: "search__result-card__top", dangerouslySetInnerHTML: { __html: he.decode(result.contents) } }),
33135
- React__default["default"].createElement("div", { className: "search__result-card__footer" }, selectButton)));
33136
- }
33137
- return (React__default["default"].createElement("div", { className: `search__result-card__wrapper ${isSelected ? 'search__result-card__wrapper--selected' : ''}` },
33138
- image && (React__default["default"].createElement("div", { className: "search__result-card__img-wrapper" },
33139
- React__default["default"].createElement("img", { src: image, alt: title, className: "search__result-card__img" }),
33140
- priceBlock)),
33141
- React__default["default"].createElement("div", { className: "search__result-card__content" },
33142
- React__default["default"].createElement("div", { className: "search__result-card__content__wrapper" },
33143
- React__default["default"].createElement("div", { className: "search__result-card__header" },
33144
- React__default["default"].createElement("div", { className: "search__result-card__header__wrapper" },
33145
- !!result.stars && (React__default["default"].createElement("div", { className: "rating" }, [...Array(result.stars)].map((_, index) => (React__default["default"].createElement(Icon, { name: "ui-star", key: `rating-star-${index + 1}`, width: 14, height: 14 }))))),
33146
- React__default["default"].createElement("h3", { className: "search__result-card__title" }, title)),
33147
- !image && priceBlock),
33148
- React__default["default"].createElement("span", { className: "search__result-card__location" },
33149
- React__default["default"].createElement(Icon, { name: "ui-location", height: 16 }),
33150
- getLocation(result)),
33151
- React__default["default"].createElement("div", { className: "search__result-card__options" },
33152
- showNights && nights > 0 && (React__default["default"].createElement("div", { className: "search__result-card__option" },
33153
- React__default["default"].createElement(Icon, { name: "ui-moon", height: 16 }),
33154
- nights,
33155
- " ",
33156
- translations?.SRP.NIGHTS)),
33157
- selectedOption?.accommodationName && (React__default["default"].createElement("div", { className: "search__result-card__option" },
33158
- React__default["default"].createElement(Icon, { name: "ui-bed", height: 16 }),
33159
- selectedOption.accommodationName)),
33160
- selectedOption?.regimeName && (React__default["default"].createElement("div", { className: "search__result-card__option" },
33161
- React__default["default"].createElement(Icon, { name: "ui-utensils", height: 16 }),
33162
- selectedOption.regimeName))),
33163
- description && React__default["default"].createElement("p", { className: "search__result-card__description" }, description)),
33164
- React__default["default"].createElement("div", { className: "search__result-card__footer" }, selectButton))));
33165
- };
33166
-
33167
33289
  const getNodeIcon = (node) => {
33168
33290
  if (isTrajectFlightNode(node))
33169
33291
  return 'ui-flight';
@@ -33178,15 +33300,10 @@ const TrajectResults = ({ isLoading }) => {
33178
33300
  const dispatch = reactRedux.useDispatch();
33179
33301
  const translations = getTranslations(context?.languageCode ?? 'en-GB');
33180
33302
  const locale = getLocale(context?.languageCode ?? 'en-GB');
33181
- const { trajectNodeAvailability, flightsLoading, trajectFlightsResolved, selectedOutwardKey, selectedReturnKey } = reactRedux.useSelector((state) => state.searchResults);
33182
- const uniqueOutwardFlights = reactRedux.useSelector(selectUniqueOutwardFlights);
33183
- const uniqueReturnFlights = reactRedux.useSelector(selectUniqueReturnFlights);
33184
- const selectedOutward = reactRedux.useSelector(selectSelectedOutward);
33185
- const selectedReturn = reactRedux.useSelector(selectSelectedReturn);
33303
+ const { trajectNodeAvailability, trajectFlightAvailability } = reactRedux.useSelector((state) => state.searchResults);
33186
33304
  const [expandedNodes, setExpandedNodes] = React.useState({});
33187
33305
  const trajectEntry = context?.trajectEntry;
33188
33306
  const nodesWithLines = React.useMemo(() => (trajectEntry ? getTrajectNodesWithLines(trajectEntry) : []), [trajectEntry]);
33189
- const flightPairing = React.useMemo(() => pairTrajectFlights(nodesWithLines), [nodesWithLines]);
33190
33307
  const lineByNodeId = React.useMemo(() => new Map(nodesWithLines.map((x) => [x.node.nodeId, x])), [nodesWithLines]);
33191
33308
  const nodesByDay = React.useMemo(() => {
33192
33309
  const map = new Map();
@@ -33208,12 +33325,28 @@ const TrajectResults = ({ isLoading }) => {
33208
33325
  if (!result)
33209
33326
  return;
33210
33327
  dispatch(setTrajectNodeSelection({ lineGuid: item.line.guid, code }));
33211
- dispatch(updateEditableEntryLine(applyResultToLine(item.line, result)));
33328
+ applyResultToLines(item.lines, result).forEach((line) => dispatch(updateEditableEntryLine(line)));
33329
+ };
33330
+ const handleShowMore = (item) => {
33331
+ dispatch(setTrajectEditLineGuid(item.line.guid));
33332
+ dispatch(setFlyInType('acco-results'));
33333
+ dispatch(setFlyInIsOpen(true));
33334
+ };
33335
+ const handleEditOptions = (item) => {
33336
+ const availability = trajectNodeAvailability[item.line.guid];
33337
+ const result = availability?.results.find((x) => x.code === availability?.selectedCode);
33338
+ if (!result)
33339
+ return;
33340
+ dispatch(setPackagingAccoSearchDetails([result]));
33341
+ dispatch(setSelectedPackagingAccoResult(result.code));
33342
+ dispatch(setTrajectEditLineGuid(item.line.guid));
33343
+ dispatch(setFlyInType('acco-details'));
33344
+ dispatch(setFlyInIsOpen(true));
33212
33345
  };
33213
33346
  const renderServiceNode = (item) => {
33347
+ console.log('renderServiceNode', item);
33214
33348
  const { node, line } = item;
33215
33349
  const availability = trajectNodeAvailability[line.guid];
33216
- const isExpanded = !!expandedNodes[line.guid];
33217
33350
  const results = availability?.results ?? [];
33218
33351
  const selected = results.find((x) => x.code === availability?.selectedCode) ?? null;
33219
33352
  const configured = results.filter((x) => x.code !== selected?.code && !!findCandidateForResult(node, x));
@@ -33235,34 +33368,27 @@ const TrajectResults = ({ isLoading }) => {
33235
33368
  node.preferredAccommodationName,
33236
33369
  " \u2014 ",
33237
33370
  translations.SRP.NO_RESULTS))),
33238
- React__default["default"].createElement("div", { className: "search__results__cards search__results__cards--extended" }, [selected, ...configured, ...(isExpanded ? others : [])].map((result) => (React__default["default"].createElement(TrajectNodeCard, { key: result.code, item: item, result: result, isSelected: result.code === selected.code, languageCode: context.languageCode, translations: translations, onSelect: () => handleSelect(item, result.code), showNights: node.serviceType === ACCOMMODATION_SERVICE_TYPE })))),
33371
+ React__default["default"].createElement("div", { className: "search__results__cards search__results__cards--compact" }, [selected, ...configured].map((result) => (React__default["default"].createElement(TrajectNodeCard, { key: result.code, item: item, result: result, isSelected: result.code === selected.code, languageCode: context.languageCode, translations: translations, onSelect: () => handleSelect(item, result.code), onEditOptions: node.serviceType === ACCOMMODATION_SERVICE_TYPE ? () => handleEditOptions(item) : undefined, showNights: node.serviceType === ACCOMMODATION_SERVICE_TYPE })))),
33239
33372
  others.length > 0 && (React__default["default"].createElement("div", { className: "search__results__cards__actions" },
33240
- React__default["default"].createElement("button", { type: "button", className: "cta cta--secondary", onClick: () => toggleExpanded(line.guid) }, isExpanded ? translations.SRP.SHOW_LESS : `${translations.SRP.SHOW_MORE} (${others.length})`)))))));
33373
+ React__default["default"].createElement("button", { type: "button", className: "cta cta--secondary", onClick: () => handleShowMore(item) },
33374
+ translations.SRP.SHOW_MORE,
33375
+ " (",
33376
+ others.length,
33377
+ ")")))))));
33378
+ };
33379
+ const handleSelectFlight = (item, flight) => {
33380
+ dispatch(setTrajectFlightSelection({ lineGuid: item.line.guid, guid: flight.outwardGuid }));
33381
+ dispatch(updateEditableEntryLine(applyFlightToLine(item.line, flight)));
33241
33382
  };
33242
33383
  const renderFlightNode = (item) => {
33243
33384
  const { node, line } = item;
33244
- const isOutbound = flightPairing.outbound?.node.nodeId === node.nodeId;
33245
- const isInbound = flightPairing.inbound?.node.nodeId === node.nodeId;
33246
- // A middle leg of a multi-flight traject: the round-trip flight search cannot express it.
33247
- if (!isOutbound && !isInbound) {
33248
- return (React__default["default"].createElement(React__default["default"].Fragment, { key: line.guid },
33249
- React__default["default"].createElement("div", { className: "search__results__label search__results__label--secondary" },
33250
- React__default["default"].createElement("div", { className: "search__results__label__date" },
33251
- React__default["default"].createElement("p", { className: "search__results__label__date-date" }, dateFns.format(new Date(line.from), 'd', { locale })),
33252
- React__default["default"].createElement("p", null, dateFns.format(new Date(line.from), 'MMM', { locale }))),
33253
- React__default["default"].createElement("div", { className: "search__results__label__text" },
33254
- React__default["default"].createElement(Icon, { name: "ui-flight", height: 16 }),
33255
- React__default["default"].createElement("h3", null,
33256
- React__default["default"].createElement("strong", null, node.name)))),
33257
- React__default["default"].createElement("div", { className: "no-results" },
33258
- node.departureAirportCode,
33259
- " \u2192 ",
33260
- node.arrivalAirportCode)));
33261
- }
33262
- const flights = isOutbound ? uniqueOutwardFlights : uniqueReturnFlights;
33263
- const selectedFlight = isOutbound ? selectedOutward : selectedReturn;
33264
- const selectedKey = isOutbound ? selectedOutwardKey : selectedReturnKey;
33265
- const visible = flights.filter((x) => getFlightKey(isOutbound ? x.outward.segments : x.return.segments) !== selectedKey);
33385
+ const availability = trajectFlightAvailability[line.guid];
33386
+ const status = availability?.status ?? 'loading';
33387
+ const flights = availability?.results ?? [];
33388
+ const selected = flights.find((x) => x.outwardGuid === availability?.selectedGuid) ?? null;
33389
+ const others = flights.filter((x) => x.outwardGuid !== availability?.selectedGuid);
33390
+ const isExpanded = !!expandedNodes[line.guid];
33391
+ const visible = isExpanded ? others : others.slice(0, 2);
33266
33392
  return (React__default["default"].createElement(React__default["default"].Fragment, { key: line.guid },
33267
33393
  React__default["default"].createElement("div", { className: "search__results__label search__results__label--secondary" },
33268
33394
  React__default["default"].createElement("div", { className: "search__results__label__date" },
@@ -33272,11 +33398,19 @@ const TrajectResults = ({ isLoading }) => {
33272
33398
  React__default["default"].createElement(Icon, { name: "ui-flight", height: 16 }),
33273
33399
  React__default["default"].createElement("h3", null,
33274
33400
  translations.SRP.SELECT,
33275
- " ",
33276
- React__default["default"].createElement("strong", null, isOutbound ? translations.SRP.DEPARTURE : translations.SRP.RETURN)))),
33277
- flightsLoading || !trajectFlightsResolved ? (React__default["default"].createElement(Spinner, { label: translations.SRP.LOADING_FLIGHTS })) : flights.length === 0 ? (React__default["default"].createElement("div", { className: "no-results" }, translations.SRP.NO_RESULTS)) : (React__default["default"].createElement("div", { className: "search__results__cards search__results__cards--extended" },
33278
- selectedFlight && (React__default["default"].createElement(IndependentFlightOption, { key: `flight-${selectedKey}`, item: isOutbound ? selectedFlight.outward : selectedFlight.return, guid: selectedFlight.outwardGuid, selectedGuid: selectedFlight.outwardGuid, isOutward: isOutbound, showSelectedState: true, price: selectedFlight.price, onSelect: isOutbound ? () => dispatch(setSelectedOutwardKey(null)) : undefined })),
33279
- visible.map((result) => (React__default["default"].createElement(IndependentFlightOption, { key: `flight-${result.outwardGuid}`, item: isOutbound ? result.outward : result.return, guid: result.outwardGuid, isOutward: isOutbound, price: result.price, currentSelectedPrice: selectedFlight?.price, onSelect: () => dispatch(isOutbound ? setSelectedOutwardKey(getFlightKey(result.outward.segments)) : setSelectedReturnKey(getFlightKey(result.return.segments))) })))))));
33401
+ ' ',
33402
+ React__default["default"].createElement("strong", null,
33403
+ node.departureAirportCode,
33404
+ " - ",
33405
+ node.arrivalAirportCode)))),
33406
+ status === 'loading' && React__default["default"].createElement(Spinner, { label: translations.SRP.LOADING_FLIGHTS }),
33407
+ status === 'unavailable' && React__default["default"].createElement("div", { className: "no-results" }, translations.SRP.NO_RESULTS),
33408
+ status === 'resolved' && selected && (React__default["default"].createElement(React__default["default"].Fragment, null,
33409
+ React__default["default"].createElement("div", { className: "search__results__cards search__results__cards--extended" },
33410
+ React__default["default"].createElement(IndependentFlightOption, { key: `flight-${selected.outwardGuid}`, item: selected.outward, guid: selected.outwardGuid, selectedGuid: selected.outwardGuid, isOutward: true, showSelectedState: true, price: selected.price }),
33411
+ visible.map((result) => (React__default["default"].createElement(IndependentFlightOption, { key: `flight-${result.outwardGuid}`, item: result.outward, guid: result.outwardGuid, isOutward: true, price: result.price, currentSelectedPrice: selected.price, onSelect: () => handleSelectFlight(item, result) })))),
33412
+ others.length > 2 && (React__default["default"].createElement("div", { className: "search__results__cards__actions" },
33413
+ React__default["default"].createElement("button", { type: "button", className: "cta cta--secondary", onClick: () => toggleExpanded(line.guid) }, isExpanded ? translations.SRP.SHOW_LESS : `${translations.SRP.SHOW_MORE} (${others.length - 2})`)))))));
33280
33414
  };
33281
33415
  if (isLoading)
33282
33416
  return React__default["default"].createElement(Spinner, { label: translations.SRP.LOADING_ITINERARY });
@@ -33285,6 +33419,8 @@ const TrajectResults = ({ isLoading }) => {
33285
33419
  if (!dayNodes.length)
33286
33420
  return null;
33287
33421
  return (React__default["default"].createElement(React__default["default"].Fragment, { key: `day-${day.order}` }, dayNodes.map((node) => {
33422
+ if (node.serviceType !== ACCOMMODATION_SERVICE_TYPE && node.serviceType !== EXCURSION_SERVICE_TYPE && node.serviceType !== FLIGHT_SERVICE_TYPE)
33423
+ return null;
33288
33424
  // if (node.trajectNodeType === CONNECTING_TRAJECT_NODE_TYPE) return null;
33289
33425
  // if (node.trajectNodeType !== REGULAR_TRAJECT_NODE_TYPE) return renderContextNode(node);
33290
33426
  if (node.trajectNodeType !== REGULAR_TRAJECT_NODE_TYPE)
@@ -33302,7 +33438,7 @@ const useTrajectAvailability = () => {
33302
33438
  const context = React.useContext(SearchResultsConfigurationContext);
33303
33439
  const dispatch = reactRedux.useDispatch();
33304
33440
  const trajectEntry = context?.trajectEntry;
33305
- const selectedCombinationFlight = reactRedux.useSelector(selectSelectedCombinationFlight);
33441
+ const showNonPreferredItems = !!context?.showNonPreferredItems;
33306
33442
  React.useEffect(() => {
33307
33443
  if (!context || !trajectEntry)
33308
33444
  return;
@@ -33322,6 +33458,13 @@ const useTrajectAvailability = () => {
33322
33458
  language: context.languageCode ?? 'en-GB',
33323
33459
  rooms: getPackagingRoomsFromEntry(trajectEntry.entry)
33324
33460
  };
33461
+ // Flight searches take passengers rather than rooms; the API buckets by age.
33462
+ const pax = trajectEntry.entry.pax ?? [];
33463
+ const ageOf = (p) => p.age ?? 30;
33464
+ const adults = pax.filter((p) => ageOf(p) >= 12).length;
33465
+ const kids = pax.filter((p) => ageOf(p) >= 2 && ageOf(p) < 12).length;
33466
+ const babies = pax.filter((p) => ageOf(p) < 2).length;
33467
+ const flightPax = lodash.concat(Array.from({ length: adults }, (_, index) => ({ id: index, age: 31 })), Array.from({ length: kids }, (_, index) => ({ id: index + adults, age: 8 })), Array.from({ length: babies }, (_, index) => ({ id: index + adults + kids, age: 1 })));
33325
33468
  const nodesWithLines = getTrajectNodesWithLines(trajectEntry);
33326
33469
  const resolveNode = async (item) => {
33327
33470
  const lineGuid = item.line.guid;
@@ -33340,18 +33483,20 @@ const useTrajectAvailability = () => {
33340
33483
  return null;
33341
33484
  }
33342
33485
  }));
33343
- let results = attempts.filter((x) => !!x).filter((x, i, all) => all.findIndex((y) => y.code === x.code) === i);
33344
- let picked = results[0] ?? null;
33486
+ const configuredResults = attempts
33487
+ .filter((x) => !!x)
33488
+ .filter((x, i, all) => all.findIndex((y) => y.code === x.code) === i);
33345
33489
  const preferredCandidate = candidates.find((x) => x.isPreferred) ?? candidates[0];
33346
- let preferredUnavailable = !!preferredCandidate && !results.some((x) => matchesCode(x, preferredCandidate.code));
33347
- // Nothing configured was available — fall back to the cheapest result in the destination.
33348
- if (!picked) {
33349
- results = (await search(config, buildTrajectNodeRequest(item, searchContext, ''), controller.signal)) ?? [];
33350
- picked = pickCheapest(results);
33490
+ let preferredUnavailable = !!preferredCandidate && !configuredResults.some((x) => matchesCode(x, preferredCandidate.code));
33491
+ const destinationResults = showNonPreferredItems
33492
+ ? ((await search(config, buildTrajectNodeRequest(item, searchContext, ''), controller.signal)) ?? []).filter((x) => !configuredResults.some((configured) => configured.code === x.code))
33493
+ : [];
33494
+ const results = [...configuredResults, ...destinationResults];
33495
+ const picked = configuredResults[0] ?? pickCheapest(destinationResults);
33496
+ if (!configuredResults.length)
33351
33497
  preferredUnavailable = candidates.length > 0;
33352
- }
33353
33498
  if (controller.signal.aborted)
33354
- return null;
33499
+ return [];
33355
33500
  dispatch(setTrajectNodeAvailability({
33356
33501
  lineGuid,
33357
33502
  results,
@@ -33359,91 +33504,58 @@ const useTrajectAvailability = () => {
33359
33504
  preferredUnavailable
33360
33505
  }));
33361
33506
  if (!picked)
33362
- return lineGuid;
33363
- dispatch(updateEditableEntryLine(applyResultToLine(item.line, picked)));
33364
- return null;
33507
+ return item.lines.map((x) => x.guid);
33508
+ applyResultToLines(item.lines, picked).forEach((line) => dispatch(updateEditableEntryLine(line)));
33509
+ return [];
33365
33510
  }
33366
33511
  catch (error) {
33367
33512
  if (controller.signal.aborted)
33368
- return null;
33513
+ return [];
33369
33514
  console.error('Failed to resolve traject node availability', item.node.name, error);
33370
33515
  dispatch(setTrajectNodeAvailability({ lineGuid, results: [], selectedCode: null, preferredUnavailable: false }));
33371
- return lineGuid;
33516
+ return item.lines.map((x) => x.guid);
33372
33517
  }
33373
33518
  };
33374
- const resolveFlights = async () => {
33375
- const { outbound, inbound } = pairTrajectFlights(nodesWithLines);
33376
- if (!outbound) {
33377
- dispatch(setTrajectFlightsResolved(true));
33378
- return;
33519
+ const resolveFlightNode = async (item) => {
33520
+ const lineGuid = item.line.guid;
33521
+ if (!item.node.departureAirportCode || !item.node.arrivalAirportCode) {
33522
+ dispatch(setTrajectFlightAvailability({ lineGuid, results: [], selectedGuid: null }));
33523
+ return item.lines.map((x) => x.guid);
33379
33524
  }
33380
- dispatch(setFlightsLoading(true));
33525
+ dispatch(setTrajectFlightLoading(lineGuid));
33381
33526
  try {
33382
- const pax = trajectEntry.entry.pax ?? [];
33383
- const ageOf = (p) => p.age ?? 30;
33384
- const adults = pax.filter((p) => ageOf(p) >= 12).length;
33385
- const kids = pax.filter((p) => ageOf(p) >= 2 && ageOf(p) < 12).length;
33386
- const babies = pax.filter((p) => ageOf(p) < 2).length;
33387
- const request = {
33388
- transactionId: trajectEntry.entry.transactionId,
33389
- officeId: searchContext.officeId,
33390
- catalogueId: searchContext.catalogueId,
33391
- agentId: searchContext.agentId,
33392
- language: searchContext.language,
33393
- departureAirportCode: outbound.node.departureAirportCode ?? '',
33394
- arrivalAirportCode: outbound.node.arrivalAirportCode ?? '',
33395
- returnAirportCode: inbound?.node.arrivalAirportCode ?? outbound.node.departureAirportCode ?? null,
33396
- luggageIncluded: null,
33397
- maxStops: null,
33398
- travelClass: null,
33399
- pax: lodash.concat(Array.from({ length: adults }, (_, index) => ({ id: index, age: 31 })), Array.from({ length: kids }, (_, index) => ({ id: index + adults, age: 8 })), Array.from({ length: babies }, (_, index) => ({ id: index + adults + kids, age: 1 }))),
33400
- outward: { date: dateToDateStruct(new Date(outbound.line.from)) },
33401
- return: { date: dateToDateStruct(new Date((inbound ?? outbound).line.from)) }
33402
- };
33403
- const flights = (await build.searchPackagingFlights(config, request, controller.signal)) ?? [];
33527
+ const flights = (await build.searchPackagingFlights(config, buildTrajectFlightRequest(item, searchContext, flightPax), controller.signal)) ?? [];
33404
33528
  if (controller.signal.aborted)
33405
- return;
33406
- dispatch(setPackagingFlightResults(flights));
33407
- const firstFlight = lodash.first(flights);
33408
- if (firstFlight) {
33409
- dispatch(setSelectedOutwardKey(getFlightKey(firstFlight.outward.segments)));
33410
- dispatch(setSelectedReturnKey(getFlightKey(firstFlight.return.segments)));
33411
- }
33529
+ return [];
33530
+ const picked = pickCheapestFlight(flights);
33531
+ dispatch(setTrajectFlightAvailability({ lineGuid, results: flights, selectedGuid: picked?.outwardGuid ?? null }));
33532
+ if (!picked)
33533
+ return item.lines.map((x) => x.guid);
33534
+ dispatch(updateEditableEntryLine(applyFlightToLine(item.line, picked)));
33535
+ return [];
33412
33536
  }
33413
33537
  catch (error) {
33414
- if (!controller.signal.aborted)
33415
- console.error('Failed to resolve traject flights', error);
33416
- }
33417
- finally {
33418
- if (!controller.signal.aborted) {
33419
- dispatch(setFlightsLoading(false));
33420
- dispatch(setTrajectFlightsResolved(true));
33421
- }
33538
+ if (controller.signal.aborted)
33539
+ return [];
33540
+ console.error('Failed to resolve traject flight', item.node.name, error);
33541
+ dispatch(setTrajectFlightAvailability({ lineGuid, results: [], selectedGuid: null }));
33542
+ return item.lines.map((x) => x.guid);
33422
33543
  }
33423
33544
  };
33424
33545
  (async () => {
33425
33546
  const searchable = nodesWithLines.filter((x) => isSearchableTrajectNode(x.node));
33426
- const [unresolved] = await Promise.all([Promise.all(searchable.map(resolveNode)), resolveFlights()]);
33547
+ const flightNodes = nodesWithLines.filter((x) => isTrajectFlightNode(x.node));
33548
+ const unresolved = (await Promise.all([...searchable.map(resolveNode), ...flightNodes.map(resolveFlightNode)])).flat();
33427
33549
  if (controller.signal.aborted)
33428
33550
  return;
33429
33551
  // A node with no availability anywhere cannot be priced or booked, so its line comes off
33430
33552
  // the entry. The card stays visible and reports the gap.
33431
- const emptyLineGuids = unresolved.filter((guid) => !!guid);
33553
+ const emptyLineGuids = unresolved.filter(Boolean);
33432
33554
  if (emptyLineGuids.length)
33433
33555
  dispatch(removeEditableEntryLines(emptyLineGuids));
33434
33556
  })();
33435
33557
  return () => controller.abort();
33436
- }, [trajectEntry]);
33437
- // Keep the traject's own flight lines in step with whatever outward/return the user picks.
33438
- React.useEffect(() => {
33439
- if (!trajectEntry || !selectedCombinationFlight)
33440
- return;
33441
- const { outbound, inbound } = pairTrajectFlights(getTrajectNodesWithLines(trajectEntry));
33442
- if (outbound)
33443
- dispatch(updateEditableEntryLine(applyFlightToLine(outbound.line, selectedCombinationFlight, true)));
33444
- if (inbound)
33445
- dispatch(updateEditableEntryLine(applyFlightToLine(inbound.line, selectedCombinationFlight, false)));
33446
- }, [trajectEntry, selectedCombinationFlight]);
33558
+ }, [trajectEntry, showNonPreferredItems]);
33447
33559
  };
33448
33560
 
33449
33561
  const SearchResultsContainer = ({ onBookingStarted }) => {
@@ -33451,7 +33563,7 @@ const SearchResultsContainer = ({ onBookingStarted }) => {
33451
33563
  const dispatch = reactRedux.useDispatch();
33452
33564
  const context = React.useContext(SearchResultsConfigurationContext);
33453
33565
  const translations = getTranslations(context?.languageCode ?? 'en-GB');
33454
- const { results, filteredResults, packagingAccoResults, filteredPackagingAccoResults, isLoading, flightsLoading, initialFilters, filters, flightFilters, selectedSortType, selectedFlightSortType, selectedSearchResult, selectedPackagingAccoResultCode, flyInIsOpen, packagingAccoSearchDetails, editablePackagingEntry, transactionId, flyInType, packagingFlightResults, confirmedExcursionsByDay, bookPackagingEntry } = reactRedux.useSelector((state) => state.searchResults);
33566
+ const { results, filteredResults, packagingAccoResults, filteredPackagingAccoResults, isLoading, flightsLoading, initialFilters, filters, flightFilters, selectedSortType, selectedFlightSortType, selectedSearchResult, selectedPackagingAccoResultCode, flyInIsOpen, packagingAccoSearchDetails, editablePackagingEntry, transactionId, flyInType, packagingFlightResults, confirmedExcursionsByDay, bookPackagingEntry, trajectEditLineGuid } = reactRedux.useSelector((state) => state.searchResults);
33455
33567
  const isMobile = useMediaQuery('(max-width: 1200px)');
33456
33568
  // Resolves live availability per traject node; no-op unless a traject entry is configured.
33457
33569
  useTrajectAvailability();
@@ -33740,6 +33852,20 @@ const SearchResultsContainer = ({ onBookingStarted }) => {
33740
33852
  setDetailsIsLoading(false);
33741
33853
  };
33742
33854
  const handleConfirmHotelSwap = () => {
33855
+ if (context?.trajectEntry && trajectEditLineGuid) {
33856
+ const item = getTrajectNodesWithLines(context.trajectEntry).find((x) => x.line.guid === trajectEditLineGuid);
33857
+ const result = packagingAccoSearchDetails.find((x) => x.code === selectedPackagingAccoResultCode);
33858
+ if (item && result) {
33859
+ const currentLines = item.lines
33860
+ .map((line) => editablePackagingEntry?.lines?.find((x) => x.guid === line.guid))
33861
+ .filter((x) => !!x);
33862
+ applyResultToLines(currentLines, result).forEach((line) => dispatch(updateEditableEntryLine(line)));
33863
+ dispatch(updateTrajectNodeResult({ lineGuid: trajectEditLineGuid, result }));
33864
+ }
33865
+ dispatch(setTrajectEditLineGuid(null));
33866
+ handleFlyInToggle(false);
33867
+ return;
33868
+ }
33743
33869
  const updatedEntry = swapHotelInPackagingEntry();
33744
33870
  if (!updatedEntry)
33745
33871
  return;
@@ -34088,6 +34214,10 @@ const SearchResultsContainer = ({ onBookingStarted }) => {
34088
34214
  const fetchPackagingAccoSearchDetails = async () => {
34089
34215
  if (!selectedPackagingAccoResultCode || !context)
34090
34216
  return;
34217
+ // A traject node already resolved its own availability, options per room included, and hands
34218
+ // that result straight to the fly-in.
34219
+ if (context.trajectEntry)
34220
+ return;
34091
34221
  if (skipInitialPackagingAccoDetailsRef.current) {
34092
34222
  skipInitialPackagingAccoDetailsRef.current = false;
34093
34223
  return;
@@ -37602,20 +37732,20 @@ function ItineraryMapView({ destinations: propDestinations, adults = 2, dateFrom
37602
37732
  const isStartEnd = dest.isStart || dest.isEnd;
37603
37733
  let iconHtml;
37604
37734
  if (isStartEnd) {
37605
- iconHtml = `
37606
- <div class="map-view__marker map-view__marker--plane">
37607
- <svg viewBox="0 0 576 512" fill="currentColor" width="16" height="16" xmlns="http://www.w3.org/2000/svg">
37608
- <path d="M482.3 192c34.2 0 93.7 29 93.7 64c0 36-59.5 64-93.7 64l-116.6 0L265.2 495.9c-5.7 10.1-16.3 16.1-27.8 16.1l-56.2 0c-10.6 0-18.3-10.3-15.8-20.6l49-196.3L112 288 68.8 377.6c-3 6.1-9.2 10.4-16 10.4l-2.8 0c-9.2 0-16-8.8-13.3-17.6L80 256L36.7 141.6C34 132.8 40.8 124 50 124l2.8 0c6.8 0 13 4.3 16 10.4L112 224l102.9 0-49-196.3C163.5 17.3 171.2 7 181.8 7l56.2 0c11.5 0 22.1 6 27.8 16.1L365.7 192l116.6 0z"/>
37609
- </svg>
37735
+ iconHtml = `
37736
+ <div class="map-view__marker map-view__marker--plane">
37737
+ <svg viewBox="0 0 576 512" fill="currentColor" width="16" height="16" xmlns="http://www.w3.org/2000/svg">
37738
+ <path d="M482.3 192c34.2 0 93.7 29 93.7 64c0 36-59.5 64-93.7 64l-116.6 0L265.2 495.9c-5.7 10.1-16.3 16.1-27.8 16.1l-56.2 0c-10.6 0-18.3-10.3-15.8-20.6l49-196.3L112 288 68.8 377.6c-3 6.1-9.2 10.4-16 10.4l-2.8 0c-9.2 0-16-8.8-13.3-17.6L80 256L36.7 141.6C34 132.8 40.8 124 50 124l2.8 0c6.8 0 13 4.3 16 10.4L112 224l102.9 0-49-196.3C163.5 17.3 171.2 7 181.8 7l56.2 0c11.5 0 22.1 6 27.8 16.1L365.7 192l116.6 0z"/>
37739
+ </svg>
37610
37740
  </div>`;
37611
37741
  }
37612
37742
  else {
37613
37743
  stopIndex++;
37614
37744
  const imgStyle = dest.imageUrl ? `style="background-image:url('${dest.imageUrl}')"` : '';
37615
- iconHtml = `
37616
- <div class="map-view__marker map-view__marker--stop" ${imgStyle}>
37617
- ${!dest.imageUrl ? `<span class="map-view__marker-number">${stopIndex}</span>` : ''}
37618
- <span class="map-view__marker-badge">${stopIndex}</span>
37745
+ iconHtml = `
37746
+ <div class="map-view__marker map-view__marker--stop" ${imgStyle}>
37747
+ ${!dest.imageUrl ? `<span class="map-view__marker-number">${stopIndex}</span>` : ''}
37748
+ <span class="map-view__marker-badge">${stopIndex}</span>
37619
37749
  </div>`;
37620
37750
  }
37621
37751
  const icon = L.divIcon({