@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.
@@ -3,7 +3,7 @@ import { BookingPackage, BookingPackageItem, EntryLight, PackagingAccommodationR
3
3
  export interface SearchResultsState {
4
4
  results: BookingPackageItem[];
5
5
  filteredResults: BookingPackageItem[];
6
- selectedSearchResultId: number | null;
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<number | null, 'searchResults/setSelectedSearchResult'>,
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 { BookingPackageItem, FlightSearchResponseItem, WebsiteConfigurationSearchConfiguration } from '@qite/tide-client';
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: BookingPackageItem) => void;
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';
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ type GroupTourFlyInProps = {
3
+ isLoading: boolean;
4
+ isOpen: boolean;
5
+ setIsOpen: (open: boolean) => void;
6
+ };
7
+ declare const GroupTourFlyIn: React.FC<GroupTourFlyInProps>;
8
+ export default GroupTourFlyIn;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qite/tide-booking-component",
3
- "version": "1.4.87",
3
+ "version": "1.4.88",
4
4
  "description": "React Booking wizard & Booking product component for Tide",
5
5
  "main": "build/build-cjs/index.js",
6
6
  "types": "build/build-cjs/src/index.d.ts",
@@ -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 { selectedSearchResultId } = useSelector((state: SearchResultsRootState) => state.searchResults);
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 = (productId: number) => {
33
- dispatch(setSelectedSearchResult(productId));
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="cta cta--select" onClick={() => context.onBook && context.onBook(result)}>
97
- {translations?.PRODUCT.BOOK_NOW}
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
- selectedSearchResultId,
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.productId));
496
+ dispatch(setSelectedSearchResult(matching));
497
497
  }
498
498
  } else {
499
499
  if (context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight) {
500
- dispatch(setSelectedSearchResult(packageSearchResults[0]?.productId));
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', selectedSearchResultId);
581
- if (!selectedSearchResultId || !context) return;
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 === selectedSearchResultId);
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 (selectedSearchResultId) {
734
+ if (selectedSearchResult) {
735
735
  fetchDetails();
736
736
  }
737
737
  if (selectedPackagingAccoResultCode) {
738
738
  fetchPackagingAccoSearchDetails();
739
739
  }
740
- }, [selectedSearchResultId, selectedPackagingAccoResultCode]);
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
- selectedSearchResultId: number | null;
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
- selectedSearchResultId: null,
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<number | null>) {
56
- state.selectedSearchResultId = action.payload;
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 { BookingPackageItem, FlightSearchResponseItem, WebsiteConfigurationSearchConfiguration } from '@qite/tide-client';
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: BookingPackageItem) => void;
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
- {(srpType === PortalQsmType.Accommodation || srpType === PortalQsmType.GroupTour) && (
74
- <AccommodationFlyIn isLoading={true} isOpen={isOpen} setIsOpen={setIsOpen} />
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: 30px 0 0px;
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: 20rem;
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 {