@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.
@@ -2,7 +2,7 @@
2
2
  import * as React from 'react';
3
3
  import React__default, { useLayoutEffect as useLayoutEffect$1, useEffect, useContext, useState, useRef, useCallback, useMemo } from 'react';
4
4
  import JsonURL from '@jsonurl/jsonurl';
5
- import { format as format$2, isToday, getDate, startOfWeek, startOfMonth, endOfWeek, addWeeks, endOfMonth, eachDayOfInterval, getYear, getMonth, getISOWeek, getISODay, isSameMonth, startOfDay, isAfter, isEqual, isWithinInterval, endOfDay, isBefore, isSameDay, differenceInCalendarDays, addMonths, addDays, formatISO, addYears, differenceInYears, parseISO, differenceInMinutes, parse, startOfToday, isValid } from 'date-fns';
5
+ import { format as format$2, isToday, getDate, startOfWeek, startOfMonth, endOfWeek, addWeeks, endOfMonth, eachDayOfInterval, getYear, getMonth, getISOWeek, getISODay, isSameMonth, startOfDay, isAfter, isEqual, isWithinInterval, endOfDay, isBefore, isSameDay, differenceInCalendarDays, addMonths, addDays, formatISO, addYears, differenceInYears, parseISO, differenceInMinutes, isValid, parse, startOfToday } from 'date-fns';
6
6
  import { merge, cloneDeep, isNil, isArray as isArray$1, compact, isEmpty as isEmpty$1, range, chunk, isFunction as isFunction$1, clamp, now, omit, isPlainObject as isPlainObject$1, mapValues, pickBy, first, orderBy, uniq, uniqBy, sortBy, sum, last, findIndex as findIndex$1, set, get, isEqual as isEqual$1, groupBy, minBy, concat, flatMap } from 'lodash';
7
7
  import { arSA, da, de, enGB, es, fr, is, it, nl, nb, pl, pt, sv, ja, enUS } from 'date-fns/locale';
8
8
  import { usePopper } from 'react-popper';
@@ -22861,6 +22861,10 @@ const NAME_CHARACTERS_REGEX = /^[\p{L}\s-]+$/u;
22861
22861
  function hasInvalidNameCharacters(name) {
22862
22862
  return !NAME_CHARACTERS_REGEX.test(name);
22863
22863
  }
22864
+ // Rejects values like a 5-digit year, which the native date input allows while typing
22865
+ function isValidBirthDate(birthDateText) {
22866
+ return isValid(parse(birthDateText, 'yyyy-MM-dd', new Date()));
22867
+ }
22864
22868
  function getAge(birthDateText, startDateText) {
22865
22869
  var birthDate = new Date(birthDateText);
22866
22870
  var startDate = new Date(startDateText);
@@ -22928,7 +22932,7 @@ const validateForm$1 = (values, agentRequired, bookingType, translations, formFi
22928
22932
  }
22929
22933
  }
22930
22934
  if (isFormFieldPresent('birthDate')) {
22931
- if (isEmpty$1(adult.birthDate)) {
22935
+ if (isEmpty$1(adult.birthDate) || !isValidBirthDate(adult.birthDate)) {
22932
22936
  set(errors, `rooms[${rIndex}].adults[${index}].birthDate`, formatTravelerField(rIndex + 1, index + 1, translations.TRAVELERS_FORM.BIRTHDATE));
22933
22937
  }
22934
22938
  else if (values.endDate) {
@@ -22961,7 +22965,7 @@ const validateForm$1 = (values, agentRequired, bookingType, translations, formFi
22961
22965
  }
22962
22966
  }
22963
22967
  if (isFormFieldPresent('birthDate')) {
22964
- if (isEmpty$1(child.birthDate)) {
22968
+ if (isEmpty$1(child.birthDate) || !isValidBirthDate(child.birthDate)) {
22965
22969
  set(errors, `rooms[${rIndex}].children[${index}].birthDate`, formatTravelerField(rIndex + 1, r.adults.length + index + 1, translations.TRAVELERS_FORM.BIRTHDATE));
22966
22970
  }
22967
22971
  else if (child.isBaby && values.startDate) {
@@ -23186,6 +23190,13 @@ const TypeAheadInput = ({ name, value, placeholder, options, onChange, onSelect,
23186
23190
  function isBabyAge(age, maxBabyAge = BABY_MAX_AGE) {
23187
23191
  return typeof age === 'number' && age <= maxBabyAge;
23188
23192
  }
23193
+ // The native date input can hold values format() throws on (e.g. a 5+ digit year while typing), so never format an unvalidated birthDate
23194
+ function formatBirthDate$1(birthDate) {
23195
+ if (!birthDate)
23196
+ return '';
23197
+ const parsed = parse(birthDate, 'yyyy-MM-dd', new Date());
23198
+ return isValid(parsed) ? format$2(parsed, 'dd-MM-yyyy') : '';
23199
+ }
23189
23200
  function createTraveler(traveler, followNumber, personTranslation, isCompact, maxBabyAge) {
23190
23201
  if (isCompact) {
23191
23202
  return {
@@ -23493,10 +23504,7 @@ const SharedTravelersForm = ({ formik, translations, travellersSettings, countri
23493
23504
  !useCompactForm && (bookingType !== 'b2b' || travellersSettings?.mainBookerFormFields?.length) ? (React__default.createElement("div", { className: "form__region" },
23494
23505
  React__default.createElement("div", { className: "form__region-header" },
23495
23506
  React__default.createElement("h5", { className: "form__region-heading" }, translations.TRAVELERS_FORM.MAIN_BOOKER),
23496
- React__default.createElement("p", { className: "form__region-label" }, compact([
23497
- compact([mainBooker?.firstName, mainBooker?.lastName]).join(' '),
23498
- mainBooker?.birthDate && format$2(parse(mainBooker.birthDate, 'yyyy-MM-dd', new Date()), 'dd-MM-yyyy')
23499
- ]).join(', '))),
23507
+ React__default.createElement("p", { className: "form__region-label" }, compact([compact([mainBooker?.firstName, mainBooker?.lastName]).join(' '), formatBirthDate$1(mainBooker?.birthDate)]).join(', '))),
23500
23508
  travellersSettings?.mainBookerFormFields?.length ? (React__default.createElement("div", { className: "main-booker-form__grid" }, travellersSettings.mainBookerFormFields.map((field, index) => (React__default.createElement("div", { key: index, className: `control control--${field.type}` }, getControl(field.type, {}, field.type)))))) : (React__default.createElement(React__default.Fragment, null,
23501
23509
  React__default.createElement("div", { className: "form__twocolumn" },
23502
23510
  React__default.createElement("div", { className: "form__twocolumn-column" },
@@ -27341,7 +27349,8 @@ const initialState$1 = {
27341
27349
  currentStep: 0,
27342
27350
  bookingNumber: undefined,
27343
27351
  trajectNodeAvailability: {},
27344
- trajectFlightsResolved: false
27352
+ trajectFlightAvailability: {},
27353
+ trajectEditLineGuid: null
27345
27354
  };
27346
27355
  const searchResultsSlice = createSlice({
27347
27356
  name: 'searchResults',
@@ -27563,16 +27572,51 @@ const searchResultsSlice = createSlice({
27563
27572
  const guids = new Set(action.payload);
27564
27573
  state.editablePackagingEntry.lines = (state.editablePackagingEntry.lines ?? []).filter((x) => !guids.has(x.guid));
27565
27574
  },
27566
- setTrajectFlightsResolved(state, action) {
27567
- state.trajectFlightsResolved = action.payload;
27575
+ setTrajectFlightLoading(state, action) {
27576
+ const lineGuid = action.payload;
27577
+ state.trajectFlightAvailability[lineGuid] = {
27578
+ lineGuid,
27579
+ status: 'loading',
27580
+ results: state.trajectFlightAvailability[lineGuid]?.results ?? [],
27581
+ selectedGuid: state.trajectFlightAvailability[lineGuid]?.selectedGuid ?? null
27582
+ };
27583
+ },
27584
+ setTrajectFlightAvailability(state, action) {
27585
+ const { lineGuid, results, selectedGuid } = action.payload;
27586
+ state.trajectFlightAvailability[lineGuid] = {
27587
+ lineGuid,
27588
+ status: selectedGuid ? 'resolved' : 'unavailable',
27589
+ results,
27590
+ selectedGuid
27591
+ };
27592
+ },
27593
+ setTrajectFlightSelection(state, action) {
27594
+ const flight = state.trajectFlightAvailability[action.payload.lineGuid];
27595
+ if (!flight)
27596
+ return;
27597
+ flight.selectedGuid = action.payload.guid;
27598
+ flight.status = 'resolved';
27599
+ },
27600
+ setTrajectEditLineGuid(state, action) {
27601
+ state.trajectEditLineGuid = action.payload;
27602
+ },
27603
+ updateTrajectNodeResult(state, action) {
27604
+ const { lineGuid, result } = action.payload;
27605
+ const node = state.trajectNodeAvailability[lineGuid];
27606
+ if (!node)
27607
+ return;
27608
+ node.results = node.results.map((x) => (x.code === result.code ? result : x));
27609
+ node.selectedCode = result.code;
27610
+ node.status = 'resolved';
27568
27611
  },
27569
27612
  resetTrajectAvailability(state) {
27570
27613
  state.trajectNodeAvailability = {};
27571
- state.trajectFlightsResolved = false;
27614
+ state.trajectFlightAvailability = {};
27615
+ state.trajectEditLineGuid = null;
27572
27616
  }
27573
27617
  }
27574
27618
  });
27575
- 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;
27619
+ 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;
27576
27620
  var searchResultsReducer = searchResultsSlice.reducer;
27577
27621
 
27578
27622
  const ItemPicker = ({ items, selection, selectedSortByType, label, placeholder, classModifier, onPick, valueFormatter }) => {
@@ -30552,6 +30596,293 @@ const ExcursionDetails = () => {
30552
30596
  React__default.createElement("button", { type: "button", className: "cta cta--primary", onClick: handleConfirm }, translations?.QSM.CONFIRM))));
30553
30597
  };
30554
30598
 
30599
+ const REGULAR_TRAJECT_NODE_TYPE = 0;
30600
+ const isSearchableTrajectNode = (node) => node.serviceType === ACCOMMODATION_SERVICE_TYPE || node.serviceType === EXCURSION_SERVICE_TYPE;
30601
+ const isTrajectFlightNode = (node) => node.serviceType === FLIGHT_SERVICE_TYPE;
30602
+ const getTrajectNodesWithLines = (trajectEntry) => {
30603
+ const linesByGuid = new Map((trajectEntry.entry.lines ?? []).map((line) => [line.guid, line]));
30604
+ return [...(trajectEntry.nodes ?? [])]
30605
+ .sort((a, b) => a.dayOrder - b.dayOrder || a.order - b.order)
30606
+ .map((node) => {
30607
+ const guids = node.lineGuids?.length ? node.lineGuids : [node.lineGuid];
30608
+ const lines = guids.map((guid) => linesByGuid.get(guid)).filter((x) => !!x);
30609
+ return { node, line: lines[0], lines };
30610
+ })
30611
+ .filter((x) => !!x.line);
30612
+ };
30613
+ const buildDestination = (line) => {
30614
+ if (line.location?.id)
30615
+ return { id: line.location.id, isLocation: true };
30616
+ if (line.oord?.id)
30617
+ return { id: line.oord.id, isOord: true };
30618
+ if (line.region?.id)
30619
+ return { id: line.region.id, isRegion: true };
30620
+ if (line.country?.id)
30621
+ return { id: line.country.id, isCountry: true };
30622
+ if (line.latitude && line.longitude)
30623
+ return { latitude: line.latitude, longitude: line.longitude };
30624
+ return { id: 0 };
30625
+ };
30626
+ const buildTrajectNodeRequest = ({ node, line }, context, productCode) => ({
30627
+ transactionId: context.transactionId,
30628
+ officeId: context.officeId,
30629
+ agentId: context.agentId ?? null,
30630
+ catalogueId: context.catalogueId,
30631
+ searchConfigurationId: context.searchConfigurationId,
30632
+ portalId: context.portalId ?? null,
30633
+ vendorConfigurationId: node.externalVendorId ?? null,
30634
+ language: context.language,
30635
+ serviceType: node.serviceType,
30636
+ fromDate: toDateOnlyString(line.from),
30637
+ toDate: toDateOnlyString(line.to),
30638
+ destination: buildDestination(line),
30639
+ productCode,
30640
+ rooms: context.rooms,
30641
+ tagIds: []
30642
+ });
30643
+ const getNodeCandidates = (node) => {
30644
+ const configured = (node.alternatives ?? []).filter((x) => !!x.code && x.code.trim().length > 0);
30645
+ if (configured.length)
30646
+ return configured;
30647
+ const fallbackCode = node.code ?? node.preferredAccommodationCode;
30648
+ if (!fallbackCode || !fallbackCode.trim().length)
30649
+ return [];
30650
+ return [
30651
+ {
30652
+ code: fallbackCode,
30653
+ name: node.name,
30654
+ description: node.description,
30655
+ imageUrl: node.imageUrl,
30656
+ contentSource: node.contentSource,
30657
+ vendor: node.vendor,
30658
+ externalVendorId: node.externalVendorId,
30659
+ isPreferred: true
30660
+ }
30661
+ ];
30662
+ };
30663
+ const findCandidateForResult = (node, result) => getNodeCandidates(node).find((candidate) => matchesCode(result, candidate.code)) ?? null;
30664
+ const matchesCode = (result, code) => {
30665
+ if (result.code === code)
30666
+ return true;
30667
+ return (result.rooms ?? []).some((room) => (room.options ?? []).some((option) => option.accommodationCode === code));
30668
+ };
30669
+ const getPackagingRoomsFromEntry = (entry) => {
30670
+ const paxById = new Map((entry.pax ?? []).map((p) => [p.id, p]));
30671
+ const rooms = (entry.rooms ?? []).map((room) => ({
30672
+ travellers: (room.paxIds ?? []).map((paxId) => {
30673
+ const pax = paxById.get(paxId);
30674
+ return {
30675
+ id: paxId,
30676
+ age: pax?.age ?? null,
30677
+ dateOfBirth: pax?.dateOfBirth ?? null
30678
+ };
30679
+ })
30680
+ }));
30681
+ return rooms.filter((room) => room.travellers.length > 0);
30682
+ };
30683
+ const pickCheapest = (results) => [...results].sort((a, b) => a.price - b.price)[0] ?? null;
30684
+ const applyResultToLines = (lines, result) => lines.map((line, index) => applyResultToLine(line, result, index));
30685
+ const getOptionForRoom = (result, roomIndex) => {
30686
+ const room = (result.rooms ?? [])[roomIndex];
30687
+ if (room)
30688
+ return (room.options ?? []).find((x) => x.isSelected) ?? (room.options ?? [])[0];
30689
+ return (result.rooms ?? []).flatMap((x) => x.options ?? []).find((x) => x.isSelected) ?? (result.rooms ?? [])[0]?.options?.[0];
30690
+ };
30691
+ const applyResultToLine = (line, result, roomIndex = 0) => {
30692
+ const option = getOptionForRoom(result, roomIndex);
30693
+ return {
30694
+ ...line,
30695
+ productName: result.name ?? line.productName,
30696
+ productCode: result.code ?? line.productCode,
30697
+ accommodationCode: option?.accommodationCode ?? line.accommodationCode,
30698
+ accommodationName: option?.accommodationName ?? line.accommodationName,
30699
+ regimeCode: option?.regimeCode ?? line.regimeCode,
30700
+ regimeName: option?.regimeName ?? line.regimeName,
30701
+ latitude: result.latitude ?? line.latitude,
30702
+ longitude: result.longitude ?? line.longitude,
30703
+ isChanged: true
30704
+ };
30705
+ };
30706
+ const toDateOnlyUtcString = (value) => {
30707
+ const date = new Date(value);
30708
+ return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate())).toISOString();
30709
+ };
30710
+ const toTimeOnlyString = (value) => {
30711
+ const date = new Date(value);
30712
+ const hh = String(date.getUTCHours()).padStart(2, '0');
30713
+ const mm = String(date.getUTCMinutes()).padStart(2, '0');
30714
+ const ss = String(date.getUTCSeconds()).padStart(2, '0');
30715
+ return `${hh}:${mm}:${ss}`;
30716
+ };
30717
+ const mapFlightSegmentsToFlightLines = (segments) => segments.map((segment) => ({
30718
+ airlineCode: segment.marketingAirlineCode,
30719
+ airlineDescription: segment.marketingAirlineName,
30720
+ operatingAirlineCode: segment.operatingAirlineCode,
30721
+ operatingAirlineDescription: segment.operatingAirlineName,
30722
+ flightNumber: segment.flightNumber,
30723
+ operatingFlightNumber: segment.operatingFlightNumber ?? null,
30724
+ departureDate: toDateOnlyUtcString(segment.departureDateTime),
30725
+ departureTime: toTimeOnlyString(segment.departureDateTime),
30726
+ departureAirportCode: segment.departureAirportCode,
30727
+ departureAirportDescription: segment.departureAirportName,
30728
+ arrivalDate: toDateOnlyUtcString(segment.arrivalDateTime),
30729
+ arrivalTime: toTimeOnlyString(segment.arrivalDateTime),
30730
+ arrivalAirportCode: segment.arrivalAirportCode,
30731
+ arrivalAirportDescription: segment.arrivalAirportName,
30732
+ durationInTicks: segment.durationInTicks
30733
+ }));
30734
+ const buildTrajectFlightRequest = ({ node, line }, context, pax) => ({
30735
+ transactionId: context.transactionId,
30736
+ officeId: context.officeId,
30737
+ catalogueId: context.catalogueId,
30738
+ agentId: context.agentId ?? null,
30739
+ language: context.language,
30740
+ departureAirportCode: node.departureAirportCode ?? '',
30741
+ arrivalAirportCode: node.arrivalAirportCode ?? '',
30742
+ returnAirportCode: null,
30743
+ luggageIncluded: null,
30744
+ maxStops: null,
30745
+ travelClass: null,
30746
+ vendorConfigurationId: node.externalVendorId ?? null,
30747
+ pax,
30748
+ outward: { date: dateToDateStruct(new Date(line.from)) },
30749
+ return: null
30750
+ });
30751
+ const pickCheapestFlight = (flights) => [...flights].sort((a, b) => a.price - b.price)[0] ?? null;
30752
+ const applyFlightToLine = (line, flight) => {
30753
+ const segments = flight.outward?.segments ?? [];
30754
+ if (!segments.length)
30755
+ return line;
30756
+ const firstSegment = segments[0];
30757
+ const lastSegment = segments[segments.length - 1];
30758
+ return {
30759
+ ...line,
30760
+ from: new Date(firstSegment.departureDateTime).toISOString(),
30761
+ to: new Date(lastSegment.arrivalDateTime).toISOString(),
30762
+ productName: `${firstSegment.departureAirportName} - ${lastSegment.arrivalAirportName} (${firstSegment.marketingAirlineName})`,
30763
+ productCode: `${firstSegment.departureAirportCode} ${lastSegment.arrivalAirportCode}/${firstSegment.marketingAirlineCode}`,
30764
+ accommodationName: firstSegment.metaData?.farePriceClassName ?? line.accommodationName,
30765
+ accommodationCode: firstSegment.metaData?.fareCode ?? line.accommodationCode,
30766
+ flightInformation: {
30767
+ pnr: '',
30768
+ flightLines: mapFlightSegmentsToFlightLines(segments)
30769
+ },
30770
+ isChanged: true
30771
+ };
30772
+ };
30773
+
30774
+ const getLocation = (result) => {
30775
+ const place = result.locationName || result.regionName || result.oordName;
30776
+ if (!place)
30777
+ return result.countryName ?? '';
30778
+ return result.countryName ? `${place}, ${result.countryName}` : place;
30779
+ };
30780
+ const toPlainText = (value) => {
30781
+ if (!value)
30782
+ return '';
30783
+ return he
30784
+ .decode(value.replace(/<[^>]*>/g, ' '))
30785
+ .replace(/\s+/g, ' ')
30786
+ .trim();
30787
+ };
30788
+ const TrajectNodeCard = ({ item, result, isSelected, languageCode, translations, onSelect, onEditOptions, showNights }) => {
30789
+ const { node } = item;
30790
+ const selectedPerRoom = (result.rooms ?? []).map((room) => (room.options ?? []).find((x) => x.isSelected) ?? (room.options ?? [])[0]);
30791
+ const isMultiRoom = selectedPerRoom.length > 1;
30792
+ const price = formatPrice$3(result.price, result.currencyCode, languageCode);
30793
+ const nights = calculateNights(new Date(result.fromDate), new Date(result.toDate));
30794
+ const candidate = findCandidateForResult(node, result);
30795
+ const image = candidate?.imageUrl ?? null;
30796
+ const description = toPlainText(candidate?.description);
30797
+ const title = candidate?.name ?? result.name;
30798
+ const priceBlock = (React__default.createElement("div", { className: "search__result-card__price__wrapper" },
30799
+ React__default.createElement("span", { className: "search__result-card__price__label" }, translations?.SHARED.TOTAL_PRICE),
30800
+ React__default.createElement("span", { className: "search__result-card__price" }, price)));
30801
+ const canEditOptions = isSelected && !!onEditOptions && (result.rooms ?? []).some((room) => (room.options ?? []).length > 1);
30802
+ const selectButton = (React__default.createElement(React__default.Fragment, null,
30803
+ React__default.createElement("button", { type: "button", className: `cta ${isSelected ? 'cta--selected' : 'cta--select'}`, onClick: onSelect }, isSelected ? translations?.SHARED.SELECTED : translations?.SHARED.SELECT),
30804
+ canEditOptions && (React__default.createElement("button", { type: "button", className: "cta cta--secondary", onClick: onEditOptions },
30805
+ translations?.SRP.SELECT,
30806
+ " ",
30807
+ translations?.SRP.ACCOMMODATION))));
30808
+ if (result.contents?.length) {
30809
+ return (React__default.createElement("div", { className: `search__result-card__wrapper search__result-card__wrapper--custom ${isSelected ? 'search__result-card__wrapper--selected' : ''}` },
30810
+ React__default.createElement("div", { className: "search__result-card__top", dangerouslySetInnerHTML: { __html: he.decode(result.contents) } }),
30811
+ React__default.createElement("div", { className: "search__result-card__footer" }, selectButton)));
30812
+ }
30813
+ return (React__default.createElement("div", { className: `search__result-card__wrapper ${isSelected ? 'search__result-card__wrapper--selected' : ''}` },
30814
+ image && (React__default.createElement("div", { className: "search__result-card__img-wrapper" },
30815
+ React__default.createElement("img", { src: image, alt: title, className: "search__result-card__img" }),
30816
+ priceBlock)),
30817
+ React__default.createElement("div", { className: "search__result-card__content" },
30818
+ React__default.createElement("div", { className: "search__result-card__content__wrapper" },
30819
+ React__default.createElement("div", { className: "search__result-card__header" },
30820
+ React__default.createElement("div", { className: "search__result-card__header__wrapper" },
30821
+ !!result.stars && (React__default.createElement("div", { className: "rating" }, [...Array(result.stars)].map((_, index) => (React__default.createElement(Icon, { name: "ui-star", key: `rating-star-${index + 1}`, width: 14, height: 14 }))))),
30822
+ React__default.createElement("h3", { className: "search__result-card__title" }, title)),
30823
+ !image && priceBlock),
30824
+ React__default.createElement("span", { className: "search__result-card__location" },
30825
+ React__default.createElement(Icon, { name: "ui-location", height: 16 }),
30826
+ getLocation(result)),
30827
+ React__default.createElement("div", { className: "search__result-card__options" },
30828
+ showNights && nights > 0 && (React__default.createElement("div", { className: "search__result-card__option" },
30829
+ React__default.createElement(Icon, { name: "ui-moon", height: 16 }),
30830
+ nights,
30831
+ " ",
30832
+ translations?.SRP.NIGHTS)),
30833
+ selectedPerRoom.map((option, roomIndex) => (React__default.createElement(React__default.Fragment, { key: `room-${roomIndex}` },
30834
+ option?.accommodationName && (React__default.createElement("div", { className: "search__result-card__option" },
30835
+ React__default.createElement(Icon, { name: "ui-bed", height: 16 }),
30836
+ isMultiRoom && `${translations?.SHARED.ROOM} ${roomIndex + 1}: `,
30837
+ option.accommodationName)),
30838
+ option?.regimeName && (React__default.createElement("div", { className: "search__result-card__option" },
30839
+ React__default.createElement(Icon, { name: "ui-utensils", height: 16 }),
30840
+ option.regimeName)))))),
30841
+ description && React__default.createElement("p", { className: "search__result-card__description" }, description)),
30842
+ React__default.createElement("div", { className: "search__result-card__footer" }, selectButton))));
30843
+ };
30844
+
30845
+ const TrajectResultsFlyIn = ({ isLoading }) => {
30846
+ const context = useContext(SearchResultsConfigurationContext);
30847
+ const dispatch = useDispatch();
30848
+ const translations = getTranslations(context?.languageCode ?? 'en-GB');
30849
+ const { trajectNodeAvailability, trajectEditLineGuid } = useSelector((state) => state.searchResults);
30850
+ const trajectEntry = context?.trajectEntry;
30851
+ const item = useMemo(() => {
30852
+ if (!trajectEntry || !trajectEditLineGuid)
30853
+ return null;
30854
+ return getTrajectNodesWithLines(trajectEntry).find((x) => x.line.guid === trajectEditLineGuid) ?? null;
30855
+ }, [trajectEntry, trajectEditLineGuid]);
30856
+ if (!context || !item)
30857
+ return null;
30858
+ const availability = trajectNodeAvailability[item.line.guid];
30859
+ const results = availability?.results ?? [];
30860
+ const ordered = [...results].sort((a, b) => {
30861
+ const aConfigured = findCandidateForResult(item.node, a) ? 0 : 1;
30862
+ const bConfigured = findCandidateForResult(item.node, b) ? 0 : 1;
30863
+ return aConfigured - bConfigured || a.price - b.price;
30864
+ });
30865
+ const handleSelect = (code) => {
30866
+ const result = results.find((x) => x.code === code);
30867
+ if (!result)
30868
+ return;
30869
+ dispatch(setTrajectNodeSelection({ lineGuid: item.line.guid, code }));
30870
+ applyResultToLines(item.lines, result).forEach((line) => dispatch(updateEditableEntryLine(line)));
30871
+ dispatch(setTrajectEditLineGuid(null));
30872
+ dispatch(setFlyInIsOpen(false));
30873
+ };
30874
+ if (isLoading) {
30875
+ return React__default.createElement(React__default.Fragment, null, context.customSpinner ?? React__default.createElement(Spinner, { label: translations.SRP.LOADING_ACCOMMODATIONS }));
30876
+ }
30877
+ return (React__default.createElement("div", { className: "flyin__content" },
30878
+ React__default.createElement("div", { className: "search__result-row" },
30879
+ React__default.createElement("span", { className: "search__result-row-text" },
30880
+ ordered.length,
30881
+ "\u00A0",
30882
+ translations.SRP.TOTAL_RESULTS_LABEL)),
30883
+ ordered.length === 0 ? (React__default.createElement("div", { className: "no-results" }, translations.SRP.NO_RESULTS)) : (React__default.createElement("div", { className: "search__results__cards search__results__cards--compact" }, ordered.map((result) => (React__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 })))))));
30884
+ };
30885
+
30555
30886
  const FlyIn = ({ srpType, isOpen, setIsOpen, className = '', onPanelRef, detailsLoading, flyInType, isPackageEditFlow, handleConfirm, sortByTypes, activeSearchSeed, toggleFilters, filtersOpen }) => {
30556
30887
  const dispatch = useDispatch();
30557
30888
  const context = useContext(SearchResultsConfigurationContext);
@@ -30597,6 +30928,10 @@ const FlyIn = ({ srpType, isOpen, setIsOpen, className = '', onPanelRef, details
30597
30928
  }
30598
30929
  };
30599
30930
  const handleGoBack = () => {
30931
+ if (context?.trajectEntry) {
30932
+ handleClose();
30933
+ return;
30934
+ }
30600
30935
  if (flyInType === 'acco-details') {
30601
30936
  dispatch(setFlyInType('acco-results'));
30602
30937
  }
@@ -30620,7 +30955,7 @@ const FlyIn = ({ srpType, isOpen, setIsOpen, className = '', onPanelRef, details
30620
30955
  dispatch(setBookPackagingEntry(true));
30621
30956
  }
30622
30957
  };
30623
- return (React__default.createElement("div", { className: `flyin ${isOpen ? 'flyin--active' : ''} ${className} ${isPackageEditFlow || flyInType === 'acco-results' ? 'flyin--large' : ''} ${flyInType === 'excursion-results' || flyInType === 'excursion-details' ? 'flyin--medium' : ''}
30958
+ return (React__default.createElement("div", { className: `flyin ${isOpen ? 'flyin--active' : ''} ${className} ${isPackageEditFlow || flyInType === 'acco-results' ? 'flyin--large' : ''} ${flyInType === 'excursion-results' || flyInType === 'excursion-details' ? 'flyin--medium' : ''}
30624
30959
  ${flyInType === 'flight-outward-results' || flyInType === 'flight-return-results' ? 'flyin--flight' : ''}` },
30625
30960
  React__default.createElement("div", { className: `flyin__panel ${isOpen ? 'flyin__panel--active' : ''}`, ref: panelRef },
30626
30961
  React__default.createElement("div", { className: "flyin__content" },
@@ -30646,7 +30981,10 @@ const FlyIn = ({ srpType, isOpen, setIsOpen, className = '', onPanelRef, details
30646
30981
  React__default.createElement(Icon, { name: "ui-chevron", width: 14, height: 14, "aria-hidden": "true" }),
30647
30982
  "Go Back")))),
30648
30983
  srpType === build.PortalQsmType.Flight && React__default.createElement(FlightsFlyIn, { isOpen: isOpen, setIsOpen: setIsOpen }),
30649
- (srpType === build.PortalQsmType.Accommodation || srpType === build.PortalQsmType.AccommodationAndFlight) && flyInType === 'acco-results' && (React__default.createElement("div", { className: "flyin__content flyin__content--columns" },
30984
+ context?.trajectEntry && flyInType === 'acco-results' && React__default.createElement(TrajectResultsFlyIn, { isLoading: detailsLoading }),
30985
+ !context?.trajectEntry &&
30986
+ (srpType === build.PortalQsmType.Accommodation || srpType === build.PortalQsmType.AccommodationAndFlight) &&
30987
+ flyInType === 'acco-results' && (React__default.createElement("div", { className: "flyin__content flyin__content--columns" },
30650
30988
  React__default.createElement(Filters, { initialFilters: initialFilters, filters: filters, isOpen: filtersOpen, handleSetIsOpen: () => toggleFilters && toggleFilters(),
30651
30989
  // handleApplyFilters={() => setSearchTrigger((prev) => prev + 1)}
30652
30990
  isLoading: isLoading, setFilters: (filters) => dispatch(setFilters(filters)), resetFilters: (filters) => dispatch(resetFilters(filters)) }),
@@ -32919,222 +33257,6 @@ const BookPackagingEntry = ({ activeSearchSeed, isLoading, isConfirmationPage })
32919
33257
  React__default.createElement(WLSidebar, { activeSearchSeed: activeSearchSeed, packagingAccoResult: selectedPackagingAccoResult }))));
32920
33258
  };
32921
33259
 
32922
- const REGULAR_TRAJECT_NODE_TYPE = 0;
32923
- const isSearchableTrajectNode = (node) => node.serviceType === ACCOMMODATION_SERVICE_TYPE || node.serviceType === EXCURSION_SERVICE_TYPE;
32924
- const isTrajectFlightNode = (node) => node.serviceType === FLIGHT_SERVICE_TYPE;
32925
- const getTrajectNodesWithLines = (trajectEntry) => {
32926
- const linesByGuid = new Map((trajectEntry.entry.lines ?? []).map((line) => [line.guid, line]));
32927
- return [...(trajectEntry.nodes ?? [])]
32928
- .sort((a, b) => a.dayOrder - b.dayOrder || a.order - b.order)
32929
- .map((node) => ({ node, line: linesByGuid.get(node.lineGuid) }))
32930
- .filter((x) => !!x.line);
32931
- };
32932
- const buildDestination = (line) => {
32933
- if (line.location?.id)
32934
- return { id: line.location.id, isLocation: true };
32935
- if (line.oord?.id)
32936
- return { id: line.oord.id, isOord: true };
32937
- if (line.region?.id)
32938
- return { id: line.region.id, isRegion: true };
32939
- if (line.country?.id)
32940
- return { id: line.country.id, isCountry: true };
32941
- if (line.latitude && line.longitude)
32942
- return { latitude: line.latitude, longitude: line.longitude };
32943
- return { id: 0 };
32944
- };
32945
- const buildTrajectNodeRequest = ({ node, line }, context, productCode) => ({
32946
- transactionId: context.transactionId,
32947
- officeId: context.officeId,
32948
- agentId: context.agentId ?? null,
32949
- catalogueId: context.catalogueId,
32950
- searchConfigurationId: context.searchConfigurationId,
32951
- portalId: context.portalId ?? null,
32952
- vendorConfigurationId: node.externalVendorId ?? null,
32953
- language: context.language,
32954
- serviceType: node.serviceType,
32955
- fromDate: toDateOnlyString(line.from),
32956
- toDate: toDateOnlyString(line.to),
32957
- destination: buildDestination(line),
32958
- productCode,
32959
- rooms: context.rooms,
32960
- tagIds: []
32961
- });
32962
- const getNodeCandidates = (node) => {
32963
- const configured = (node.alternatives ?? []).filter((x) => !!x.code && x.code.trim().length > 0);
32964
- if (configured.length)
32965
- return configured;
32966
- const fallbackCode = node.code ?? node.preferredAccommodationCode;
32967
- if (!fallbackCode || !fallbackCode.trim().length)
32968
- return [];
32969
- return [
32970
- {
32971
- code: fallbackCode,
32972
- name: node.name,
32973
- description: node.description,
32974
- imageUrl: node.imageUrl,
32975
- contentSource: node.contentSource,
32976
- vendor: node.vendor,
32977
- externalVendorId: node.externalVendorId,
32978
- isPreferred: true
32979
- }
32980
- ];
32981
- };
32982
- const findCandidateForResult = (node, result) => getNodeCandidates(node).find((candidate) => matchesCode(result, candidate.code)) ?? null;
32983
- const matchesCode = (result, code) => {
32984
- if (result.code === code)
32985
- return true;
32986
- return (result.rooms ?? []).some((room) => (room.options ?? []).some((option) => option.accommodationCode === code));
32987
- };
32988
- const getPackagingRoomsFromEntry = (entry) => {
32989
- const paxById = new Map((entry.pax ?? []).map((p) => [p.id, p]));
32990
- const rooms = (entry.rooms ?? []).map((room) => ({
32991
- travellers: (room.paxIds ?? []).map((paxId) => {
32992
- const pax = paxById.get(paxId);
32993
- return {
32994
- id: paxId,
32995
- age: pax?.age ?? null,
32996
- dateOfBirth: pax?.dateOfBirth ?? null
32997
- };
32998
- })
32999
- }));
33000
- return rooms.filter((room) => room.travellers.length > 0);
33001
- };
33002
- const pickCheapest = (results) => [...results].sort((a, b) => a.price - b.price)[0] ?? null;
33003
- const applyResultToLine = (line, result) => {
33004
- const option = (result.rooms ?? []).flatMap((room) => room.options ?? []).find((x) => x.isSelected) ?? (result.rooms ?? [])[0]?.options?.[0];
33005
- return {
33006
- ...line,
33007
- productName: result.name ?? line.productName,
33008
- productCode: result.code ?? line.productCode,
33009
- accommodationCode: option?.accommodationCode ?? line.accommodationCode,
33010
- accommodationName: option?.accommodationName ?? line.accommodationName,
33011
- regimeCode: option?.regimeCode ?? line.regimeCode,
33012
- regimeName: option?.regimeName ?? line.regimeName,
33013
- latitude: result.latitude ?? line.latitude,
33014
- longitude: result.longitude ?? line.longitude,
33015
- isChanged: true
33016
- };
33017
- };
33018
- const toDateOnlyUtcString = (value) => {
33019
- const date = new Date(value);
33020
- return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate())).toISOString();
33021
- };
33022
- const toTimeOnlyString = (value) => {
33023
- const date = new Date(value);
33024
- const hh = String(date.getUTCHours()).padStart(2, '0');
33025
- const mm = String(date.getUTCMinutes()).padStart(2, '0');
33026
- const ss = String(date.getUTCSeconds()).padStart(2, '0');
33027
- return `${hh}:${mm}:${ss}`;
33028
- };
33029
- const mapFlightSegmentsToFlightLines = (segments) => segments.map((segment) => ({
33030
- airlineCode: segment.marketingAirlineCode,
33031
- airlineDescription: segment.marketingAirlineName,
33032
- operatingAirlineCode: segment.operatingAirlineCode,
33033
- operatingAirlineDescription: segment.operatingAirlineName,
33034
- flightNumber: segment.flightNumber,
33035
- operatingFlightNumber: segment.operatingFlightNumber ?? null,
33036
- departureDate: toDateOnlyUtcString(segment.departureDateTime),
33037
- departureTime: toTimeOnlyString(segment.departureDateTime),
33038
- departureAirportCode: segment.departureAirportCode,
33039
- departureAirportDescription: segment.departureAirportName,
33040
- arrivalDate: toDateOnlyUtcString(segment.arrivalDateTime),
33041
- arrivalTime: toTimeOnlyString(segment.arrivalDateTime),
33042
- arrivalAirportCode: segment.arrivalAirportCode,
33043
- arrivalAirportDescription: segment.arrivalAirportName,
33044
- durationInTicks: segment.durationInTicks
33045
- }));
33046
- const applyFlightToLine = (line, flight, isOutbound) => {
33047
- const segments = (isOutbound ? flight.outward?.segments : flight.return?.segments) ?? [];
33048
- if (!segments.length)
33049
- return line;
33050
- const firstSegment = segments[0];
33051
- const lastSegment = segments[segments.length - 1];
33052
- return {
33053
- ...line,
33054
- from: new Date(firstSegment.departureDateTime).toISOString(),
33055
- to: new Date(lastSegment.arrivalDateTime).toISOString(),
33056
- productName: `${firstSegment.departureAirportName} - ${lastSegment.arrivalAirportName} (${firstSegment.marketingAirlineName})`,
33057
- productCode: `${firstSegment.departureAirportCode} ${lastSegment.arrivalAirportCode}/${firstSegment.marketingAirlineCode}`,
33058
- accommodationName: firstSegment.metaData?.farePriceClassName ?? line.accommodationName,
33059
- accommodationCode: firstSegment.metaData?.fareCode ?? line.accommodationCode,
33060
- flightInformation: {
33061
- pnr: '',
33062
- flightLines: mapFlightSegmentsToFlightLines(segments)
33063
- },
33064
- isChanged: true
33065
- };
33066
- };
33067
- const pairTrajectFlights = (nodes) => {
33068
- const flights = nodes.filter((x) => isTrajectFlightNode(x.node));
33069
- return {
33070
- outbound: flights[0] ?? null,
33071
- inbound: flights.length > 1 ? flights[flights.length - 1] : null,
33072
- unsupported: flights.slice(1, Math.max(flights.length - 1, 1))
33073
- };
33074
- };
33075
-
33076
- const getLocation = (result) => {
33077
- const place = result.locationName || result.regionName || result.oordName;
33078
- if (!place)
33079
- return result.countryName ?? '';
33080
- return result.countryName ? `${place}, ${result.countryName}` : place;
33081
- };
33082
- const toPlainText = (value) => {
33083
- if (!value)
33084
- return '';
33085
- return he
33086
- .decode(value.replace(/<[^>]*>/g, ' '))
33087
- .replace(/\s+/g, ' ')
33088
- .trim();
33089
- };
33090
- const TrajectNodeCard = ({ item, result, isSelected, languageCode, translations, onSelect, showNights }) => {
33091
- const { node } = item;
33092
- const selectedOption = first(result.rooms)?.options?.find((x) => x.isSelected) ?? first(result.rooms)?.options?.[0];
33093
- const price = formatPrice$3(result.price, result.currencyCode, languageCode);
33094
- const nights = calculateNights(new Date(result.fromDate), new Date(result.toDate));
33095
- const candidate = findCandidateForResult(node, result);
33096
- const image = candidate?.imageUrl ?? null;
33097
- const description = toPlainText(candidate?.description);
33098
- const title = candidate?.name ?? result.name;
33099
- const priceBlock = (React__default.createElement("div", { className: "search__result-card__price__wrapper" },
33100
- React__default.createElement("span", { className: "search__result-card__price__label" }, translations?.SHARED.TOTAL_PRICE),
33101
- React__default.createElement("span", { className: "search__result-card__price" }, price)));
33102
- const selectButton = (React__default.createElement("button", { type: "button", className: `cta ${isSelected ? 'cta--selected' : 'cta--select'}`, onClick: onSelect }, isSelected ? translations?.SHARED.SELECTED : translations?.SHARED.SELECT));
33103
- if (result.contents?.length) {
33104
- return (React__default.createElement("div", { className: `search__result-card__wrapper search__result-card__wrapper--custom ${isSelected ? 'search__result-card__wrapper--selected' : ''}` },
33105
- React__default.createElement("div", { className: "search__result-card__top", dangerouslySetInnerHTML: { __html: he.decode(result.contents) } }),
33106
- React__default.createElement("div", { className: "search__result-card__footer" }, selectButton)));
33107
- }
33108
- return (React__default.createElement("div", { className: `search__result-card__wrapper ${isSelected ? 'search__result-card__wrapper--selected' : ''}` },
33109
- image && (React__default.createElement("div", { className: "search__result-card__img-wrapper" },
33110
- React__default.createElement("img", { src: image, alt: title, className: "search__result-card__img" }),
33111
- priceBlock)),
33112
- React__default.createElement("div", { className: "search__result-card__content" },
33113
- React__default.createElement("div", { className: "search__result-card__content__wrapper" },
33114
- React__default.createElement("div", { className: "search__result-card__header" },
33115
- React__default.createElement("div", { className: "search__result-card__header__wrapper" },
33116
- !!result.stars && (React__default.createElement("div", { className: "rating" }, [...Array(result.stars)].map((_, index) => (React__default.createElement(Icon, { name: "ui-star", key: `rating-star-${index + 1}`, width: 14, height: 14 }))))),
33117
- React__default.createElement("h3", { className: "search__result-card__title" }, title)),
33118
- !image && priceBlock),
33119
- React__default.createElement("span", { className: "search__result-card__location" },
33120
- React__default.createElement(Icon, { name: "ui-location", height: 16 }),
33121
- getLocation(result)),
33122
- React__default.createElement("div", { className: "search__result-card__options" },
33123
- showNights && nights > 0 && (React__default.createElement("div", { className: "search__result-card__option" },
33124
- React__default.createElement(Icon, { name: "ui-moon", height: 16 }),
33125
- nights,
33126
- " ",
33127
- translations?.SRP.NIGHTS)),
33128
- selectedOption?.accommodationName && (React__default.createElement("div", { className: "search__result-card__option" },
33129
- React__default.createElement(Icon, { name: "ui-bed", height: 16 }),
33130
- selectedOption.accommodationName)),
33131
- selectedOption?.regimeName && (React__default.createElement("div", { className: "search__result-card__option" },
33132
- React__default.createElement(Icon, { name: "ui-utensils", height: 16 }),
33133
- selectedOption.regimeName))),
33134
- description && React__default.createElement("p", { className: "search__result-card__description" }, description)),
33135
- React__default.createElement("div", { className: "search__result-card__footer" }, selectButton))));
33136
- };
33137
-
33138
33260
  const getNodeIcon = (node) => {
33139
33261
  if (isTrajectFlightNode(node))
33140
33262
  return 'ui-flight';
@@ -33149,15 +33271,10 @@ const TrajectResults = ({ isLoading }) => {
33149
33271
  const dispatch = useDispatch();
33150
33272
  const translations = getTranslations(context?.languageCode ?? 'en-GB');
33151
33273
  const locale = getLocale(context?.languageCode ?? 'en-GB');
33152
- const { trajectNodeAvailability, flightsLoading, trajectFlightsResolved, selectedOutwardKey, selectedReturnKey } = useSelector((state) => state.searchResults);
33153
- const uniqueOutwardFlights = useSelector(selectUniqueOutwardFlights);
33154
- const uniqueReturnFlights = useSelector(selectUniqueReturnFlights);
33155
- const selectedOutward = useSelector(selectSelectedOutward);
33156
- const selectedReturn = useSelector(selectSelectedReturn);
33274
+ const { trajectNodeAvailability, trajectFlightAvailability } = useSelector((state) => state.searchResults);
33157
33275
  const [expandedNodes, setExpandedNodes] = useState({});
33158
33276
  const trajectEntry = context?.trajectEntry;
33159
33277
  const nodesWithLines = useMemo(() => (trajectEntry ? getTrajectNodesWithLines(trajectEntry) : []), [trajectEntry]);
33160
- const flightPairing = useMemo(() => pairTrajectFlights(nodesWithLines), [nodesWithLines]);
33161
33278
  const lineByNodeId = useMemo(() => new Map(nodesWithLines.map((x) => [x.node.nodeId, x])), [nodesWithLines]);
33162
33279
  const nodesByDay = useMemo(() => {
33163
33280
  const map = new Map();
@@ -33179,12 +33296,28 @@ const TrajectResults = ({ isLoading }) => {
33179
33296
  if (!result)
33180
33297
  return;
33181
33298
  dispatch(setTrajectNodeSelection({ lineGuid: item.line.guid, code }));
33182
- dispatch(updateEditableEntryLine(applyResultToLine(item.line, result)));
33299
+ applyResultToLines(item.lines, result).forEach((line) => dispatch(updateEditableEntryLine(line)));
33300
+ };
33301
+ const handleShowMore = (item) => {
33302
+ dispatch(setTrajectEditLineGuid(item.line.guid));
33303
+ dispatch(setFlyInType('acco-results'));
33304
+ dispatch(setFlyInIsOpen(true));
33305
+ };
33306
+ const handleEditOptions = (item) => {
33307
+ const availability = trajectNodeAvailability[item.line.guid];
33308
+ const result = availability?.results.find((x) => x.code === availability?.selectedCode);
33309
+ if (!result)
33310
+ return;
33311
+ dispatch(setPackagingAccoSearchDetails([result]));
33312
+ dispatch(setSelectedPackagingAccoResult(result.code));
33313
+ dispatch(setTrajectEditLineGuid(item.line.guid));
33314
+ dispatch(setFlyInType('acco-details'));
33315
+ dispatch(setFlyInIsOpen(true));
33183
33316
  };
33184
33317
  const renderServiceNode = (item) => {
33318
+ console.log('renderServiceNode', item);
33185
33319
  const { node, line } = item;
33186
33320
  const availability = trajectNodeAvailability[line.guid];
33187
- const isExpanded = !!expandedNodes[line.guid];
33188
33321
  const results = availability?.results ?? [];
33189
33322
  const selected = results.find((x) => x.code === availability?.selectedCode) ?? null;
33190
33323
  const configured = results.filter((x) => x.code !== selected?.code && !!findCandidateForResult(node, x));
@@ -33206,34 +33339,27 @@ const TrajectResults = ({ isLoading }) => {
33206
33339
  node.preferredAccommodationName,
33207
33340
  " \u2014 ",
33208
33341
  translations.SRP.NO_RESULTS))),
33209
- React__default.createElement("div", { className: "search__results__cards search__results__cards--extended" }, [selected, ...configured, ...(isExpanded ? others : [])].map((result) => (React__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 })))),
33342
+ React__default.createElement("div", { className: "search__results__cards search__results__cards--compact" }, [selected, ...configured].map((result) => (React__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 })))),
33210
33343
  others.length > 0 && (React__default.createElement("div", { className: "search__results__cards__actions" },
33211
- React__default.createElement("button", { type: "button", className: "cta cta--secondary", onClick: () => toggleExpanded(line.guid) }, isExpanded ? translations.SRP.SHOW_LESS : `${translations.SRP.SHOW_MORE} (${others.length})`)))))));
33344
+ React__default.createElement("button", { type: "button", className: "cta cta--secondary", onClick: () => handleShowMore(item) },
33345
+ translations.SRP.SHOW_MORE,
33346
+ " (",
33347
+ others.length,
33348
+ ")")))))));
33349
+ };
33350
+ const handleSelectFlight = (item, flight) => {
33351
+ dispatch(setTrajectFlightSelection({ lineGuid: item.line.guid, guid: flight.outwardGuid }));
33352
+ dispatch(updateEditableEntryLine(applyFlightToLine(item.line, flight)));
33212
33353
  };
33213
33354
  const renderFlightNode = (item) => {
33214
33355
  const { node, line } = item;
33215
- const isOutbound = flightPairing.outbound?.node.nodeId === node.nodeId;
33216
- const isInbound = flightPairing.inbound?.node.nodeId === node.nodeId;
33217
- // A middle leg of a multi-flight traject: the round-trip flight search cannot express it.
33218
- if (!isOutbound && !isInbound) {
33219
- return (React__default.createElement(React__default.Fragment, { key: line.guid },
33220
- React__default.createElement("div", { className: "search__results__label search__results__label--secondary" },
33221
- React__default.createElement("div", { className: "search__results__label__date" },
33222
- React__default.createElement("p", { className: "search__results__label__date-date" }, format$2(new Date(line.from), 'd', { locale })),
33223
- React__default.createElement("p", null, format$2(new Date(line.from), 'MMM', { locale }))),
33224
- React__default.createElement("div", { className: "search__results__label__text" },
33225
- React__default.createElement(Icon, { name: "ui-flight", height: 16 }),
33226
- React__default.createElement("h3", null,
33227
- React__default.createElement("strong", null, node.name)))),
33228
- React__default.createElement("div", { className: "no-results" },
33229
- node.departureAirportCode,
33230
- " \u2192 ",
33231
- node.arrivalAirportCode)));
33232
- }
33233
- const flights = isOutbound ? uniqueOutwardFlights : uniqueReturnFlights;
33234
- const selectedFlight = isOutbound ? selectedOutward : selectedReturn;
33235
- const selectedKey = isOutbound ? selectedOutwardKey : selectedReturnKey;
33236
- const visible = flights.filter((x) => getFlightKey(isOutbound ? x.outward.segments : x.return.segments) !== selectedKey);
33356
+ const availability = trajectFlightAvailability[line.guid];
33357
+ const status = availability?.status ?? 'loading';
33358
+ const flights = availability?.results ?? [];
33359
+ const selected = flights.find((x) => x.outwardGuid === availability?.selectedGuid) ?? null;
33360
+ const others = flights.filter((x) => x.outwardGuid !== availability?.selectedGuid);
33361
+ const isExpanded = !!expandedNodes[line.guid];
33362
+ const visible = isExpanded ? others : others.slice(0, 2);
33237
33363
  return (React__default.createElement(React__default.Fragment, { key: line.guid },
33238
33364
  React__default.createElement("div", { className: "search__results__label search__results__label--secondary" },
33239
33365
  React__default.createElement("div", { className: "search__results__label__date" },
@@ -33243,11 +33369,19 @@ const TrajectResults = ({ isLoading }) => {
33243
33369
  React__default.createElement(Icon, { name: "ui-flight", height: 16 }),
33244
33370
  React__default.createElement("h3", null,
33245
33371
  translations.SRP.SELECT,
33246
- " ",
33247
- React__default.createElement("strong", null, isOutbound ? translations.SRP.DEPARTURE : translations.SRP.RETURN)))),
33248
- flightsLoading || !trajectFlightsResolved ? (React__default.createElement(Spinner, { label: translations.SRP.LOADING_FLIGHTS })) : flights.length === 0 ? (React__default.createElement("div", { className: "no-results" }, translations.SRP.NO_RESULTS)) : (React__default.createElement("div", { className: "search__results__cards search__results__cards--extended" },
33249
- selectedFlight && (React__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 })),
33250
- visible.map((result) => (React__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))) })))))));
33372
+ ' ',
33373
+ React__default.createElement("strong", null,
33374
+ node.departureAirportCode,
33375
+ " - ",
33376
+ node.arrivalAirportCode)))),
33377
+ status === 'loading' && React__default.createElement(Spinner, { label: translations.SRP.LOADING_FLIGHTS }),
33378
+ status === 'unavailable' && React__default.createElement("div", { className: "no-results" }, translations.SRP.NO_RESULTS),
33379
+ status === 'resolved' && selected && (React__default.createElement(React__default.Fragment, null,
33380
+ React__default.createElement("div", { className: "search__results__cards search__results__cards--extended" },
33381
+ React__default.createElement(IndependentFlightOption, { key: `flight-${selected.outwardGuid}`, item: selected.outward, guid: selected.outwardGuid, selectedGuid: selected.outwardGuid, isOutward: true, showSelectedState: true, price: selected.price }),
33382
+ visible.map((result) => (React__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) })))),
33383
+ others.length > 2 && (React__default.createElement("div", { className: "search__results__cards__actions" },
33384
+ React__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})`)))))));
33251
33385
  };
33252
33386
  if (isLoading)
33253
33387
  return React__default.createElement(Spinner, { label: translations.SRP.LOADING_ITINERARY });
@@ -33256,6 +33390,8 @@ const TrajectResults = ({ isLoading }) => {
33256
33390
  if (!dayNodes.length)
33257
33391
  return null;
33258
33392
  return (React__default.createElement(React__default.Fragment, { key: `day-${day.order}` }, dayNodes.map((node) => {
33393
+ if (node.serviceType !== ACCOMMODATION_SERVICE_TYPE && node.serviceType !== EXCURSION_SERVICE_TYPE && node.serviceType !== FLIGHT_SERVICE_TYPE)
33394
+ return null;
33259
33395
  // if (node.trajectNodeType === CONNECTING_TRAJECT_NODE_TYPE) return null;
33260
33396
  // if (node.trajectNodeType !== REGULAR_TRAJECT_NODE_TYPE) return renderContextNode(node);
33261
33397
  if (node.trajectNodeType !== REGULAR_TRAJECT_NODE_TYPE)
@@ -33273,7 +33409,7 @@ const useTrajectAvailability = () => {
33273
33409
  const context = useContext(SearchResultsConfigurationContext);
33274
33410
  const dispatch = useDispatch();
33275
33411
  const trajectEntry = context?.trajectEntry;
33276
- const selectedCombinationFlight = useSelector(selectSelectedCombinationFlight);
33412
+ const showNonPreferredItems = !!context?.showNonPreferredItems;
33277
33413
  useEffect(() => {
33278
33414
  if (!context || !trajectEntry)
33279
33415
  return;
@@ -33293,6 +33429,13 @@ const useTrajectAvailability = () => {
33293
33429
  language: context.languageCode ?? 'en-GB',
33294
33430
  rooms: getPackagingRoomsFromEntry(trajectEntry.entry)
33295
33431
  };
33432
+ // Flight searches take passengers rather than rooms; the API buckets by age.
33433
+ const pax = trajectEntry.entry.pax ?? [];
33434
+ const ageOf = (p) => p.age ?? 30;
33435
+ const adults = pax.filter((p) => ageOf(p) >= 12).length;
33436
+ const kids = pax.filter((p) => ageOf(p) >= 2 && ageOf(p) < 12).length;
33437
+ const babies = pax.filter((p) => ageOf(p) < 2).length;
33438
+ const flightPax = 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 })));
33296
33439
  const nodesWithLines = getTrajectNodesWithLines(trajectEntry);
33297
33440
  const resolveNode = async (item) => {
33298
33441
  const lineGuid = item.line.guid;
@@ -33311,18 +33454,20 @@ const useTrajectAvailability = () => {
33311
33454
  return null;
33312
33455
  }
33313
33456
  }));
33314
- let results = attempts.filter((x) => !!x).filter((x, i, all) => all.findIndex((y) => y.code === x.code) === i);
33315
- let picked = results[0] ?? null;
33457
+ const configuredResults = attempts
33458
+ .filter((x) => !!x)
33459
+ .filter((x, i, all) => all.findIndex((y) => y.code === x.code) === i);
33316
33460
  const preferredCandidate = candidates.find((x) => x.isPreferred) ?? candidates[0];
33317
- let preferredUnavailable = !!preferredCandidate && !results.some((x) => matchesCode(x, preferredCandidate.code));
33318
- // Nothing configured was available — fall back to the cheapest result in the destination.
33319
- if (!picked) {
33320
- results = (await search(config, buildTrajectNodeRequest(item, searchContext, ''), controller.signal)) ?? [];
33321
- picked = pickCheapest(results);
33461
+ let preferredUnavailable = !!preferredCandidate && !configuredResults.some((x) => matchesCode(x, preferredCandidate.code));
33462
+ const destinationResults = showNonPreferredItems
33463
+ ? ((await search(config, buildTrajectNodeRequest(item, searchContext, ''), controller.signal)) ?? []).filter((x) => !configuredResults.some((configured) => configured.code === x.code))
33464
+ : [];
33465
+ const results = [...configuredResults, ...destinationResults];
33466
+ const picked = configuredResults[0] ?? pickCheapest(destinationResults);
33467
+ if (!configuredResults.length)
33322
33468
  preferredUnavailable = candidates.length > 0;
33323
- }
33324
33469
  if (controller.signal.aborted)
33325
- return null;
33470
+ return [];
33326
33471
  dispatch(setTrajectNodeAvailability({
33327
33472
  lineGuid,
33328
33473
  results,
@@ -33330,91 +33475,58 @@ const useTrajectAvailability = () => {
33330
33475
  preferredUnavailable
33331
33476
  }));
33332
33477
  if (!picked)
33333
- return lineGuid;
33334
- dispatch(updateEditableEntryLine(applyResultToLine(item.line, picked)));
33335
- return null;
33478
+ return item.lines.map((x) => x.guid);
33479
+ applyResultToLines(item.lines, picked).forEach((line) => dispatch(updateEditableEntryLine(line)));
33480
+ return [];
33336
33481
  }
33337
33482
  catch (error) {
33338
33483
  if (controller.signal.aborted)
33339
- return null;
33484
+ return [];
33340
33485
  console.error('Failed to resolve traject node availability', item.node.name, error);
33341
33486
  dispatch(setTrajectNodeAvailability({ lineGuid, results: [], selectedCode: null, preferredUnavailable: false }));
33342
- return lineGuid;
33487
+ return item.lines.map((x) => x.guid);
33343
33488
  }
33344
33489
  };
33345
- const resolveFlights = async () => {
33346
- const { outbound, inbound } = pairTrajectFlights(nodesWithLines);
33347
- if (!outbound) {
33348
- dispatch(setTrajectFlightsResolved(true));
33349
- return;
33490
+ const resolveFlightNode = async (item) => {
33491
+ const lineGuid = item.line.guid;
33492
+ if (!item.node.departureAirportCode || !item.node.arrivalAirportCode) {
33493
+ dispatch(setTrajectFlightAvailability({ lineGuid, results: [], selectedGuid: null }));
33494
+ return item.lines.map((x) => x.guid);
33350
33495
  }
33351
- dispatch(setFlightsLoading(true));
33496
+ dispatch(setTrajectFlightLoading(lineGuid));
33352
33497
  try {
33353
- const pax = trajectEntry.entry.pax ?? [];
33354
- const ageOf = (p) => p.age ?? 30;
33355
- const adults = pax.filter((p) => ageOf(p) >= 12).length;
33356
- const kids = pax.filter((p) => ageOf(p) >= 2 && ageOf(p) < 12).length;
33357
- const babies = pax.filter((p) => ageOf(p) < 2).length;
33358
- const request = {
33359
- transactionId: trajectEntry.entry.transactionId,
33360
- officeId: searchContext.officeId,
33361
- catalogueId: searchContext.catalogueId,
33362
- agentId: searchContext.agentId,
33363
- language: searchContext.language,
33364
- departureAirportCode: outbound.node.departureAirportCode ?? '',
33365
- arrivalAirportCode: outbound.node.arrivalAirportCode ?? '',
33366
- returnAirportCode: inbound?.node.arrivalAirportCode ?? outbound.node.departureAirportCode ?? null,
33367
- luggageIncluded: null,
33368
- maxStops: null,
33369
- travelClass: null,
33370
- pax: 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 }))),
33371
- outward: { date: dateToDateStruct(new Date(outbound.line.from)) },
33372
- return: { date: dateToDateStruct(new Date((inbound ?? outbound).line.from)) }
33373
- };
33374
- const flights = (await build.searchPackagingFlights(config, request, controller.signal)) ?? [];
33498
+ const flights = (await build.searchPackagingFlights(config, buildTrajectFlightRequest(item, searchContext, flightPax), controller.signal)) ?? [];
33375
33499
  if (controller.signal.aborted)
33376
- return;
33377
- dispatch(setPackagingFlightResults(flights));
33378
- const firstFlight = first(flights);
33379
- if (firstFlight) {
33380
- dispatch(setSelectedOutwardKey(getFlightKey(firstFlight.outward.segments)));
33381
- dispatch(setSelectedReturnKey(getFlightKey(firstFlight.return.segments)));
33382
- }
33500
+ return [];
33501
+ const picked = pickCheapestFlight(flights);
33502
+ dispatch(setTrajectFlightAvailability({ lineGuid, results: flights, selectedGuid: picked?.outwardGuid ?? null }));
33503
+ if (!picked)
33504
+ return item.lines.map((x) => x.guid);
33505
+ dispatch(updateEditableEntryLine(applyFlightToLine(item.line, picked)));
33506
+ return [];
33383
33507
  }
33384
33508
  catch (error) {
33385
- if (!controller.signal.aborted)
33386
- console.error('Failed to resolve traject flights', error);
33387
- }
33388
- finally {
33389
- if (!controller.signal.aborted) {
33390
- dispatch(setFlightsLoading(false));
33391
- dispatch(setTrajectFlightsResolved(true));
33392
- }
33509
+ if (controller.signal.aborted)
33510
+ return [];
33511
+ console.error('Failed to resolve traject flight', item.node.name, error);
33512
+ dispatch(setTrajectFlightAvailability({ lineGuid, results: [], selectedGuid: null }));
33513
+ return item.lines.map((x) => x.guid);
33393
33514
  }
33394
33515
  };
33395
33516
  (async () => {
33396
33517
  const searchable = nodesWithLines.filter((x) => isSearchableTrajectNode(x.node));
33397
- const [unresolved] = await Promise.all([Promise.all(searchable.map(resolveNode)), resolveFlights()]);
33518
+ const flightNodes = nodesWithLines.filter((x) => isTrajectFlightNode(x.node));
33519
+ const unresolved = (await Promise.all([...searchable.map(resolveNode), ...flightNodes.map(resolveFlightNode)])).flat();
33398
33520
  if (controller.signal.aborted)
33399
33521
  return;
33400
33522
  // A node with no availability anywhere cannot be priced or booked, so its line comes off
33401
33523
  // the entry. The card stays visible and reports the gap.
33402
- const emptyLineGuids = unresolved.filter((guid) => !!guid);
33524
+ const emptyLineGuids = unresolved.filter(Boolean);
33403
33525
  if (emptyLineGuids.length)
33404
33526
  dispatch(removeEditableEntryLines(emptyLineGuids));
33405
33527
  })();
33406
33528
  return () => controller.abort();
33407
- }, [trajectEntry]);
33408
- // Keep the traject's own flight lines in step with whatever outward/return the user picks.
33409
- useEffect(() => {
33410
- if (!trajectEntry || !selectedCombinationFlight)
33411
- return;
33412
- const { outbound, inbound } = pairTrajectFlights(getTrajectNodesWithLines(trajectEntry));
33413
- if (outbound)
33414
- dispatch(updateEditableEntryLine(applyFlightToLine(outbound.line, selectedCombinationFlight, true)));
33415
- if (inbound)
33416
- dispatch(updateEditableEntryLine(applyFlightToLine(inbound.line, selectedCombinationFlight, false)));
33417
- }, [trajectEntry, selectedCombinationFlight]);
33529
+ }, [trajectEntry, showNonPreferredItems]);
33418
33530
  };
33419
33531
 
33420
33532
  const SearchResultsContainer = ({ onBookingStarted }) => {
@@ -33422,7 +33534,7 @@ const SearchResultsContainer = ({ onBookingStarted }) => {
33422
33534
  const dispatch = useDispatch();
33423
33535
  const context = useContext(SearchResultsConfigurationContext);
33424
33536
  const translations = getTranslations(context?.languageCode ?? 'en-GB');
33425
- const { results, filteredResults, packagingAccoResults, filteredPackagingAccoResults, isLoading, flightsLoading, initialFilters, filters, flightFilters, selectedSortType, selectedFlightSortType, selectedSearchResult, selectedPackagingAccoResultCode, flyInIsOpen, packagingAccoSearchDetails, editablePackagingEntry, transactionId, flyInType, packagingFlightResults, confirmedExcursionsByDay, bookPackagingEntry } = useSelector((state) => state.searchResults);
33537
+ const { results, filteredResults, packagingAccoResults, filteredPackagingAccoResults, isLoading, flightsLoading, initialFilters, filters, flightFilters, selectedSortType, selectedFlightSortType, selectedSearchResult, selectedPackagingAccoResultCode, flyInIsOpen, packagingAccoSearchDetails, editablePackagingEntry, transactionId, flyInType, packagingFlightResults, confirmedExcursionsByDay, bookPackagingEntry, trajectEditLineGuid } = useSelector((state) => state.searchResults);
33426
33538
  const isMobile = useMediaQuery('(max-width: 1200px)');
33427
33539
  // Resolves live availability per traject node; no-op unless a traject entry is configured.
33428
33540
  useTrajectAvailability();
@@ -33711,6 +33823,20 @@ const SearchResultsContainer = ({ onBookingStarted }) => {
33711
33823
  setDetailsIsLoading(false);
33712
33824
  };
33713
33825
  const handleConfirmHotelSwap = () => {
33826
+ if (context?.trajectEntry && trajectEditLineGuid) {
33827
+ const item = getTrajectNodesWithLines(context.trajectEntry).find((x) => x.line.guid === trajectEditLineGuid);
33828
+ const result = packagingAccoSearchDetails.find((x) => x.code === selectedPackagingAccoResultCode);
33829
+ if (item && result) {
33830
+ const currentLines = item.lines
33831
+ .map((line) => editablePackagingEntry?.lines?.find((x) => x.guid === line.guid))
33832
+ .filter((x) => !!x);
33833
+ applyResultToLines(currentLines, result).forEach((line) => dispatch(updateEditableEntryLine(line)));
33834
+ dispatch(updateTrajectNodeResult({ lineGuid: trajectEditLineGuid, result }));
33835
+ }
33836
+ dispatch(setTrajectEditLineGuid(null));
33837
+ handleFlyInToggle(false);
33838
+ return;
33839
+ }
33714
33840
  const updatedEntry = swapHotelInPackagingEntry();
33715
33841
  if (!updatedEntry)
33716
33842
  return;
@@ -34059,6 +34185,10 @@ const SearchResultsContainer = ({ onBookingStarted }) => {
34059
34185
  const fetchPackagingAccoSearchDetails = async () => {
34060
34186
  if (!selectedPackagingAccoResultCode || !context)
34061
34187
  return;
34188
+ // A traject node already resolved its own availability, options per room included, and hands
34189
+ // that result straight to the fly-in.
34190
+ if (context.trajectEntry)
34191
+ return;
34062
34192
  if (skipInitialPackagingAccoDetailsRef.current) {
34063
34193
  skipInitialPackagingAccoDetailsRef.current = false;
34064
34194
  return;
@@ -37573,20 +37703,20 @@ function ItineraryMapView({ destinations: propDestinations, adults = 2, dateFrom
37573
37703
  const isStartEnd = dest.isStart || dest.isEnd;
37574
37704
  let iconHtml;
37575
37705
  if (isStartEnd) {
37576
- iconHtml = `
37577
- <div class="map-view__marker map-view__marker--plane">
37578
- <svg viewBox="0 0 576 512" fill="currentColor" width="16" height="16" xmlns="http://www.w3.org/2000/svg">
37579
- <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"/>
37580
- </svg>
37706
+ iconHtml = `
37707
+ <div class="map-view__marker map-view__marker--plane">
37708
+ <svg viewBox="0 0 576 512" fill="currentColor" width="16" height="16" xmlns="http://www.w3.org/2000/svg">
37709
+ <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"/>
37710
+ </svg>
37581
37711
  </div>`;
37582
37712
  }
37583
37713
  else {
37584
37714
  stopIndex++;
37585
37715
  const imgStyle = dest.imageUrl ? `style="background-image:url('${dest.imageUrl}')"` : '';
37586
- iconHtml = `
37587
- <div class="map-view__marker map-view__marker--stop" ${imgStyle}>
37588
- ${!dest.imageUrl ? `<span class="map-view__marker-number">${stopIndex}</span>` : ''}
37589
- <span class="map-view__marker-badge">${stopIndex}</span>
37716
+ iconHtml = `
37717
+ <div class="map-view__marker map-view__marker--stop" ${imgStyle}>
37718
+ ${!dest.imageUrl ? `<span class="map-view__marker-number">${stopIndex}</span>` : ''}
37719
+ <span class="map-view__marker-badge">${stopIndex}</span>
37590
37720
  </div>`;
37591
37721
  }
37592
37722
  const icon = L.divIcon({