@qite/tide-booking-component 1.4.108 → 1.4.110

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 (86) hide show
  1. package/build/build-cjs/index.js +1607 -886
  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/search-results/components/book-packaging-entry/index.d.ts +7 -0
  7. package/build/build-cjs/src/search-results/components/book-packaging-entry/wl-sidebar.d.ts +7 -0
  8. package/build/build-cjs/src/search-results/components/spinner/spinner.d.ts +4 -1
  9. package/build/build-cjs/src/search-results/store/search-results-slice.d.ts +5 -1
  10. package/build/build-cjs/src/shared/booking/BookingPanel.d.ts +13 -0
  11. package/build/build-cjs/src/shared/booking/Sidebar.d.ts +34 -0
  12. package/build/build-cjs/src/shared/booking/StepIndicators.d.ts +7 -0
  13. package/build/build-cjs/src/shared/components/flyin/flyin.d.ts +2 -0
  14. package/build/build-cjs/src/shared/components/flyin/packaging-flights-flyin.d.ts +2 -0
  15. package/build/build-cjs/src/shared/utils/localization-util.d.ts +1 -0
  16. package/build/build-esm/index.js +1591 -881
  17. package/build/build-esm/src/booking-wizard/components/step-route.d.ts +2 -2
  18. package/build/build-esm/src/booking-wizard/features/sidebar/sidebar-flight.d.ts +1 -0
  19. package/build/build-esm/src/booking-wizard/features/sidebar/sidebar-util.d.ts +2 -1
  20. package/build/build-esm/src/booking-wizard/features/sidebar/sidebar.d.ts +0 -31
  21. package/build/build-esm/src/search-results/components/book-packaging-entry/index.d.ts +7 -0
  22. package/build/build-esm/src/search-results/components/book-packaging-entry/wl-sidebar.d.ts +7 -0
  23. package/build/build-esm/src/search-results/components/spinner/spinner.d.ts +4 -1
  24. package/build/build-esm/src/search-results/store/search-results-slice.d.ts +5 -1
  25. package/build/build-esm/src/shared/booking/BookingPanel.d.ts +13 -0
  26. package/build/build-esm/src/shared/booking/Sidebar.d.ts +34 -0
  27. package/build/build-esm/src/shared/booking/StepIndicators.d.ts +7 -0
  28. package/build/build-esm/src/shared/components/flyin/flyin.d.ts +2 -0
  29. package/build/build-esm/src/shared/components/flyin/packaging-flights-flyin.d.ts +2 -0
  30. package/build/build-esm/src/shared/utils/localization-util.d.ts +1 -0
  31. package/package.json +1 -1
  32. package/src/booking-wizard/components/step-indicator.tsx +10 -31
  33. package/src/booking-wizard/components/step-route.tsx +39 -14
  34. package/src/booking-wizard/features/sidebar/index.tsx +10 -4
  35. package/src/booking-wizard/features/sidebar/sidebar-flight.tsx +2 -2
  36. package/src/booking-wizard/features/sidebar/sidebar-util.ts +1 -5
  37. package/src/booking-wizard/features/sidebar/sidebar.tsx +331 -326
  38. package/src/content/components/image-with-text.tsx +0 -1
  39. package/src/qsm/components/QSMContainer/qsm-container.tsx +189 -83
  40. package/src/qsm/components/item-picker/index.tsx +2 -6
  41. package/src/qsm/components/mobile-filter-modal/index.tsx +15 -1
  42. package/src/search-results/components/book-packaging-entry/index.tsx +48 -0
  43. package/src/search-results/components/book-packaging-entry/wl-sidebar.tsx +165 -0
  44. package/src/search-results/components/excursions/day-by-day-excursions.tsx +6 -2
  45. package/src/search-results/components/excursions/excursion-results.tsx +1 -1
  46. package/src/search-results/components/flight/flight-selection/independent-flight-selection.tsx +12 -3
  47. package/src/search-results/components/hotel/hotel-accommodation-results.tsx +6 -3
  48. package/src/search-results/components/itinerary/full-itinerary.tsx +1 -1
  49. package/src/search-results/components/itinerary/index.tsx +13 -12
  50. package/src/search-results/components/search-results-container/flight-search-results.tsx +1 -1
  51. package/src/search-results/components/search-results-container/search-results-container.tsx +239 -204
  52. package/src/search-results/components/spinner/spinner.tsx +12 -4
  53. package/src/search-results/store/search-results-slice.ts +16 -2
  54. package/src/shared/booking/BookingPanel.tsx +25 -0
  55. package/src/shared/booking/Sidebar.tsx +432 -0
  56. package/src/shared/booking/StepIndicators.tsx +30 -0
  57. package/src/shared/components/flyin/accommodation-flyin.tsx +3 -4
  58. package/src/shared/components/flyin/flights-flyin.tsx +1 -1
  59. package/src/shared/components/flyin/flyin.tsx +12 -4
  60. package/src/shared/components/flyin/group-tour-flyin.tsx +3 -4
  61. package/src/shared/components/flyin/packaging-flights-flyin.tsx +11 -4
  62. package/src/shared/components/icon.tsx +13 -0
  63. package/src/shared/translations/ar-SA.json +7 -1
  64. package/src/shared/translations/da-DK.json +7 -1
  65. package/src/shared/translations/de-DE.json +7 -1
  66. package/src/shared/translations/en-GB.json +8 -2
  67. package/src/shared/translations/es-ES.json +7 -1
  68. package/src/shared/translations/fr-BE.json +7 -1
  69. package/src/shared/translations/fr-FR.json +7 -1
  70. package/src/shared/translations/is-IS.json +7 -1
  71. package/src/shared/translations/it-IT.json +7 -1
  72. package/src/shared/translations/ja-JP.json +7 -1
  73. package/src/shared/translations/nl-BE.json +7 -1
  74. package/src/shared/translations/nl-NL.json +7 -1
  75. package/src/shared/translations/no-NO.json +7 -1
  76. package/src/shared/translations/pl-PL.json +7 -1
  77. package/src/shared/translations/pt-PT.json +7 -1
  78. package/src/shared/translations/sv-SE.json +7 -1
  79. package/src/shared/utils/localization-util.ts +8 -0
  80. package/styles/components/_footer.scss +2 -8
  81. package/styles/components/_loader.scss +82 -0
  82. package/styles/components/_search.scss +14 -7
  83. package/styles/content-blocks-variables.scss +14 -14
  84. /package/build/build-cjs/src/{booking-wizard/components → shared/booking}/product-card.d.ts +0 -0
  85. /package/build/build-esm/src/{booking-wizard/components → shared/booking}/product-card.d.ts +0 -0
  86. /package/src/{booking-wizard/components → shared/booking}/product-card.tsx +0 -0
@@ -110,6 +110,8 @@ import {
110
110
  selectUniqueReturnFlights
111
111
  } from '../../store/search-results-selectors';
112
112
  import DayByDayExcursions from '../excursions/day-by-day-excursions';
113
+ import BookPackagingEntry from '../book-packaging-entry';
114
+ import { format } from 'date-fns';
113
115
 
114
116
  type BuildPackagingEntryPartialArgs = {
115
117
  sourceEntry: PackagingEntry | null | undefined;
@@ -149,9 +151,9 @@ const SearchResultsContainer: React.FC = () => {
149
151
  editablePackagingEntry,
150
152
  transactionId,
151
153
  flyInType,
152
- itinerary,
153
154
  packagingFlightResults,
154
- confirmedExcursionsByDay
155
+ confirmedExcursionsByDay,
156
+ bookPackagingEntry
155
157
  } = useSelector((state: SearchResultsRootState) => state.searchResults);
156
158
 
157
159
  const isMobile = useMediaQuery('(max-width: 1200px)');
@@ -1450,91 +1452,67 @@ const SearchResultsContainer: React.FC = () => {
1450
1452
  <div id="tide-booking" className="search__bg">
1451
1453
  {context && (
1452
1454
  <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))}
1481
- />
1482
- )}
1483
- {context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight && (
1484
- <Itinerary
1485
- isOpen={itineraryOpen}
1486
- handleSetIsOpen={() => setItineraryOpen(!itineraryOpen)}
1487
- isLoading={pricesAreLoading}
1488
- onEditAccommodation={handleEditAccommodation}
1455
+ {bookPackagingEntry ? (
1456
+ <BookPackagingEntry activeSearchSeed={activeSearchSeed} />
1457
+ ) : (
1458
+ <div className="search__container">
1459
+ {context.searchConfiguration.qsmType === PortalQsmType.Flight && (
1460
+ <FlightSearchProvider tideConnection={context.tideConnection}>
1461
+ <FlightResultsContainer isMobile={isMobile} />
1462
+ <FlyIn
1463
+ srpType={context.searchConfiguration.qsmType}
1464
+ isOpen={flyInIsOpen}
1465
+ setIsOpen={handleFlyInToggle}
1466
+ onPanelRef={(el) => (panelRef.current = el)}
1467
+ detailsLoading={detailsIsLoading}
1468
+ filtersOpen={filtersOpen}
1489
1469
  />
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>
1470
+ </FlightSearchProvider>
1471
+ )}
1472
+ {(context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight ||
1473
+ context.searchConfiguration.qsmType === PortalQsmType.Accommodation ||
1474
+ context.searchConfiguration.qsmType === PortalQsmType.GroupTour ||
1475
+ context.searchConfiguration.qsmType === PortalQsmType.RoundTrip) && (
1476
+ <>
1477
+ {context.searchConfiguration.qsmType != PortalQsmType.AccommodationAndFlight && context.showFilters && (
1478
+ <Filters
1479
+ initialFilters={initialFilters}
1480
+ filters={filters}
1481
+ isOpen={filtersOpen}
1482
+ handleSetIsOpen={() => setFiltersOpen(!filtersOpen)}
1483
+ // handleApplyFilters={() => setSearchTrigger((prev) => prev + 1)}
1484
+ isLoading={isLoading}
1485
+ setFilters={(filters) => dispatch(setFilters(filters))}
1486
+ resetFilters={(filters) => dispatch(resetFilters(filters))}
1487
+ />
1522
1488
  )}
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">
1489
+ {context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight && (
1490
+ <Itinerary
1491
+ isOpen={itineraryOpen}
1492
+ handleSetIsOpen={() => setItineraryOpen(!itineraryOpen)}
1493
+ isLoading={isLoading || pricesAreLoading || flightsLoading || itineraryIsLoading}
1494
+ onEditAccommodation={handleEditAccommodation}
1495
+ />
1496
+ )}
1497
+ {/* ---------------- Results ---------------- */}
1498
+ <div className="search__results">
1499
+ {isMobile && (
1500
+ <div className="search__result-row">
1501
+ <div className="search__results__actions">
1502
+ {context.searchConfiguration.qsmType != PortalQsmType.AccommodationAndFlight && context.showFilters && (
1503
+ <div className="cta cta--filter" onClick={() => setFiltersOpen(true)}>
1504
+ <Icon name="ui-filter" className="mobile-filters-button__icon" height={16} />
1505
+ {translations.SRP.FILTERS}
1506
+ </div>
1507
+ )}
1508
+ {context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight && (
1509
+ <div className="cta cta--filter" onClick={() => setItineraryOpen(true)}>
1510
+ <Icon name="ui-shopping-cart" className="mobile-filters-button__icon" height={16} />
1511
+ {translations.SRP.VIEW_BOOKING}
1512
+ </div>
1513
+ )}
1514
+ </div>
1515
+ {sortByTypes && sortByTypes.length > 0 && (
1538
1516
  <ItemPicker
1539
1517
  items={sortByTypes}
1540
1518
  selection={selectedSortType?.label || undefined}
@@ -1545,139 +1523,196 @@ const SearchResultsContainer: React.FC = () => {
1545
1523
  valueFormatter={(value, direction) => getSortingName(translations, findSortByType(sortByTypes, value, direction ?? 'asc'))}
1546
1524
  onPick={(newSortKey, direction) => handleSortChange(newSortKey, direction)}
1547
1525
  />
1548
- </div>
1549
- )}
1550
- </div>
1551
- )}
1526
+ )}
1527
+ </div>
1528
+ )}
1529
+ {context.searchConfiguration.qsmType !== PortalQsmType.AccommodationAndFlight && (
1530
+ <div className="search__result-row">
1531
+ <span className="search__result-row-text">
1532
+ {!isLoading && (
1533
+ <>
1534
+ {context.searchConfiguration.qsmType === PortalQsmType.Accommodation &&
1535
+ filteredPackagingAccoResults?.length &&
1536
+ filteredPackagingAccoResults?.length}
1537
+ {context.searchConfiguration.qsmType !== PortalQsmType.Accommodation && filteredResults?.length && filteredResults.length}
1538
+ &nbsp;{translations.SRP.TOTAL_RESULTS_LABEL}
1539
+ </>
1540
+ )}
1541
+ </span>
1542
+ {!context.packagingEntry && !isMobile && sortByTypes && sortByTypes.length > 0 && (
1543
+ <div className="search__result-row-filter">
1544
+ <ItemPicker
1545
+ items={sortByTypes}
1546
+ selection={selectedSortType?.label || undefined}
1547
+ selectedSortByType={selectedSortType}
1548
+ label={translations.SRP.SORTBY}
1549
+ placeholder={translations.SRP.SORTBY}
1550
+ classModifier="travel-class-picker__items"
1551
+ valueFormatter={(value, direction) => getSortingName(translations, findSortByType(sortByTypes, value, direction ?? 'asc'))}
1552
+ onPick={(newSortKey, direction) => handleSortChange(newSortKey, direction)}
1553
+ />
1554
+ </div>
1555
+ )}
1556
+ </div>
1557
+ )}
1552
1558
 
1553
- <div className="search__results__wrapper">
1554
- {context.showTabViews &&
1555
- (context.searchConfiguration.qsmType === PortalQsmType.GroupTour ||
1556
- context.searchConfiguration.qsmType === PortalQsmType.Accommodation) && <TabViews />}
1559
+ <div className="search__results__wrapper">
1560
+ {context.showTabViews &&
1561
+ (context.searchConfiguration.qsmType === PortalQsmType.GroupTour ||
1562
+ context.searchConfiguration.qsmType === PortalQsmType.Accommodation) && <TabViews />}
1557
1563
 
1558
- {context.showRoundTripResults && context.showMockup && <RoundTripResults />}
1564
+ {context.showRoundTripResults && context.showMockup && <RoundTripResults />}
1559
1565
 
1560
- {context.searchConfiguration.qsmType === PortalQsmType.GroupTour && <GroupTourResults isLoading={isLoading} />}
1566
+ {context.searchConfiguration.qsmType === PortalQsmType.GroupTour && <GroupTourResults isLoading={isLoading} />}
1561
1567
 
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>
1568
+ {context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight && !context.packagingEntry && context.showFlightResults && (
1569
+ <>
1570
+ <div className="search__results__label search__results__label--secondary">
1571
+ <div className="search__results__label__date">
1572
+ {(() => {
1573
+ const firstResultDate = uniqueOutwardFlights.length > 0 ? uniqueOutwardFlights[0].outward.segments[0].departureDateTime : null;
1574
1574
 
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
- ))}
1575
+ const firstResultDay = firstResultDate ? format(firstResultDate, 'd') : null;
1576
+ const firstResultMonth = firstResultDate ? format(firstResultDate, 'MMM') : null;
1577
+
1578
+ return (
1579
+ <>
1580
+ <p className="search__results__label__date-date">{firstResultDay}</p>
1581
+ <p>{firstResultMonth}</p>
1582
+ </>
1583
+ );
1584
+ })()}
1585
+ </div>
1586
+ <div className="search__results__label__text">
1587
+ <Icon name="ui-flight" height={16} />
1588
+ <h3>
1589
+ {translations.SRP.SELECT} <strong> {translations.SRP.DEPARTURE}</strong>
1590
+ </h3>
1603
1591
  </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>
1592
+ </div>
1593
+
1594
+ {flightsLoading ? (
1595
+ <Spinner label={translations.SRP.LOADING_FLIGHTS} />
1596
+ ) : (
1597
+ <>
1598
+ <div className="search__results__cards search__results__cards--extended">
1599
+ {selectedOutwardKey && selectedOutward && (
1600
+ <IndependentFlightOption
1601
+ key={`flight-${selectedOutwardKey}`}
1602
+ item={selectedOutward.outward}
1603
+ guid={selectedOutward.outwardGuid}
1604
+ onSelect={() => dispatch(setSelectedOutwardKey(null))}
1605
+ selectedGuid={selectedOutward.outwardGuid}
1606
+ isOutward={true}
1607
+ showSelectedState={true}
1608
+ price={selectedOutward.price}
1609
+ />
1610
+ )}
1611
+ {visibleOutwardFlights.map((result) => (
1612
+ <IndependentFlightOption
1613
+ key={`flight-${result.outwardGuid}`}
1614
+ item={result.outward}
1615
+ onSelect={() => dispatch(setSelectedOutwardKey(getFlightKey(result.outward.segments)))}
1616
+ guid={result.outwardGuid}
1617
+ isOutward={true}
1618
+ price={result.price}
1619
+ currentSelectedPrice={selectedOutward?.price}
1620
+ />
1621
+ ))}
1609
1622
  </div>
1610
- )}
1611
- </>
1612
- )}
1613
- </>
1614
- )}
1623
+ {uniqueOutwardFlights && uniqueOutwardFlights.length > 3 && (
1624
+ <div className="search__results__cards__actions">
1625
+ <button className="cta cta--secondary" onClick={() => handleShowMoreFlights('flight-outward-results')}>
1626
+ {translations.SRP.SHOW_MORE}
1627
+ </button>
1628
+ </div>
1629
+ )}
1630
+ </>
1631
+ )}
1632
+ </>
1633
+ )}
1615
1634
 
1616
- {context.showHotelAccommodationResults && !context.packagingEntry && <HotelAccommodationResults isLoading={isLoading} />}
1635
+ {context.showHotelAccommodationResults && !context.packagingEntry && <HotelAccommodationResults isLoading={isLoading} />}
1617
1636
 
1618
- {context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight && !context.packagingEntry && <DayByDayExcursions />}
1637
+ {context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight && !context.packagingEntry && <DayByDayExcursions />}
1619
1638
 
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>
1639
+ {context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight && !context.packagingEntry && context.showFlightResults && (
1640
+ <>
1641
+ <div className="search__results__label search__results__label--secondary">
1642
+ <div className="search__results__label__date">
1643
+ {(() => {
1644
+ const firstResultDate = uniqueReturnFlights.length > 0 ? uniqueReturnFlights[0].return.segments[0].departureDateTime : null;
1645
+
1646
+ const firstResultDay = firstResultDate ? format(firstResultDate, 'd') : null;
1647
+ const firstResultMonth = firstResultDate ? format(firstResultDate, 'MMM') : null;
1648
+
1649
+ return (
1650
+ <>
1651
+ <p className="search__results__label__date-date">{firstResultDay}</p>
1652
+ <p>{firstResultMonth}</p>
1653
+ </>
1654
+ );
1655
+ })()}
1656
+ </div>
1657
+ <div className="search__results__label__text">
1658
+ <Icon name="ui-flight" height={16} />
1659
+ <h3>
1660
+ {translations.SRP.SELECT} <strong> {translations.SRP.RETURN}</strong>
1661
+ </h3>
1662
+ </div>
1630
1663
  </div>
1631
- </div>
1632
1664
 
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
- )}
1665
+ <div className="search__results__cards search__results__cards--extended">
1666
+ {selectedReturnKey && selectedReturn && (
1667
+ <IndependentFlightOption
1668
+ key={`flight-${selectedReturnKey}`}
1669
+ item={selectedReturn.return}
1670
+ guid={selectedReturn.outwardGuid}
1671
+ selectedGuid={selectedReturn.outwardGuid}
1672
+ isOutward={false}
1673
+ showSelectedState={true}
1674
+ price={selectedReturn.price}
1675
+ />
1676
+ )}
1677
+ {uniqueReturnFlights.map((result) => (
1678
+ <IndependentFlightOption
1679
+ key={`flight-${result.outwardGuid}`}
1680
+ item={result.return}
1681
+ onSelect={() => dispatch(setSelectedReturnKey(getFlightKey(result.return.segments)))}
1682
+ guid={result.outwardGuid}
1683
+ isOutward={false}
1684
+ currentSelectedPrice={selectedReturn?.price}
1685
+ price={result.price}
1686
+ />
1687
+ ))}
1688
+ </div>
1689
+ </>
1690
+ )}
1659
1691
 
1660
- {context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight && context.packagingEntry && (
1661
- <FullItinerary isLoading={itineraryIsLoading} />
1662
- )}
1692
+ {context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight && context.packagingEntry && (
1693
+ <FullItinerary isLoading={itineraryIsLoading} />
1694
+ )}
1695
+ </div>
1663
1696
  </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>
1697
+ {/* <button onClick={() => handleFlyInToggle(!flyInIsOpen)}>Toggle FlyIn</button> */}
1698
+ <FlyIn
1699
+ srpType={context.searchConfiguration.qsmType}
1700
+ isOpen={flyInIsOpen}
1701
+ setIsOpen={handleFlyInToggle}
1702
+ handleConfirm={() => handleConfirmHotelSwap()}
1703
+ onPanelRef={(el) => (panelRef.current = el)}
1704
+ detailsLoading={detailsIsLoading}
1705
+ flyInType={flyInType}
1706
+ isPackageEditFlow={!!context.packagingEntry}
1707
+ sortByTypes={sortByTypes}
1708
+ activeSearchSeed={activeSearchSeed}
1709
+ toggleFilters={() => setFiltersOpen(!filtersOpen)}
1710
+ filtersOpen={filtersOpen}
1711
+ />
1712
+ </>
1713
+ )}
1714
+ </div>
1715
+ )}
1681
1716
  </div>
1682
1717
  )}
1683
1718
  </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
  };
@@ -56,6 +56,9 @@ export interface SearchResultsState {
56
56
  excursionSearchParams: ExcursionSearchParams | null;
57
57
  selectedExcursionSearchResult: PackagingAccommodationResponse | null;
58
58
  confirmedExcursionsByDay: Record<string, PackagingAccommodationResponse[]>;
59
+
60
+ bookPackagingEntry: boolean;
61
+ currentStep: number;
59
62
  }
60
63
 
61
64
  const initialState: SearchResultsState = {
@@ -103,7 +106,10 @@ const initialState: SearchResultsState = {
103
106
 
104
107
  excursionSearchParams: null,
105
108
  selectedExcursionSearchResult: null,
106
- confirmedExcursionsByDay: {}
109
+ confirmedExcursionsByDay: {},
110
+
111
+ bookPackagingEntry: false,
112
+ currentStep: 0
107
113
  };
108
114
 
109
115
  const searchResultsSlice = createSlice({
@@ -280,6 +286,12 @@ const searchResultsSlice = createSlice({
280
286
  },
281
287
  clearConfirmedExcursionsForDay: (state, action: PayloadAction<{ dayKey: string }>) => {
282
288
  delete state.confirmedExcursionsByDay[action.payload.dayKey];
289
+ },
290
+ setBookPackagingEntry(state, action: PayloadAction<boolean>) {
291
+ state.bookPackagingEntry = action.payload;
292
+ },
293
+ setCurrentStep(state, action: PayloadAction<number>) {
294
+ state.currentStep = action.payload;
283
295
  }
284
296
  }
285
297
  });
@@ -325,7 +337,9 @@ export const {
325
337
  setSelectedExcursionSearchResult,
326
338
  confirmExcursionForDay,
327
339
  removeConfirmedExcursionForDay,
328
- clearConfirmedExcursionsForDay
340
+ clearConfirmedExcursionsForDay,
341
+ setBookPackagingEntry,
342
+ setCurrentStep
329
343
  } = searchResultsSlice.actions;
330
344
 
331
345
  export default searchResultsSlice.reducer;
@@ -0,0 +1,25 @@
1
+ import React from 'react';
2
+
3
+ interface BookingPanelProps {
4
+ currentStep: number;
5
+ stepLabels: string[];
6
+ renderTitle: (step: number) => React.ReactNode;
7
+ children: React.ReactNode;
8
+ StepIndicatorsComponent: React.ComponentType<{ currentStep: number; stepLabels: string[] }>;
9
+ }
10
+
11
+ const BookingPanel: React.FC<BookingPanelProps> = ({ currentStep, stepLabels, renderTitle, children, StepIndicatorsComponent }) => {
12
+ return (
13
+ <div className="booking__panel">
14
+ <StepIndicatorsComponent currentStep={currentStep} stepLabels={stepLabels} />
15
+ <div className="booking__panel-frame booking__panel-frame--transparent">
16
+ <div className="booking__panel-heading">
17
+ <h4 className="booking__panel-title">{renderTitle(currentStep)}</h4>
18
+ </div>
19
+ <div className="booking__panel-body">{children}</div>
20
+ </div>
21
+ </div>
22
+ );
23
+ };
24
+
25
+ export default BookingPanel;