@qite/tide-booking-component 1.4.87 → 1.4.88
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.
- package/build/build-cjs/index.js +361 -67
- package/build/build-cjs/src/search-results/store/search-results-slice.d.ts +2 -2
- package/build/build-cjs/src/search-results/types.d.ts +2 -2
- package/build/build-cjs/src/shared/components/flyin/group-tour-flyin.d.ts +8 -0
- package/build/build-esm/index.js +356 -67
- package/build/build-esm/src/search-results/store/search-results-slice.d.ts +2 -2
- package/build/build-esm/src/search-results/types.d.ts +2 -2
- package/build/build-esm/src/shared/components/flyin/group-tour-flyin.d.ts +8 -0
- package/package.json +1 -1
- package/src/search-results/components/group-tour/group-tour-card.tsx +5 -11
- package/src/search-results/components/search-results-container/search-results-container.tsx +8 -8
- package/src/search-results/store/search-results-slice.ts +4 -4
- package/src/search-results/types.ts +2 -2
- package/src/shared/components/flyin/flyin.tsx +3 -3
- package/src/shared/components/flyin/group-tour-flyin.tsx +254 -0
- package/styles/components/_booking.scss +2 -2
- package/styles/components/_form.scss +3 -3
|
@@ -3,7 +3,7 @@ import { BookingPackage, BookingPackageItem, EntryLight, PackagingAccommodationR
|
|
|
3
3
|
export interface SearchResultsState {
|
|
4
4
|
results: BookingPackageItem[];
|
|
5
5
|
filteredResults: BookingPackageItem[];
|
|
6
|
-
|
|
6
|
+
selectedSearchResult: BookingPackageItem | null;
|
|
7
7
|
packagingAccoResults: PackagingAccommodationResponse[];
|
|
8
8
|
filteredPackagingAccoResults: PackagingAccommodationResponse[];
|
|
9
9
|
packagingAccoSearchDetails: PackagingAccommodationResponse[];
|
|
@@ -21,7 +21,7 @@ export interface SearchResultsState {
|
|
|
21
21
|
}
|
|
22
22
|
export declare const setResults: import('@reduxjs/toolkit').ActionCreatorWithPayload<BookingPackageItem[], 'searchResults/setResults'>,
|
|
23
23
|
setFilteredResults: import('@reduxjs/toolkit').ActionCreatorWithPayload<BookingPackageItem[], 'searchResults/setFilteredResults'>,
|
|
24
|
-
setSelectedSearchResult: import('@reduxjs/toolkit').ActionCreatorWithPayload<
|
|
24
|
+
setSelectedSearchResult: import('@reduxjs/toolkit').ActionCreatorWithPayload<BookingPackageItem | null, 'searchResults/setSelectedSearchResult'>,
|
|
25
25
|
setPackagingAccoResults: import('@reduxjs/toolkit').ActionCreatorWithPayload<PackagingAccommodationResponse[], 'searchResults/setPackagingAccoResults'>,
|
|
26
26
|
setFilteredPackagingAccoResults: import('@reduxjs/toolkit').ActionCreatorWithPayload<
|
|
27
27
|
PackagingAccommodationResponse[],
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BookingPackage, FlightSearchResponseItem, WebsiteConfigurationSearchConfiguration } from '@qite/tide-client';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
3
|
export type FlightSelectionMode = 'paired' | 'independent';
|
|
4
4
|
export interface SearchResultsConfiguration {
|
|
@@ -34,7 +34,7 @@ export interface SearchResultsConfiguration {
|
|
|
34
34
|
url: string;
|
|
35
35
|
alt: string;
|
|
36
36
|
};
|
|
37
|
-
onBook?: (result:
|
|
37
|
+
onBook?: (result: BookingPackage) => void;
|
|
38
38
|
}
|
|
39
39
|
export type FilterType = 'checkbox' | 'toggle' | 'slider' | 'star-rating';
|
|
40
40
|
export type FilterProperty = 'regime' | 'accommodation' | 'max-duration' | 'price' | 'rating' | 'theme';
|
package/package.json
CHANGED
|
@@ -17,7 +17,7 @@ interface GroupTourCardProps {
|
|
|
17
17
|
|
|
18
18
|
const GroupTourCard: React.FC<GroupTourCardProps> = ({ result, languageCode, cmsItem }) => {
|
|
19
19
|
const context = useContext(SearchResultsConfigurationContext);
|
|
20
|
-
const {
|
|
20
|
+
const { selectedSearchResult } = useSelector((state: SearchResultsRootState) => state.searchResults);
|
|
21
21
|
if (!context) {
|
|
22
22
|
return;
|
|
23
23
|
}
|
|
@@ -29,8 +29,8 @@ const GroupTourCard: React.FC<GroupTourCardProps> = ({ result, languageCode, cms
|
|
|
29
29
|
const femaleCount = genders.filter((g) => g === 1).length;
|
|
30
30
|
const otherCount = genders.filter((g) => g === 2).length;
|
|
31
31
|
|
|
32
|
-
const handleChange = (
|
|
33
|
-
dispatch(setSelectedSearchResult(
|
|
32
|
+
const handleChange = (result: BookingPackageItem) => {
|
|
33
|
+
dispatch(setSelectedSearchResult(result));
|
|
34
34
|
};
|
|
35
35
|
|
|
36
36
|
return (
|
|
@@ -93,14 +93,8 @@ const GroupTourCard: React.FC<GroupTourCardProps> = ({ result, languageCode, cms
|
|
|
93
93
|
</div>
|
|
94
94
|
{/* <div className="search__result-card__allotment__price__info">Gelieve € 450,00 zakgeld mee te nemen</div> */}
|
|
95
95
|
</div>
|
|
96
|
-
<button type="button" className=
|
|
97
|
-
{translations?.
|
|
98
|
-
</button>
|
|
99
|
-
<button
|
|
100
|
-
type="button"
|
|
101
|
-
className={`cta ${selectedSearchResultId === result.productId ? 'cta--selected' : 'cta--select'}`}
|
|
102
|
-
onClick={() => handleChange(result.productId)}>
|
|
103
|
-
{selectedSearchResultId === result.productId ? translations?.SHARED.SELECTED : translations?.SHARED.SELECT}
|
|
96
|
+
<button type="button" className={`cta ${selectedSearchResult === result ? 'cta--selected' : 'cta--select'}`} onClick={() => handleChange(result)}>
|
|
97
|
+
{selectedSearchResult === result ? translations?.SHARED.SELECTED : translations?.SHARED.SELECT}
|
|
104
98
|
</button>
|
|
105
99
|
</div>
|
|
106
100
|
</div>
|
|
@@ -78,7 +78,7 @@ const SearchResultsContainer: React.FC = () => {
|
|
|
78
78
|
isLoading,
|
|
79
79
|
filters,
|
|
80
80
|
selectedSortType,
|
|
81
|
-
|
|
81
|
+
selectedSearchResult,
|
|
82
82
|
selectedPackagingAccoResultCode,
|
|
83
83
|
flyInIsOpen
|
|
84
84
|
} = useSelector((state: SearchResultsRootState) => state.searchResults);
|
|
@@ -493,11 +493,11 @@ const SearchResultsContainer: React.FC = () => {
|
|
|
493
493
|
const matching = packageSearchResults.find((r) => r.productId === entry?.id);
|
|
494
494
|
|
|
495
495
|
if (matching) {
|
|
496
|
-
dispatch(setSelectedSearchResult(matching
|
|
496
|
+
dispatch(setSelectedSearchResult(matching));
|
|
497
497
|
}
|
|
498
498
|
} else {
|
|
499
499
|
if (context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight) {
|
|
500
|
-
dispatch(setSelectedSearchResult(packageSearchResults[0]
|
|
500
|
+
dispatch(setSelectedSearchResult(packageSearchResults[0]));
|
|
501
501
|
}
|
|
502
502
|
}
|
|
503
503
|
}
|
|
@@ -577,8 +577,8 @@ const SearchResultsContainer: React.FC = () => {
|
|
|
577
577
|
// Seperate detailsCall
|
|
578
578
|
useEffect(() => {
|
|
579
579
|
const fetchDetails = async () => {
|
|
580
|
-
console.log('Fetching details for selected search result',
|
|
581
|
-
if (!
|
|
580
|
+
console.log('Fetching details for selected search result', selectedSearchResult);
|
|
581
|
+
if (!selectedSearchResult || !context) return;
|
|
582
582
|
if (context?.searchConfiguration.qsmType === PortalQsmType.Accommodation || context?.searchConfiguration.qsmType === PortalQsmType.GroupTour) {
|
|
583
583
|
handleFlyInToggle(true);
|
|
584
584
|
}
|
|
@@ -589,7 +589,7 @@ const SearchResultsContainer: React.FC = () => {
|
|
|
589
589
|
apiKey: context.tideConnection.apiKey
|
|
590
590
|
};
|
|
591
591
|
|
|
592
|
-
const selectedItem = results.find((r) => r.productId ===
|
|
592
|
+
const selectedItem = results.find((r) => r.productId === selectedSearchResult.productId);
|
|
593
593
|
if (!selectedItem) {
|
|
594
594
|
// TODO: handle this case better, show an error message to the user
|
|
595
595
|
return;
|
|
@@ -731,13 +731,13 @@ const SearchResultsContainer: React.FC = () => {
|
|
|
731
731
|
}
|
|
732
732
|
};
|
|
733
733
|
|
|
734
|
-
if (
|
|
734
|
+
if (selectedSearchResult) {
|
|
735
735
|
fetchDetails();
|
|
736
736
|
}
|
|
737
737
|
if (selectedPackagingAccoResultCode) {
|
|
738
738
|
fetchPackagingAccoSearchDetails();
|
|
739
739
|
}
|
|
740
|
-
}, [
|
|
740
|
+
}, [selectedSearchResult, selectedPackagingAccoResultCode]);
|
|
741
741
|
|
|
742
742
|
useEffect(() => {
|
|
743
743
|
if (context?.searchConfiguration.qsmType === PortalQsmType.Accommodation) {
|
|
@@ -5,7 +5,7 @@ import { BookingPackage, BookingPackageItem, EntryLight, PackagingAccommodationR
|
|
|
5
5
|
export interface SearchResultsState {
|
|
6
6
|
results: BookingPackageItem[];
|
|
7
7
|
filteredResults: BookingPackageItem[];
|
|
8
|
-
|
|
8
|
+
selectedSearchResult: BookingPackageItem | null;
|
|
9
9
|
packagingAccoResults: PackagingAccommodationResponse[];
|
|
10
10
|
filteredPackagingAccoResults: PackagingAccommodationResponse[];
|
|
11
11
|
packagingAccoSearchDetails: PackagingAccommodationResponse[];
|
|
@@ -25,7 +25,7 @@ export interface SearchResultsState {
|
|
|
25
25
|
const initialState: SearchResultsState = {
|
|
26
26
|
results: [],
|
|
27
27
|
filteredResults: [],
|
|
28
|
-
|
|
28
|
+
selectedSearchResult: null,
|
|
29
29
|
packagingAccoResults: [],
|
|
30
30
|
filteredPackagingAccoResults: [],
|
|
31
31
|
packagingAccoSearchDetails: [],
|
|
@@ -52,8 +52,8 @@ const searchResultsSlice = createSlice({
|
|
|
52
52
|
setFilteredResults(state, action: PayloadAction<BookingPackageItem[]>) {
|
|
53
53
|
state.filteredResults = action.payload;
|
|
54
54
|
},
|
|
55
|
-
setSelectedSearchResult(state, action: PayloadAction<
|
|
56
|
-
state.
|
|
55
|
+
setSelectedSearchResult(state, action: PayloadAction<BookingPackageItem | null>) {
|
|
56
|
+
state.selectedSearchResult = action.payload;
|
|
57
57
|
},
|
|
58
58
|
setPackagingAccoResults(state, action: PayloadAction<PackagingAccommodationResponse[]>) {
|
|
59
59
|
state.packagingAccoResults = action.payload;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BookingPackage, FlightSearchResponseItem, WebsiteConfigurationSearchConfiguration } from '@qite/tide-client';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
3
|
|
|
4
4
|
export type FlightSelectionMode = 'paired' | 'independent';
|
|
@@ -51,7 +51,7 @@ export interface SearchResultsConfiguration {
|
|
|
51
51
|
languageCode?: string;
|
|
52
52
|
|
|
53
53
|
destinationImage?: { url: string; alt: string };
|
|
54
|
-
onBook?: (result:
|
|
54
|
+
onBook?: (result: BookingPackage) => void;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
export type FilterType = 'checkbox' | 'toggle' | 'slider' | 'star-rating';
|
|
@@ -6,6 +6,7 @@ import { setSelectedFlight, setSelectedFlightDetails } from '../../../search-res
|
|
|
6
6
|
import FlightsFlyIn from './flights-flyin';
|
|
7
7
|
import AccommodationFlyIn from './accommodation-flyin';
|
|
8
8
|
import { PortalQsmType } from '@qite/tide-client';
|
|
9
|
+
import GroupTourFlyIn from './group-tour-flyin';
|
|
9
10
|
|
|
10
11
|
type FlyInProps = {
|
|
11
12
|
title: string;
|
|
@@ -70,9 +71,8 @@ const FlyIn: React.FC<FlyInProps> = ({ title, srpType, isOpen, setIsOpen, classN
|
|
|
70
71
|
</div>
|
|
71
72
|
</div>
|
|
72
73
|
{srpType === PortalQsmType.Flight && <FlightsFlyIn isOpen={isOpen} setIsOpen={setIsOpen} />}
|
|
73
|
-
{
|
|
74
|
-
|
|
75
|
-
)}
|
|
74
|
+
{srpType === PortalQsmType.Accommodation && <AccommodationFlyIn isLoading={true} isOpen={isOpen} setIsOpen={setIsOpen} />}
|
|
75
|
+
{srpType === PortalQsmType.GroupTour && <GroupTourFlyIn isLoading={true} isOpen={isOpen} setIsOpen={setIsOpen} />}
|
|
76
76
|
</div>
|
|
77
77
|
</div>
|
|
78
78
|
);
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import React, { useContext, useMemo } from 'react';
|
|
2
|
+
import { useDispatch, useSelector } from 'react-redux';
|
|
3
|
+
import ItemPicker from '../../../qsm/components/item-picker';
|
|
4
|
+
import { SearchResultsRootState } from '../../../search-results/store/search-results-store';
|
|
5
|
+
import SearchResultsConfigurationContext from '../../../search-results/search-results-configuration-context';
|
|
6
|
+
import { getTranslations } from '../../utils/localization-util';
|
|
7
|
+
import { setBookingPackageDetails } from '../../../search-results/store/search-results-slice';
|
|
8
|
+
import { BookingPackageOption } from '@qite/tide-client';
|
|
9
|
+
import { PickerItem } from '../../types';
|
|
10
|
+
import { first } from 'lodash';
|
|
11
|
+
|
|
12
|
+
type GroupTourFlyInProps = {
|
|
13
|
+
isLoading: boolean;
|
|
14
|
+
isOpen: boolean;
|
|
15
|
+
setIsOpen: (open: boolean) => void;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
type GroupedAccommodation = {
|
|
19
|
+
accommodationCode: string;
|
|
20
|
+
accommodationName: string;
|
|
21
|
+
regimes: PickerItem[];
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const formatPrice = (price?: number, currencyCode = 'EUR') => {
|
|
25
|
+
if (typeof price !== 'number') return '';
|
|
26
|
+
|
|
27
|
+
return new Intl.NumberFormat('nl-BE', {
|
|
28
|
+
style: 'currency',
|
|
29
|
+
currency: currencyCode
|
|
30
|
+
}).format(price);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const GroupTourFlyIn: React.FC<GroupTourFlyInProps> = ({ isLoading, isOpen, setIsOpen }) => {
|
|
34
|
+
const dispatch = useDispatch();
|
|
35
|
+
const context = useContext(SearchResultsConfigurationContext);
|
|
36
|
+
const language = context?.languageCode ?? 'en-GB';
|
|
37
|
+
const translations = getTranslations(language);
|
|
38
|
+
|
|
39
|
+
const { bookingPackageDetails } = useSelector((state: SearchResultsRootState) => state.searchResults);
|
|
40
|
+
|
|
41
|
+
const selectedBookingPackageDetails = useMemo<BookingPackageOption | undefined>(() => {
|
|
42
|
+
return bookingPackageDetails?.options?.find((x) => x.isSelected);
|
|
43
|
+
}, [bookingPackageDetails]);
|
|
44
|
+
|
|
45
|
+
const groupedRooms = useMemo(() => {
|
|
46
|
+
if (!selectedBookingPackageDetails?.rooms) return [];
|
|
47
|
+
|
|
48
|
+
return selectedBookingPackageDetails.rooms.map((room) => {
|
|
49
|
+
const groupedMap = new Map<string, GroupedAccommodation>();
|
|
50
|
+
|
|
51
|
+
room.options.forEach((option) => {
|
|
52
|
+
const key = option.accommodationCode;
|
|
53
|
+
|
|
54
|
+
if (!groupedMap.has(key)) {
|
|
55
|
+
groupedMap.set(key, {
|
|
56
|
+
accommodationCode: option.accommodationCode,
|
|
57
|
+
accommodationName: option.accommodationName,
|
|
58
|
+
regimes: []
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
groupedMap.get(key)?.regimes.push({
|
|
63
|
+
id: option.entryLineGuid,
|
|
64
|
+
label: option.regimeName
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
return Array.from(groupedMap.values());
|
|
69
|
+
});
|
|
70
|
+
}, [selectedBookingPackageDetails]);
|
|
71
|
+
|
|
72
|
+
if (!bookingPackageDetails) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const getSelectedOptionForRoom = (roomIndex: number) => {
|
|
77
|
+
return selectedBookingPackageDetails?.rooms?.[roomIndex]?.options?.find((option) => option.isSelected);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const getSelectedOptionForAccommodation = (roomIndex: number, accommodationCode: string) => {
|
|
81
|
+
return selectedBookingPackageDetails?.rooms?.[roomIndex]?.options?.find((option) => option.accommodationCode === accommodationCode && option.isSelected);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const handlePick = (roomIndex: number, selectedGuid?: string) => {
|
|
85
|
+
if (!bookingPackageDetails || !selectedBookingPackageDetails) return;
|
|
86
|
+
|
|
87
|
+
const updatedBookingPackageDetails = {
|
|
88
|
+
...bookingPackageDetails,
|
|
89
|
+
options: bookingPackageDetails.options.map((bookingOption) => {
|
|
90
|
+
if (!bookingOption.isSelected) {
|
|
91
|
+
return bookingOption;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
...bookingOption,
|
|
96
|
+
rooms: bookingOption.rooms.map((room, currentRoomIndex) => {
|
|
97
|
+
if (currentRoomIndex !== roomIndex) {
|
|
98
|
+
return room;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
...room,
|
|
103
|
+
options: room.options.map((roomOption) => ({
|
|
104
|
+
...roomOption,
|
|
105
|
+
isSelected: roomOption.entryLineGuid === selectedGuid
|
|
106
|
+
}))
|
|
107
|
+
};
|
|
108
|
+
})
|
|
109
|
+
};
|
|
110
|
+
})
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
dispatch(setBookingPackageDetails({ details: updatedBookingPackageDetails }));
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const handleConfirm = () => {
|
|
117
|
+
if (isOpen) {
|
|
118
|
+
setIsOpen(false);
|
|
119
|
+
}
|
|
120
|
+
console.log('Booking package details sent to onBook callback:', bookingPackageDetails);
|
|
121
|
+
if (context?.onBook) {
|
|
122
|
+
context.onBook(bookingPackageDetails);
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const calculateTotalPrice = () => {
|
|
127
|
+
const selectedOptions = selectedBookingPackageDetails?.rooms.flatMap((room) => room.options.filter((option) => option.isSelected));
|
|
128
|
+
const totalPrice = selectedOptions?.reduce((total, option) => total + (option.price || 0), 0);
|
|
129
|
+
return formatPrice(totalPrice, bookingPackageDetails?.currencyCode);
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const getPriceDifference = (currentSelectedPrice: number | undefined, roomIndex: number, accommodationCode: string, regimeId?: string) => {
|
|
133
|
+
let targetPrice = 0;
|
|
134
|
+
|
|
135
|
+
const selectedOption = getSelectedOptionForAccommodation(roomIndex, accommodationCode);
|
|
136
|
+
|
|
137
|
+
if (selectedOption?.price) {
|
|
138
|
+
targetPrice = selectedOption.price;
|
|
139
|
+
} else {
|
|
140
|
+
const firstOption = selectedBookingPackageDetails?.rooms[roomIndex].options.find((option) => option.accommodationCode === accommodationCode);
|
|
141
|
+
targetPrice = firstOption?.price || 0;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (regimeId) {
|
|
145
|
+
const regimeOption = selectedBookingPackageDetails?.rooms[roomIndex].options.find((option) => option.entryLineGuid === regimeId);
|
|
146
|
+
targetPrice = regimeOption?.price || 0;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return targetPrice - (currentSelectedPrice || 0);
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const formatPriceDifference = (difference: number, currencyCode: string) => {
|
|
153
|
+
if (difference === 0) {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const formattedAbsoluteValue = formatPrice(Math.abs(difference), currencyCode);
|
|
158
|
+
return `${difference > 0 ? '+' : '-'} ${formattedAbsoluteValue}`;
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const getPriceDifferenceClassName = (difference: number) => {
|
|
162
|
+
if (difference < 0) {
|
|
163
|
+
return 'flyin__acco__price flyin__acco__price--decrease';
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (difference > 0) {
|
|
167
|
+
return 'flyin__acco__price flyin__acco__price--increase';
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return 'flyin__acco__price';
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
const regimeFormatter = (roomIndex: number, accommodation: GroupedAccommodation, regimeId: string, label: string) => {
|
|
174
|
+
const roomOption = getSelectedOptionForRoom(roomIndex);
|
|
175
|
+
|
|
176
|
+
const difference = getPriceDifference(roomOption?.price, roomIndex, accommodation.accommodationCode, regimeId);
|
|
177
|
+
|
|
178
|
+
return `${label} ${difference !== 0 ? `(${formatPriceDifference(difference, bookingPackageDetails.currencyCode)})` : ''}`;
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
return (
|
|
182
|
+
<>
|
|
183
|
+
<div className="flyin__content">
|
|
184
|
+
{groupedRooms.map((roomAccommodations, roomIndex) => {
|
|
185
|
+
const selectedRoomOption = getSelectedOptionForRoom(roomIndex);
|
|
186
|
+
|
|
187
|
+
return (
|
|
188
|
+
<div className="flyin__acco" key={`room-${roomIndex}`}>
|
|
189
|
+
<h3 className="flyin__acco__room-title">Room {roomIndex + 1}</h3>
|
|
190
|
+
<div className="flyin__acco__cards">
|
|
191
|
+
{roomAccommodations.map((accommodation) => {
|
|
192
|
+
const selectedOption = getSelectedOptionForAccommodation(roomIndex, accommodation.accommodationCode);
|
|
193
|
+
|
|
194
|
+
const priceDifference = getPriceDifference(selectedRoomOption?.price, roomIndex, accommodation.accommodationCode);
|
|
195
|
+
return (
|
|
196
|
+
<div className="flyin__acco__card" key={`${roomIndex}-${accommodation.accommodationCode}`}>
|
|
197
|
+
<div className="flyin__acco__content">
|
|
198
|
+
<h4 className="flyin__acco__title">{accommodation.accommodationName}</h4>
|
|
199
|
+
</div>
|
|
200
|
+
|
|
201
|
+
<div className="flyin__acco__footer">
|
|
202
|
+
<ItemPicker
|
|
203
|
+
items={accommodation.regimes}
|
|
204
|
+
selection={selectedOption?.regimeName}
|
|
205
|
+
label={''}
|
|
206
|
+
placeholder={'Select regime'}
|
|
207
|
+
classModifier=""
|
|
208
|
+
onPick={(selected, selectedGuid) => handlePick(roomIndex, selectedGuid)}
|
|
209
|
+
valueFormatter={(id, label) => regimeFormatter(roomIndex, accommodation, id, label)}
|
|
210
|
+
/>
|
|
211
|
+
|
|
212
|
+
<div className="flyin__acco__footer__actions">
|
|
213
|
+
<button
|
|
214
|
+
className={
|
|
215
|
+
selectedRoomOption?.accommodationCode == accommodation.accommodationCode ? 'cta cta--select cta--selected' : 'cta cta--select'
|
|
216
|
+
}
|
|
217
|
+
onClick={() => {
|
|
218
|
+
handlePick(roomIndex, selectedOption ? selectedOption.entryLineGuid : first(accommodation.regimes)?.id);
|
|
219
|
+
}}>
|
|
220
|
+
{selectedRoomOption?.accommodationCode == accommodation.accommodationCode
|
|
221
|
+
? translations?.SHARED.SELECTED
|
|
222
|
+
: translations?.SHARED.SELECT}
|
|
223
|
+
</button>
|
|
224
|
+
|
|
225
|
+
<div className="flyin__acco__price__wrapper">
|
|
226
|
+
<span className={getPriceDifferenceClassName(priceDifference)}>
|
|
227
|
+
{formatPriceDifference(priceDifference, bookingPackageDetails?.currencyCode)}
|
|
228
|
+
</span>
|
|
229
|
+
</div>
|
|
230
|
+
</div>
|
|
231
|
+
</div>
|
|
232
|
+
</div>
|
|
233
|
+
);
|
|
234
|
+
})}
|
|
235
|
+
</div>
|
|
236
|
+
</div>
|
|
237
|
+
);
|
|
238
|
+
})}
|
|
239
|
+
</div>
|
|
240
|
+
|
|
241
|
+
<div className="flyin__footer">
|
|
242
|
+
<div className="flyin__footer__price">Total price: {calculateTotalPrice()}</div>
|
|
243
|
+
|
|
244
|
+
<div className="flyin__button-wrapper">
|
|
245
|
+
<button className="cta cta--select" onClick={handleConfirm}>
|
|
246
|
+
{translations.PRODUCT.BOOK_NOW}
|
|
247
|
+
</button>
|
|
248
|
+
</div>
|
|
249
|
+
</div>
|
|
250
|
+
</>
|
|
251
|
+
);
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
export default GroupTourFlyIn;
|
|
@@ -120,7 +120,7 @@
|
|
|
120
120
|
|
|
121
121
|
.booking__panel-heading {
|
|
122
122
|
//padding: 0;
|
|
123
|
-
margin-bottom: 30px;
|
|
123
|
+
// margin-bottom: 30px;
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
.booking__panel-body {
|
|
@@ -707,7 +707,7 @@
|
|
|
707
707
|
}
|
|
708
708
|
|
|
709
709
|
&__header {
|
|
710
|
-
padding:
|
|
710
|
+
padding: 15px 0 0px;
|
|
711
711
|
}
|
|
712
712
|
|
|
713
713
|
&__header-heading {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
&__wrapper {
|
|
5
5
|
display: flex;
|
|
6
6
|
flex-direction: column;
|
|
7
|
-
gap:
|
|
7
|
+
gap: 2rem;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
&__label {
|
|
@@ -1214,7 +1214,7 @@
|
|
|
1214
1214
|
}
|
|
1215
1215
|
|
|
1216
1216
|
&__container {
|
|
1217
|
-
margin-top: -25px;
|
|
1217
|
+
// margin-top: -25px;
|
|
1218
1218
|
}
|
|
1219
1219
|
|
|
1220
1220
|
&__header {
|
|
@@ -1502,7 +1502,7 @@
|
|
|
1502
1502
|
background: var(--tide-booking-form-card-background);
|
|
1503
1503
|
outline: var(--tide-booking-form-card-border);
|
|
1504
1504
|
border-radius: var(--tide-booking-form-card-border-radius);
|
|
1505
|
-
margin-top: -25px;
|
|
1505
|
+
// margin-top: -25px;
|
|
1506
1506
|
}
|
|
1507
1507
|
|
|
1508
1508
|
.form__region {
|