@qite/tide-booking-component 1.4.109 → 1.4.111

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.
Files changed (106) hide show
  1. package/build/build-cjs/index.js +3613 -2276
  2. package/build/build-cjs/src/booking-wizard/components/step-route.d.ts +2 -2
  3. package/build/build-cjs/src/booking-wizard/features/sidebar/sidebar-flight.d.ts +1 -0
  4. package/build/build-cjs/src/booking-wizard/features/sidebar/sidebar-util.d.ts +2 -1
  5. package/build/build-cjs/src/booking-wizard/features/sidebar/sidebar.d.ts +0 -31
  6. package/build/build-cjs/src/booking-wizard/features/travelers-form/travelers-form.d.ts +1 -2
  7. package/build/build-cjs/src/search-results/components/book-packaging-entry/index.d.ts +8 -0
  8. package/build/build-cjs/src/search-results/components/book-packaging-entry/wl-sidebar.d.ts +7 -0
  9. package/build/build-cjs/src/search-results/components/spinner/spinner.d.ts +4 -1
  10. package/build/build-cjs/src/search-results/store/search-results-slice.d.ts +7 -1
  11. package/build/build-cjs/src/search-results/types.d.ts +3 -0
  12. package/build/build-cjs/src/shared/booking/booking-panel.d.ts +13 -0
  13. package/build/build-cjs/src/shared/booking/shared-confirmation.d.ts +25 -0
  14. package/build/build-cjs/src/shared/booking/shared-sidebar.d.ts +34 -0
  15. package/build/build-cjs/src/shared/booking/step-indicators.d.ts +7 -0
  16. package/build/build-cjs/src/shared/booking/summary.d.ts +43 -0
  17. package/build/build-cjs/src/shared/booking/travelers-form.d.ts +93 -0
  18. package/build/build-cjs/src/shared/components/flyin/flyin.d.ts +2 -0
  19. package/build/build-cjs/src/shared/components/flyin/packaging-flights-flyin.d.ts +2 -0
  20. package/build/build-cjs/src/shared/utils/booking-summary.d.ts +1 -0
  21. package/build/build-cjs/src/shared/utils/localization-util.d.ts +7 -0
  22. package/build/build-esm/index.js +3572 -2247
  23. package/build/build-esm/src/booking-wizard/components/step-route.d.ts +2 -2
  24. package/build/build-esm/src/booking-wizard/features/sidebar/sidebar-flight.d.ts +1 -0
  25. package/build/build-esm/src/booking-wizard/features/sidebar/sidebar-util.d.ts +2 -1
  26. package/build/build-esm/src/booking-wizard/features/sidebar/sidebar.d.ts +0 -31
  27. package/build/build-esm/src/booking-wizard/features/travelers-form/travelers-form.d.ts +1 -2
  28. package/build/build-esm/src/search-results/components/book-packaging-entry/index.d.ts +8 -0
  29. package/build/build-esm/src/search-results/components/book-packaging-entry/wl-sidebar.d.ts +7 -0
  30. package/build/build-esm/src/search-results/components/spinner/spinner.d.ts +4 -1
  31. package/build/build-esm/src/search-results/store/search-results-slice.d.ts +7 -1
  32. package/build/build-esm/src/search-results/types.d.ts +3 -0
  33. package/build/build-esm/src/shared/booking/booking-panel.d.ts +13 -0
  34. package/build/build-esm/src/shared/booking/shared-confirmation.d.ts +25 -0
  35. package/build/build-esm/src/shared/booking/shared-sidebar.d.ts +34 -0
  36. package/build/build-esm/src/shared/booking/step-indicators.d.ts +7 -0
  37. package/build/build-esm/src/shared/booking/summary.d.ts +43 -0
  38. package/build/build-esm/src/shared/booking/travelers-form.d.ts +93 -0
  39. package/build/build-esm/src/shared/components/flyin/flyin.d.ts +2 -0
  40. package/build/build-esm/src/shared/components/flyin/packaging-flights-flyin.d.ts +2 -0
  41. package/build/build-esm/src/shared/utils/booking-summary.d.ts +1 -0
  42. package/build/build-esm/src/shared/utils/localization-util.d.ts +7 -0
  43. package/package.json +2 -2
  44. package/src/booking-wizard/components/step-indicator.tsx +10 -31
  45. package/src/booking-wizard/components/step-route.tsx +39 -14
  46. package/src/booking-wizard/features/confirmation/confirmation.tsx +11 -55
  47. package/src/booking-wizard/features/sidebar/index.tsx +10 -4
  48. package/src/booking-wizard/features/sidebar/sidebar-flight.tsx +2 -2
  49. package/src/booking-wizard/features/sidebar/sidebar-util.ts +1 -5
  50. package/src/booking-wizard/features/sidebar/sidebar.tsx +331 -326
  51. package/src/booking-wizard/features/summary/summary.tsx +1 -1
  52. package/src/booking-wizard/features/travelers-form/travelers-form.tsx +84 -1010
  53. package/src/search-results/components/book-packaging-entry/index.tsx +229 -0
  54. package/src/search-results/components/book-packaging-entry/wl-sidebar.tsx +162 -0
  55. package/src/search-results/components/excursions/day-by-day-excursions.tsx +6 -2
  56. package/src/search-results/components/excursions/excursion-results.tsx +1 -1
  57. package/src/search-results/components/flight/flight-selection/independent-flight-selection.tsx +12 -3
  58. package/src/search-results/components/group-tour/group-tour-card.tsx +1 -1
  59. package/src/search-results/components/group-tour/group-tour-results.tsx +1 -1
  60. package/src/search-results/components/hotel/hotel-accommodation-results.tsx +6 -3
  61. package/src/search-results/components/itinerary/full-itinerary.tsx +1 -1
  62. package/src/search-results/components/itinerary/index.tsx +13 -12
  63. package/src/search-results/components/search-results-container/flight-search-results.tsx +1 -1
  64. package/src/search-results/components/search-results-container/search-results-container.tsx +280 -217
  65. package/src/search-results/components/spinner/spinner.tsx +12 -4
  66. package/src/search-results/store/search-results-slice.ts +22 -2
  67. package/src/search-results/types.ts +4 -0
  68. package/src/shared/booking/booking-panel.tsx +25 -0
  69. package/src/shared/booking/shared-confirmation.tsx +105 -0
  70. package/src/shared/booking/shared-sidebar.tsx +432 -0
  71. package/src/shared/booking/step-indicators.tsx +30 -0
  72. package/src/shared/booking/summary.tsx +380 -0
  73. package/src/shared/booking/travelers-form.tsx +870 -0
  74. package/src/shared/components/flyin/accommodation-flyin.tsx +3 -4
  75. package/src/shared/components/flyin/flights-flyin.tsx +1 -1
  76. package/src/shared/components/flyin/flyin.tsx +16 -9
  77. package/src/shared/components/flyin/group-tour-flyin.tsx +3 -4
  78. package/src/shared/components/flyin/packaging-flights-flyin.tsx +11 -4
  79. package/src/shared/components/icon.tsx +13 -0
  80. package/src/shared/translations/ar-SA.json +7 -1
  81. package/src/shared/translations/da-DK.json +7 -1
  82. package/src/shared/translations/de-DE.json +7 -1
  83. package/src/shared/translations/en-GB.json +8 -2
  84. package/src/shared/translations/es-ES.json +7 -1
  85. package/src/shared/translations/fr-BE.json +7 -1
  86. package/src/shared/translations/fr-FR.json +7 -1
  87. package/src/shared/translations/is-IS.json +7 -1
  88. package/src/shared/translations/it-IT.json +7 -1
  89. package/src/shared/translations/ja-JP.json +7 -1
  90. package/src/shared/translations/nl-BE.json +7 -1
  91. package/src/shared/translations/nl-NL.json +7 -1
  92. package/src/shared/translations/no-NO.json +7 -1
  93. package/src/shared/translations/pl-PL.json +7 -1
  94. package/src/shared/translations/pt-PT.json +7 -1
  95. package/src/shared/translations/sv-SE.json +7 -1
  96. package/src/shared/utils/booking-summary.tsx +46 -0
  97. package/src/shared/utils/localization-util.ts +8 -0
  98. package/src/shared/utils/tide-api-utils.ts +2 -2
  99. package/styles/components/_dropdown.scss +5 -0
  100. package/styles/components/_flyin.scss +43 -0
  101. package/styles/components/_loader.scss +82 -0
  102. package/styles/components/_search.scss +14 -2
  103. package/styles/content-blocks-variables.scss +14 -14
  104. /package/build/build-cjs/src/{booking-wizard/components → shared/booking}/product-card.d.ts +0 -0
  105. /package/build/build-esm/src/{booking-wizard/components → shared/booking}/product-card.d.ts +0 -0
  106. /package/src/{booking-wizard/components → shared/booking}/product-card.tsx +0 -0
@@ -29,7 +29,8 @@ import {
29
29
  setInitialFlightFilters,
30
30
  resetFlightFilters,
31
31
  setFilters,
32
- setInitialFilters
32
+ setInitialFilters,
33
+ setBookPackagingEntry
33
34
  } from '../../store/search-results-slice';
34
35
  import { FlyInType, SearchSeed, SortByType } from '../../types';
35
36
  import useMediaQuery from '../../../shared/utils/use-media-query-util';
@@ -58,7 +59,10 @@ import {
58
59
  PackagingFlightResponse,
59
60
  PackagingAccommodationResponse,
60
61
  FlightSearchResponseFlightSegment,
61
- PackagingEntryLineFlightLine
62
+ PackagingEntryLineFlightLine,
63
+ PackagingEntryAddress,
64
+ PackagingEntryPax,
65
+ PackagingEntryRoom
62
66
  } from '@qite/tide-client';
63
67
  import { getDateFromParams, getNumberFromParams, getRoomsFromParams, getStringFromParams } from '../../../shared/utils/query-string-util';
64
68
  import { concat, first, isEmpty, last, range } from 'lodash';
@@ -99,7 +103,6 @@ import FullItinerary from '../itinerary/full-itinerary';
99
103
  import { getFlightKey } from '../../utils/flight-utils';
100
104
  import IndependentFlightOption from '../flight/flight-selection/independent-flight-option';
101
105
  import { Spinner } from '../../..';
102
- import { PackagingRequestBase } from '@qite/tide-client/build/types/booking-v2/request/packaging/packaging-request-base';
103
106
  import {
104
107
  selectSelectedCombinationFlight,
105
108
  selectSelectedOutward,
@@ -110,6 +113,11 @@ import {
110
113
  selectUniqueReturnFlights
111
114
  } from '../../store/search-results-selectors';
112
115
  import DayByDayExcursions from '../excursions/day-by-day-excursions';
116
+ import BookPackagingEntry from '../book-packaging-entry';
117
+ import { format } from 'date-fns';
118
+
119
+ // TODO; fix import
120
+ import { PackagingRequestBase } from '@qite/tide-client/build/types/booking-v2/request/packaging/packaging-request-base';
113
121
 
114
122
  type BuildPackagingEntryPartialArgs = {
115
123
  sourceEntry: PackagingEntry | null | undefined;
@@ -149,9 +157,9 @@ const SearchResultsContainer: React.FC = () => {
149
157
  editablePackagingEntry,
150
158
  transactionId,
151
159
  flyInType,
152
- itinerary,
153
160
  packagingFlightResults,
154
- confirmedExcursionsByDay
161
+ confirmedExcursionsByDay,
162
+ bookPackagingEntry
155
163
  } = useSelector((state: SearchResultsRootState) => state.searchResults);
156
164
 
157
165
  const isMobile = useMediaQuery('(max-width: 1200px)');
@@ -165,6 +173,7 @@ const SearchResultsContainer: React.FC = () => {
165
173
  const [itineraryIsLoading, setItineraryIsLoading] = useState(false);
166
174
 
167
175
  const [itineraryOpen, setItineraryOpen] = useState(false);
176
+ const [isBookingConfirmation, setIsBookingConfirmation] = useState(false);
168
177
 
169
178
  const [selectedAccommodationSeed, setSelectedAccommodationSeed] = useState<SearchSeed | null>(null);
170
179
 
@@ -470,7 +479,6 @@ const SearchResultsContainer: React.FC = () => {
470
479
  const handleConfirmHotelSwap = () => {
471
480
  const updatedEntry = swapHotelInPackagingEntry();
472
481
  if (!updatedEntry) return;
473
-
474
482
  dispatch(setEditablePackagingEntry(updatedEntry));
475
483
  handleFlyInToggle(false);
476
484
  };
@@ -795,6 +803,14 @@ const SearchResultsContainer: React.FC = () => {
795
803
  if (context?.packagingEntry) {
796
804
  dispatch(setEditablePackagingEntry(structuredClone(context.packagingEntry)));
797
805
  dispatch(setTransactionId(context.packagingEntry.transactionId));
806
+
807
+ const params = new URLSearchParams(location.search);
808
+ const bookingConfirmation = getStringFromParams(params, 'bookingConfirmation');
809
+ console.log('bookingConfirmation', bookingConfirmation);
810
+ if (bookingConfirmation == 'true') {
811
+ setIsBookingConfirmation(true);
812
+ dispatch(setBookPackagingEntry(true));
813
+ }
798
814
  }
799
815
  }, [context?.packagingEntry]);
800
816
 
@@ -1109,7 +1125,6 @@ const SearchResultsContainer: React.FC = () => {
1109
1125
  });
1110
1126
 
1111
1127
  if (!nextEntry) return;
1112
-
1113
1128
  dispatch(setEditablePackagingEntry(nextEntry));
1114
1129
 
1115
1130
  if (selectedCombinationFlight) {
@@ -1416,28 +1431,43 @@ const SearchResultsContainer: React.FC = () => {
1416
1431
  }
1417
1432
 
1418
1433
  let paxId = 0;
1434
+ const pax: PackagingEntryPax[] = [];
1435
+ const rooms: PackagingEntryRoom[] = [];
1419
1436
 
1420
- const pax =
1421
- seed.rooms?.flatMap((room, roomIndex) =>
1422
- room.pax.map((_, paxIndex) => ({
1423
- id: paxId++,
1437
+ seed.rooms?.forEach((room, roomIndex) => {
1438
+ const paxIds = room.pax.map((_, paxIndex) => {
1439
+ const id = paxId++;
1440
+
1441
+ pax.push({
1442
+ id,
1424
1443
  firstName: '',
1425
1444
  lastName: '',
1426
1445
  dateOfBirth: null,
1427
1446
  isMainBooker: roomIndex === 0 && paxIndex === 0
1428
- }))
1429
- ) ?? [];
1447
+ });
1448
+
1449
+ return id;
1450
+ });
1451
+
1452
+ rooms.push({
1453
+ id: roomIndex,
1454
+ paxIds
1455
+ });
1456
+ });
1430
1457
 
1431
1458
  return {
1432
1459
  language,
1433
1460
  transactionId,
1434
1461
  dossierNumber: '',
1435
- status: 0,
1462
+ status: context?.entryStatus,
1463
+ customStatusId: context?.customEntryStatusId,
1436
1464
  bookingDate: null,
1437
1465
  price: 0,
1438
1466
  depositAmount: 0,
1439
1467
  pax,
1440
- lines: []
1468
+ rooms,
1469
+ lines: [],
1470
+ address: {} as PackagingEntryAddress
1441
1471
  } as PackagingEntry;
1442
1472
  };
1443
1473
 
@@ -1450,91 +1480,67 @@ const SearchResultsContainer: React.FC = () => {
1450
1480
  <div id="tide-booking" className="search__bg">
1451
1481
  {context && (
1452
1482
  <div className="search">
1453
- <div className="search__container">
1454
- {context.searchConfiguration.qsmType === PortalQsmType.Flight && (
1455
- <FlightSearchProvider tideConnection={context.tideConnection}>
1456
- <FlightResultsContainer isMobile={isMobile} />
1457
- <FlyIn
1458
- srpType={context.searchConfiguration.qsmType}
1459
- isOpen={flyInIsOpen}
1460
- setIsOpen={handleFlyInToggle}
1461
- onPanelRef={(el) => (panelRef.current = el)}
1462
- detailsLoading={detailsIsLoading}
1463
- />
1464
- </FlightSearchProvider>
1465
- )}
1466
- {(context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight ||
1467
- context.searchConfiguration.qsmType === PortalQsmType.Accommodation ||
1468
- context.searchConfiguration.qsmType === PortalQsmType.GroupTour ||
1469
- context.searchConfiguration.qsmType === PortalQsmType.RoundTrip) && (
1470
- <>
1471
- {context.searchConfiguration.qsmType != PortalQsmType.AccommodationAndFlight && context.showFilters && (
1472
- <Filters
1473
- initialFilters={initialFilters}
1474
- filters={filters}
1475
- isOpen={filtersOpen}
1476
- handleSetIsOpen={() => setFiltersOpen(!filtersOpen)}
1477
- // handleApplyFilters={() => setSearchTrigger((prev) => prev + 1)}
1478
- isLoading={isLoading}
1479
- setFilters={(filters) => dispatch(setFilters(filters))}
1480
- resetFilters={(filters) => dispatch(resetFilters(filters))}
1483
+ {bookPackagingEntry ? (
1484
+ <BookPackagingEntry activeSearchSeed={activeSearchSeed} isConfirmationPage={isBookingConfirmation} />
1485
+ ) : (
1486
+ <div className="search__container">
1487
+ {context.searchConfiguration.qsmType === PortalQsmType.Flight && (
1488
+ <FlightSearchProvider tideConnection={context.tideConnection}>
1489
+ <FlightResultsContainer isMobile={isMobile} />
1490
+ <FlyIn
1491
+ srpType={context.searchConfiguration.qsmType}
1492
+ isOpen={flyInIsOpen}
1493
+ setIsOpen={handleFlyInToggle}
1494
+ onPanelRef={(el) => (panelRef.current = el)}
1495
+ detailsLoading={detailsIsLoading}
1496
+ filtersOpen={filtersOpen}
1481
1497
  />
1482
- )}
1483
- {context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight && (
1484
- <Itinerary
1485
- isOpen={itineraryOpen}
1486
- handleSetIsOpen={() => setItineraryOpen(!itineraryOpen)}
1487
- isLoading={pricesAreLoading}
1488
- onEditAccommodation={handleEditAccommodation}
1489
- />
1490
- )}
1491
- {/* ---------------- Results ---------------- */}
1492
- <div className="search__results">
1493
- {isMobile && (
1494
- <div className="search__result-row">
1495
- <div className="search__results__actions">
1496
- {context.searchConfiguration.qsmType != PortalQsmType.AccommodationAndFlight && context.showFilters && (
1497
- <div className="cta cta--filter" onClick={() => setFiltersOpen(true)}>
1498
- <Icon name="ui-filter" className="mobile-filters-button__icon" height={16} />
1499
- {translations.SRP.FILTERS}
1500
- </div>
1501
- )}
1502
- {context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight && (
1503
- <div className="cta cta--filter" onClick={() => setItineraryOpen(true)}>
1504
- <Icon name="ui-calendar" className="mobile-filters-button__icon" height={16} />
1505
- {translations.SRP.SHOW_ITINERARY}
1506
- </div>
1507
- )}
1508
- </div>
1509
- {sortByTypes && sortByTypes.length > 0 && (
1510
- <ItemPicker
1511
- items={sortByTypes}
1512
- selection={selectedSortType?.label || undefined}
1513
- selectedSortByType={selectedSortType}
1514
- label={translations.SRP.SORTBY}
1515
- placeholder={translations.SRP.SORTBY}
1516
- classModifier="travel-class-picker__items"
1517
- valueFormatter={(value, direction) => getSortingName(translations, findSortByType(sortByTypes, value, direction ?? 'asc'))}
1518
- onPick={(newSortKey, direction) => handleSortChange(newSortKey, direction)}
1519
- />
1520
- )}
1521
- </div>
1498
+ </FlightSearchProvider>
1499
+ )}
1500
+ {(context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight ||
1501
+ context.searchConfiguration.qsmType === PortalQsmType.Accommodation ||
1502
+ context.searchConfiguration.qsmType === PortalQsmType.GroupTour ||
1503
+ context.searchConfiguration.qsmType === PortalQsmType.RoundTrip) && (
1504
+ <>
1505
+ {context.searchConfiguration.qsmType != PortalQsmType.AccommodationAndFlight && context.showFilters && (
1506
+ <Filters
1507
+ initialFilters={initialFilters}
1508
+ filters={filters}
1509
+ isOpen={filtersOpen}
1510
+ handleSetIsOpen={() => setFiltersOpen(!filtersOpen)}
1511
+ // handleApplyFilters={() => setSearchTrigger((prev) => prev + 1)}
1512
+ isLoading={isLoading}
1513
+ setFilters={(filters) => dispatch(setFilters(filters))}
1514
+ resetFilters={(filters) => dispatch(resetFilters(filters))}
1515
+ />
1522
1516
  )}
1523
- {context.searchConfiguration.qsmType !== PortalQsmType.AccommodationAndFlight && (
1524
- <div className="search__result-row">
1525
- <span className="search__result-row-text">
1526
- {!isLoading && (
1527
- <>
1528
- {context.searchConfiguration.qsmType === PortalQsmType.Accommodation &&
1529
- filteredPackagingAccoResults?.length &&
1530
- filteredPackagingAccoResults?.length}
1531
- {context.searchConfiguration.qsmType !== PortalQsmType.Accommodation && filteredResults?.length && filteredResults.length}
1532
- &nbsp;{translations.SRP.TOTAL_RESULTS_LABEL}
1533
- </>
1534
- )}
1535
- </span>
1536
- {!context.packagingEntry && !isMobile && sortByTypes && sortByTypes.length > 0 && (
1537
- <div className="search__result-row-filter">
1517
+ {context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight && (
1518
+ <Itinerary
1519
+ isOpen={itineraryOpen}
1520
+ handleSetIsOpen={() => setItineraryOpen(!itineraryOpen)}
1521
+ isLoading={isLoading || pricesAreLoading || flightsLoading || itineraryIsLoading}
1522
+ onEditAccommodation={handleEditAccommodation}
1523
+ />
1524
+ )}
1525
+ {/* ---------------- Results ---------------- */}
1526
+ <div className="search__results">
1527
+ {isMobile && (
1528
+ <div className="search__result-row">
1529
+ <div className="search__results__actions">
1530
+ {context.searchConfiguration.qsmType != PortalQsmType.AccommodationAndFlight && context.showFilters && (
1531
+ <div className="cta cta--filter" onClick={() => setFiltersOpen(true)}>
1532
+ <Icon name="ui-filter" className="mobile-filters-button__icon" height={16} />
1533
+ {translations.SRP.FILTERS}
1534
+ </div>
1535
+ )}
1536
+ {context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight && (
1537
+ <div className="cta cta--filter" onClick={() => setItineraryOpen(true)}>
1538
+ <Icon name="ui-shopping-cart" className="mobile-filters-button__icon" height={16} />
1539
+ {translations.SRP.VIEW_BOOKING}
1540
+ </div>
1541
+ )}
1542
+ </div>
1543
+ {sortByTypes && sortByTypes.length > 0 && (
1538
1544
  <ItemPicker
1539
1545
  items={sortByTypes}
1540
1546
  selection={selectedSortType?.label || undefined}
@@ -1545,139 +1551,196 @@ const SearchResultsContainer: React.FC = () => {
1545
1551
  valueFormatter={(value, direction) => getSortingName(translations, findSortByType(sortByTypes, value, direction ?? 'asc'))}
1546
1552
  onPick={(newSortKey, direction) => handleSortChange(newSortKey, direction)}
1547
1553
  />
1548
- </div>
1549
- )}
1550
- </div>
1551
- )}
1554
+ )}
1555
+ </div>
1556
+ )}
1557
+ {context.searchConfiguration.qsmType !== PortalQsmType.AccommodationAndFlight && (
1558
+ <div className="search__result-row">
1559
+ <span className="search__result-row-text">
1560
+ {!isLoading && (
1561
+ <>
1562
+ {context.searchConfiguration.qsmType === PortalQsmType.Accommodation &&
1563
+ filteredPackagingAccoResults?.length &&
1564
+ filteredPackagingAccoResults?.length}
1565
+ {context.searchConfiguration.qsmType !== PortalQsmType.Accommodation && filteredResults?.length && filteredResults.length}
1566
+ &nbsp;{translations.SRP.TOTAL_RESULTS_LABEL}
1567
+ </>
1568
+ )}
1569
+ </span>
1570
+ {!context.packagingEntry && !isMobile && sortByTypes && sortByTypes.length > 0 && (
1571
+ <div className="search__result-row-filter">
1572
+ <ItemPicker
1573
+ items={sortByTypes}
1574
+ selection={selectedSortType?.label || undefined}
1575
+ selectedSortByType={selectedSortType}
1576
+ label={translations.SRP.SORTBY}
1577
+ placeholder={translations.SRP.SORTBY}
1578
+ classModifier="travel-class-picker__items"
1579
+ valueFormatter={(value, direction) => getSortingName(translations, findSortByType(sortByTypes, value, direction ?? 'asc'))}
1580
+ onPick={(newSortKey, direction) => handleSortChange(newSortKey, direction)}
1581
+ />
1582
+ </div>
1583
+ )}
1584
+ </div>
1585
+ )}
1552
1586
 
1553
- <div className="search__results__wrapper">
1554
- {context.showTabViews &&
1555
- (context.searchConfiguration.qsmType === PortalQsmType.GroupTour ||
1556
- context.searchConfiguration.qsmType === PortalQsmType.Accommodation) && <TabViews />}
1587
+ <div className="search__results__wrapper">
1588
+ {context.showTabViews &&
1589
+ (context.searchConfiguration.qsmType === PortalQsmType.GroupTour ||
1590
+ context.searchConfiguration.qsmType === PortalQsmType.Accommodation) && <TabViews />}
1557
1591
 
1558
- {context.showRoundTripResults && context.showMockup && <RoundTripResults />}
1592
+ {context.showRoundTripResults && context.showMockup && <RoundTripResults />}
1559
1593
 
1560
- {context.searchConfiguration.qsmType === PortalQsmType.GroupTour && <GroupTourResults isLoading={isLoading} />}
1594
+ {context.searchConfiguration.qsmType === PortalQsmType.GroupTour && <GroupTourResults isLoading={isLoading} />}
1561
1595
 
1562
- {context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight && !context.packagingEntry && context.showFlightResults && (
1563
- <>
1564
- <div className="search__results__label search__results__label--secondary">
1565
- <div className="search__results__label__date">
1566
- <Icon name="ui-flight" height={16} fill="white" />
1567
- </div>
1568
- <div className="search__results__label__text">
1569
- <h3>
1570
- {translations.SRP.SELECT} <strong> {translations.SRP.DEPARTURE}</strong>
1571
- </h3>
1572
- </div>
1573
- </div>
1596
+ {context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight && !context.packagingEntry && context.showFlightResults && (
1597
+ <>
1598
+ <div className="search__results__label search__results__label--secondary">
1599
+ <div className="search__results__label__date">
1600
+ {(() => {
1601
+ const firstResultDate = uniqueOutwardFlights.length > 0 ? uniqueOutwardFlights[0].outward.segments[0].departureDateTime : null;
1574
1602
 
1575
- {flightsLoading ? (
1576
- <Spinner />
1577
- ) : (
1578
- <>
1579
- <div className="search__results__cards search__results__cards--extended">
1580
- {selectedOutwardKey && selectedOutward && (
1581
- <IndependentFlightOption
1582
- key={`flight-${selectedOutwardKey}`}
1583
- item={selectedOutward.outward}
1584
- guid={selectedOutward.outwardGuid}
1585
- onSelect={() => dispatch(setSelectedOutwardKey(null))}
1586
- selectedGuid={selectedOutward.outwardGuid}
1587
- isOutward={true}
1588
- showSelectedState={true}
1589
- price={selectedOutward.price}
1590
- />
1591
- )}
1592
- {visibleOutwardFlights.map((result) => (
1593
- <IndependentFlightOption
1594
- key={`flight-${result.outwardGuid}`}
1595
- item={result.outward}
1596
- onSelect={() => dispatch(setSelectedOutwardKey(getFlightKey(result.outward.segments)))}
1597
- guid={result.outwardGuid}
1598
- isOutward={true}
1599
- price={result.price}
1600
- currentSelectedPrice={selectedOutward?.price}
1601
- />
1602
- ))}
1603
+ const firstResultDay = firstResultDate ? format(firstResultDate, 'd') : null;
1604
+ const firstResultMonth = firstResultDate ? format(firstResultDate, 'MMM') : null;
1605
+
1606
+ return (
1607
+ <>
1608
+ <p className="search__results__label__date-date">{firstResultDay}</p>
1609
+ <p>{firstResultMonth}</p>
1610
+ </>
1611
+ );
1612
+ })()}
1603
1613
  </div>
1604
- {uniqueOutwardFlights && uniqueOutwardFlights.length > 3 && (
1605
- <div className="search__results__cards__actions">
1606
- <button className="cta cta--secondary" onClick={() => handleShowMoreFlights('flight-outward-results')}>
1607
- {translations.SRP.SHOW_MORE}
1608
- </button>
1614
+ <div className="search__results__label__text">
1615
+ <Icon name="ui-flight" height={16} />
1616
+ <h3>
1617
+ {translations.SRP.SELECT} <strong> {translations.SRP.DEPARTURE}</strong>
1618
+ </h3>
1619
+ </div>
1620
+ </div>
1621
+
1622
+ {flightsLoading ? (
1623
+ <Spinner label={translations.SRP.LOADING_FLIGHTS} />
1624
+ ) : (
1625
+ <>
1626
+ <div className="search__results__cards search__results__cards--extended">
1627
+ {selectedOutwardKey && selectedOutward && (
1628
+ <IndependentFlightOption
1629
+ key={`flight-${selectedOutwardKey}`}
1630
+ item={selectedOutward.outward}
1631
+ guid={selectedOutward.outwardGuid}
1632
+ onSelect={() => dispatch(setSelectedOutwardKey(null))}
1633
+ selectedGuid={selectedOutward.outwardGuid}
1634
+ isOutward={true}
1635
+ showSelectedState={true}
1636
+ price={selectedOutward.price}
1637
+ />
1638
+ )}
1639
+ {visibleOutwardFlights.map((result) => (
1640
+ <IndependentFlightOption
1641
+ key={`flight-${result.outwardGuid}`}
1642
+ item={result.outward}
1643
+ onSelect={() => dispatch(setSelectedOutwardKey(getFlightKey(result.outward.segments)))}
1644
+ guid={result.outwardGuid}
1645
+ isOutward={true}
1646
+ price={result.price}
1647
+ currentSelectedPrice={selectedOutward?.price}
1648
+ />
1649
+ ))}
1609
1650
  </div>
1610
- )}
1611
- </>
1612
- )}
1613
- </>
1614
- )}
1651
+ {uniqueOutwardFlights && uniqueOutwardFlights.length > 3 && (
1652
+ <div className="search__results__cards__actions">
1653
+ <button className="cta cta--secondary" onClick={() => handleShowMoreFlights('flight-outward-results')}>
1654
+ {translations.SRP.SHOW_MORE}
1655
+ </button>
1656
+ </div>
1657
+ )}
1658
+ </>
1659
+ )}
1660
+ </>
1661
+ )}
1615
1662
 
1616
- {context.showHotelAccommodationResults && !context.packagingEntry && <HotelAccommodationResults isLoading={isLoading} />}
1663
+ {context.showHotelAccommodationResults && !context.packagingEntry && <HotelAccommodationResults isLoading={isLoading} />}
1617
1664
 
1618
- {context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight && !context.packagingEntry && <DayByDayExcursions />}
1665
+ {context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight && !context.packagingEntry && <DayByDayExcursions />}
1619
1666
 
1620
- {context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight && !context.packagingEntry && context.showFlightResults && (
1621
- <>
1622
- <div className="search__results__label search__results__label--secondary">
1623
- <div className="search__results__label__date">
1624
- <Icon name="ui-flight" height={16} fill="white" />
1625
- </div>
1626
- <div className="search__results__label__text">
1627
- <h3>
1628
- {translations.SRP.SELECT} <strong> {translations.SRP.RETURN}</strong>
1629
- </h3>
1667
+ {context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight && !context.packagingEntry && context.showFlightResults && (
1668
+ <>
1669
+ <div className="search__results__label search__results__label--secondary">
1670
+ <div className="search__results__label__date">
1671
+ {(() => {
1672
+ const firstResultDate = uniqueReturnFlights.length > 0 ? uniqueReturnFlights[0].return.segments[0].departureDateTime : null;
1673
+
1674
+ const firstResultDay = firstResultDate ? format(firstResultDate, 'd') : null;
1675
+ const firstResultMonth = firstResultDate ? format(firstResultDate, 'MMM') : null;
1676
+
1677
+ return (
1678
+ <>
1679
+ <p className="search__results__label__date-date">{firstResultDay}</p>
1680
+ <p>{firstResultMonth}</p>
1681
+ </>
1682
+ );
1683
+ })()}
1684
+ </div>
1685
+ <div className="search__results__label__text">
1686
+ <Icon name="ui-flight" height={16} />
1687
+ <h3>
1688
+ {translations.SRP.SELECT} <strong> {translations.SRP.RETURN}</strong>
1689
+ </h3>
1690
+ </div>
1630
1691
  </div>
1631
- </div>
1632
1692
 
1633
- <div className="search__results__cards search__results__cards--extended">
1634
- {selectedReturnKey && selectedReturn && (
1635
- <IndependentFlightOption
1636
- key={`flight-${selectedReturnKey}`}
1637
- item={selectedReturn.return}
1638
- guid={selectedReturn.outwardGuid}
1639
- selectedGuid={selectedReturn.outwardGuid}
1640
- isOutward={false}
1641
- showSelectedState={true}
1642
- price={selectedReturn.price}
1643
- />
1644
- )}
1645
- {uniqueReturnFlights.map((result) => (
1646
- <IndependentFlightOption
1647
- key={`flight-${result.outwardGuid}`}
1648
- item={result.return}
1649
- onSelect={() => dispatch(setSelectedReturnKey(getFlightKey(result.return.segments)))}
1650
- guid={result.outwardGuid}
1651
- isOutward={false}
1652
- currentSelectedPrice={selectedReturn?.price}
1653
- price={result.price}
1654
- />
1655
- ))}
1656
- </div>
1657
- </>
1658
- )}
1693
+ <div className="search__results__cards search__results__cards--extended">
1694
+ {selectedReturnKey && selectedReturn && (
1695
+ <IndependentFlightOption
1696
+ key={`flight-${selectedReturnKey}`}
1697
+ item={selectedReturn.return}
1698
+ guid={selectedReturn.outwardGuid}
1699
+ selectedGuid={selectedReturn.outwardGuid}
1700
+ isOutward={false}
1701
+ showSelectedState={true}
1702
+ price={selectedReturn.price}
1703
+ />
1704
+ )}
1705
+ {uniqueReturnFlights.map((result) => (
1706
+ <IndependentFlightOption
1707
+ key={`flight-${result.outwardGuid}`}
1708
+ item={result.return}
1709
+ onSelect={() => dispatch(setSelectedReturnKey(getFlightKey(result.return.segments)))}
1710
+ guid={result.outwardGuid}
1711
+ isOutward={false}
1712
+ currentSelectedPrice={selectedReturn?.price}
1713
+ price={result.price}
1714
+ />
1715
+ ))}
1716
+ </div>
1717
+ </>
1718
+ )}
1659
1719
 
1660
- {context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight && context.packagingEntry && (
1661
- <FullItinerary isLoading={itineraryIsLoading} />
1662
- )}
1720
+ {context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight && context.packagingEntry && (
1721
+ <FullItinerary isLoading={itineraryIsLoading} />
1722
+ )}
1723
+ </div>
1663
1724
  </div>
1664
- </div>
1665
- {/* <button onClick={() => handleFlyInToggle(!flyInIsOpen)}>Toggle FlyIn</button> */}
1666
- <FlyIn
1667
- srpType={context.searchConfiguration.qsmType}
1668
- isOpen={flyInIsOpen}
1669
- setIsOpen={handleFlyInToggle}
1670
- handleConfirm={() => handleConfirmHotelSwap()}
1671
- onPanelRef={(el) => (panelRef.current = el)}
1672
- detailsLoading={detailsIsLoading}
1673
- flyInType={flyInType}
1674
- isPackageEditFlow={!!context.packagingEntry}
1675
- sortByTypes={sortByTypes}
1676
- activeSearchSeed={activeSearchSeed}
1677
- />
1678
- </>
1679
- )}
1680
- </div>
1725
+ {/* <button onClick={() => handleFlyInToggle(!flyInIsOpen)}>Toggle FlyIn</button> */}
1726
+ <FlyIn
1727
+ srpType={context.searchConfiguration.qsmType}
1728
+ isOpen={flyInIsOpen}
1729
+ setIsOpen={handleFlyInToggle}
1730
+ handleConfirm={() => handleConfirmHotelSwap()}
1731
+ onPanelRef={(el) => (panelRef.current = el)}
1732
+ detailsLoading={detailsIsLoading}
1733
+ flyInType={flyInType}
1734
+ isPackageEditFlow={!!context.packagingEntry}
1735
+ sortByTypes={sortByTypes}
1736
+ activeSearchSeed={activeSearchSeed}
1737
+ toggleFilters={() => setFiltersOpen(!filtersOpen)}
1738
+ filtersOpen={filtersOpen}
1739
+ />
1740
+ </>
1741
+ )}
1742
+ </div>
1743
+ )}
1681
1744
  </div>
1682
1745
  )}
1683
1746
  </div>
@@ -2,13 +2,21 @@ import React, { useContext } from 'react';
2
2
  import SearchResultsConfigurationContext from '../../search-results-configuration-context';
3
3
  import { getTranslations } from '../../../shared/utils/localization-util';
4
4
 
5
- const Spinner: React.FC = () => {
5
+ type SpinnerProps = {
6
+ label?: string;
7
+ };
8
+
9
+ const Spinner: React.FC<SpinnerProps> = ({ label }) => {
6
10
  const context = useContext(SearchResultsConfigurationContext);
7
11
  const translations = getTranslations(context?.languageCode ?? 'en-GB');
8
12
  return (
9
- <div className="spinner__container">
10
- <span className="spinner__icon" />
11
- <span className="spinner__label">{translations?.SRP.LOADING}</span>
13
+ <div className="loader__container">
14
+ <div className="loader__line">
15
+ <li className="loader__ball loader__ball--1"></li>
16
+ <li className="loader__ball loader__ball--2"></li>
17
+ <li className="loader__ball loader__ball--3"></li>
18
+ </div>
19
+ <div data-text={label ?? translations.SRP.LOADING} className="loader__line__text"></div>
12
20
  </div>
13
21
  );
14
22
  };