@devtravelcode/widget-native 1.0.2 → 1.0.3
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/dist/App.js +8 -5
- package/dist/modules/hooks/useHotelMapData.d.ts +1 -1
- package/dist/modules/hooks/useHotelMapData.js +26 -32
- package/dist/modules/search-form/AgeSelector.js +4 -1
- package/dist/modules/search-form/NationalityInput.js +7 -3
- package/dist/modules/search-form/TimePicker.js +4 -3
- package/dist/modules/search-form/flight/AirlinesSelector.js +7 -3
- package/dist/modules/search-form/flight/DatePicker.js +14 -2
- package/dist/modules/search-form/flight/FlightSearchForm.js +2 -1
- package/dist/modules/search-form/flight/LocationInput.js +6 -2
- package/dist/modules/search-form/flight/PassengersPicker.js +9 -4
- package/dist/modules/search-form/flight/SwitchLocationBtn.js +4 -1
- package/dist/modules/search-form/flight/flight-filters/FiltersContent.d.ts +1 -0
- package/dist/modules/search-form/flight/flight-filters/FiltersContent.js +14 -13
- package/dist/modules/search-form/flight/flight-filters/FlightFiltersDynamic.js +3 -2
- package/dist/modules/search-form/flight/flight-filters/RangeBlock.d.ts +1 -0
- package/dist/modules/search-form/flight/flight-filters/RangeBlock.js +5 -3
- package/dist/modules/search-form/flight/flight-results/FlightCard.js +3 -2
- package/dist/modules/search-form/flight/flight-results/FlightPriceBlock.js +4 -1
- package/dist/modules/search-form/flight/flight-results/ProvidersLoader.js +4 -1
- package/dist/modules/search-form/hotel/GuestSelector.js +14 -9
- package/dist/modules/search-form/hotel/HotelLocation.js +6 -2
- package/dist/modules/search-form/hotel/HotelResults.d.ts +1 -0
- package/dist/modules/search-form/hotel/HotelResults.js +32 -6
- package/dist/modules/search-form/hotel/components/HotelCard.d.ts +1 -0
- package/dist/modules/search-form/hotel/components/HotelCard.js +39 -5
- package/dist/modules/search-form/hotel/components/HotelList.d.ts +1 -0
- package/dist/modules/search-form/hotel/components/HotelList.js +2 -2
- package/dist/modules/search-form/hotel/components/HotelMap.js +40 -9
- package/dist/modules/search-form/hotel/hotel-filters/HotelFilters.js +12 -5
- package/index.ts +7 -0
- package/package.json +2 -2
|
@@ -3,9 +3,10 @@ import { View, Text, TouchableOpacity, Switch, StyleSheet, } from "react-native"
|
|
|
3
3
|
import RangeBlock from "./RangeBlock";
|
|
4
4
|
import { Colors, Spacing, FontSize, BorderRadius } from "../../../../theme/colors";
|
|
5
5
|
const SectionTitle = ({ title }) => (_jsx(View, { style: styles.sectionHeader, children: _jsx(Text, { style: styles.sectionTitle, children: title }) }));
|
|
6
|
-
const RadioOption = ({ label, selected, onPress }) => (_jsxs(TouchableOpacity, { style: styles.radioRow, onPress: onPress, activeOpacity: 0.7, children: [_jsx(View, { style: [styles.radioOuter, selected && styles.radioOuterSelected], children: selected && _jsx(View, { style: styles.radioInner }) }), _jsx(Text, { style: styles.radioLabel, children: label })] }));
|
|
7
|
-
const CheckboxOption = ({ label, checked, onPress }) => (_jsxs(TouchableOpacity, { style: styles.checkboxRow, onPress: onPress, activeOpacity: 0.7, children: [_jsx(View, { style: [styles.checkboxOuter, checked && styles.checkboxChecked], children: checked && _jsx(Text, { style: styles.checkboxMark, children: "\u2713" }) }), _jsx(Text, { style: styles.checkboxLabel, children: label })] }));
|
|
8
|
-
const FiltersContent = ({ has, openSections, toggleSection, getValue, handleRangeChange, handleInputChange, getRangeStyle, formatTime, formatDuration, airlines, translations, currencySign, dateTimeFormat, maxDayMinutes, onRangeDragStart, onRangeDragEnd, }) => {
|
|
6
|
+
const RadioOption = ({ label, selected, onPress, color }) => (_jsxs(TouchableOpacity, { style: styles.radioRow, onPress: onPress, activeOpacity: 0.7, children: [_jsx(View, { style: [styles.radioOuter, selected && styles.radioOuterSelected, selected && color ? { borderColor: color } : null], children: selected && _jsx(View, { style: [styles.radioInner, color ? { backgroundColor: color } : null] }) }), _jsx(Text, { style: styles.radioLabel, children: label })] }));
|
|
7
|
+
const CheckboxOption = ({ label, checked, onPress, color }) => (_jsxs(TouchableOpacity, { style: styles.checkboxRow, onPress: onPress, activeOpacity: 0.7, children: [_jsx(View, { style: [styles.checkboxOuter, checked && styles.checkboxChecked, checked && color ? { backgroundColor: color, borderColor: color } : null], children: checked && _jsx(Text, { style: styles.checkboxMark, children: "\u2713" }) }), _jsx(Text, { style: styles.checkboxLabel, children: label })] }));
|
|
8
|
+
const FiltersContent = ({ has, openSections, toggleSection, getValue, handleRangeChange, handleInputChange, getRangeStyle, formatTime, formatDuration, airlines, translations, currencySign, dateTimeFormat, maxDayMinutes, onRangeDragStart, onRangeDragEnd, accentColor, }) => {
|
|
9
|
+
const ac = accentColor || Colors.primary;
|
|
9
10
|
const transferLabels = [
|
|
10
11
|
translations["filter_any"] || "Any",
|
|
11
12
|
translations["filter_nonstop"] || "Nonstop",
|
|
@@ -20,20 +21,20 @@ const FiltersContent = ({ has, openSections, toggleSection, getValue, handleRang
|
|
|
20
21
|
];
|
|
21
22
|
const baggageValues = ["all", "1", "0"];
|
|
22
23
|
const transferValues = ["all", "0", "1", "2", "3"];
|
|
23
|
-
return (_jsxs(View, { style: styles.container, children: [_jsx(Text, { style: styles.title, children: translations["filters_title"] || "Filters" }), (has("minprice") || has("maxprice")) && (_jsx(RangeBlock, { title: translations["filter_price"] || "Price", minName: "minprice", maxName: "maxprice", maxValue: 1000, step: 10, open: openSections.includes("price"), toggle: () => toggleSection("price"), getValue: getValue, handleRangeChange: handleRangeChange, getRangeStyle: getRangeStyle, formatter: (v) => `${v} ${currencySign}`, isShow: true, onDragStart: onRangeDragStart, onDragEnd: onRangeDragEnd })), has("baggage") && (_jsxs(View, { style: styles.section, children: [_jsx(SectionTitle, { title: translations["filter_baggage"] || "Baggage" }), _jsx(View, { style: styles.sectionContent, children: baggageLabels.map((label, i) => (_jsx(RadioOption, { label: label, selected: getValue("baggage", "all") === baggageValues[i], onPress: () => handleInputChange("baggage", baggageValues[i]) }, i))) })] })), has("transfer") && (_jsxs(View, { style: styles.section, children: [_jsx(SectionTitle, { title: translations["filter_transfer_outbound"] || "Stops (outbound)" }), _jsx(View, { style: styles.sectionContent, children: transferLabels.map((label, i) => (_jsx(RadioOption, { label: label, selected: getValue("transfer", "all") === transferValues[i], onPress: () => handleInputChange("transfer", transferValues[i]) }, i))) })] })), has("transferReturn") && (_jsxs(View, { style: styles.section, children: [_jsx(SectionTitle, { title: translations["filter_transfer_return"] || "Stops (return)" }), _jsx(View, { style: styles.sectionContent, children: transferLabels.map((label, i) => (_jsx(RadioOption, { label: label, selected: getValue("transferReturn", "all") === transferValues[i], onPress: () => handleInputChange("transferReturn", transferValues[i]) }, i))) })] })), has("charter") && (_jsxs(View, { style: styles.section, children: [_jsx(SectionTitle, { title: translations["filter_charter"] || "Charter" }), _jsxs(View, { style: styles.toggleRow, children: [_jsx(Text, { style: styles.toggleLabel, children: translations["filter_charter_toggle"] || "Charter" }), _jsx(Switch, { value: !!getValue("charter", 0), onValueChange: (val) => handleInputChange("charter", val ? 1 : 0), trackColor: { false: Colors.border, true:
|
|
24
|
-
"No airport change" }), _jsx(Switch, { value: !!getValue("changeAirport", 0), onValueChange: (val) => handleInputChange("changeAirport", val ? 1 : 0), trackColor: { false: Colors.border, true:
|
|
25
|
-
"Departure time (outbound)", minName: "departureMin", maxName: "departureMax", maxValue: maxDayMinutes, step: 15, open: openSections.includes("departure"), toggle: () => toggleSection("departure"), getValue: getValue, handleRangeChange: handleRangeChange, getRangeStyle: getRangeStyle, formatter: formatTime, isShow: false, onDragStart: onRangeDragStart, onDragEnd: onRangeDragEnd })), (has("arrivalMin") || has("arrivalMax")) && (_jsx(RangeBlock, { title: translations["filter_arrival_outbound"] ||
|
|
26
|
-
"Arrival time (outbound)", minName: "arrivalMin", maxName: "arrivalMax", maxValue: maxDayMinutes, step: 15, open: openSections.includes("arrival"), toggle: () => toggleSection("arrival"), getValue: getValue, handleRangeChange: handleRangeChange, getRangeStyle: getRangeStyle, formatter: formatTime, isShow: false, onDragStart: onRangeDragStart, onDragEnd: onRangeDragEnd })), (has("returnMin") || has("returnMax")) && (_jsx(RangeBlock, { title: translations["filter_departure_return"] ||
|
|
27
|
-
"Departure time (return)", minName: "returnMin", maxName: "returnMax", maxValue: maxDayMinutes, step: 15, open: openSections.includes("return"), toggle: () => toggleSection("return"), getValue: getValue, handleRangeChange: handleRangeChange, getRangeStyle: getRangeStyle, formatter: formatTime, isShow: false, onDragStart: onRangeDragStart, onDragEnd: onRangeDragEnd })), (has("returnArrivalMin") || has("returnArrivalMax")) && (_jsx(RangeBlock, { title: translations["filter_arrival_return"] ||
|
|
28
|
-
"Arrival time (return)", minName: "returnArrivalMin", maxName: "returnArrivalMax", maxValue: maxDayMinutes, step: 15, open: openSections.includes("returnArrival"), toggle: () => toggleSection("returnArrival"), getValue: getValue, handleRangeChange: handleRangeChange, getRangeStyle: getRangeStyle, formatter: formatTime, isShow: false, onDragStart: onRangeDragStart, onDragEnd: onRangeDragEnd })), (has("outboundStopTimeMin") || has("outboundStopTimeMax")) && (_jsx(RangeBlock, { title: translations["filter_stop_duration_outbound"] ||
|
|
29
|
-
"Stop duration (outbound)", minName: "outboundStopTimeMin", maxName: "outboundStopTimeMax", maxValue: maxDayMinutes, step: 15, open: openSections.includes("outbound"), toggle: () => toggleSection("outbound"), getValue: getValue, handleRangeChange: handleRangeChange, getRangeStyle: getRangeStyle, formatter: formatDuration, isShow: false, onDragStart: onRangeDragStart, onDragEnd: onRangeDragEnd })), (has("inboundStopTimeMin") || has("inboundStopTimeMax")) && (_jsx(RangeBlock, { title: translations["filter_stop_duration_inbound"] ||
|
|
30
|
-
"Stop duration (return)", minName: "inboundStopTimeMin", maxName: "inboundStopTimeMax", maxValue: maxDayMinutes, step: 15, open: openSections.includes("inbound"), toggle: () => toggleSection("inbound"), getValue: getValue, handleRangeChange: handleRangeChange, getRangeStyle: getRangeStyle, formatter: formatDuration, isShow: false, onDragStart: onRangeDragStart, onDragEnd: onRangeDragEnd })), (has("durationMin") || has("durationMax")) && (_jsx(RangeBlock, { title: translations["filter_total_stop_duration"] ||
|
|
31
|
-
"Total stop duration", minName: "durationMin", maxName: "durationMax", maxValue: maxDayMinutes, step: 15, open: openSections.includes("duration"), toggle: () => toggleSection("duration"), getValue: getValue, handleRangeChange: handleRangeChange, getRangeStyle: getRangeStyle, formatter: formatDuration, isShow: false, onDragStart: onRangeDragStart, onDragEnd: onRangeDragEnd })), has("airline") && (_jsxs(View, { style: styles.section, children: [_jsx(SectionTitle, { title: translations["filter_airlines"] || "Airlines" }), _jsxs(View, { style: styles.sectionContent, children: [Object.entries(airlines).map(([code, name]) => (_jsx(CheckboxOption, { label: name, checked: Array.isArray(getValue("airline", [])) &&
|
|
24
|
+
return (_jsxs(View, { style: styles.container, children: [_jsx(Text, { style: styles.title, children: translations["filters_title"] || "Filters" }), (has("minprice") || has("maxprice")) && (_jsx(RangeBlock, { title: translations["filter_price"] || "Price", minName: "minprice", maxName: "maxprice", maxValue: 1000, step: 10, open: openSections.includes("price"), toggle: () => toggleSection("price"), getValue: getValue, handleRangeChange: handleRangeChange, getRangeStyle: getRangeStyle, formatter: (v) => `${v} ${currencySign}`, isShow: true, onDragStart: onRangeDragStart, onDragEnd: onRangeDragEnd, accentColor: ac })), has("baggage") && (_jsxs(View, { style: styles.section, children: [_jsx(SectionTitle, { title: translations["filter_baggage"] || "Baggage" }), _jsx(View, { style: styles.sectionContent, children: baggageLabels.map((label, i) => (_jsx(RadioOption, { label: label, selected: getValue("baggage", "all") === baggageValues[i], onPress: () => handleInputChange("baggage", baggageValues[i]), color: ac }, i))) })] })), has("transfer") && (_jsxs(View, { style: styles.section, children: [_jsx(SectionTitle, { title: translations["filter_transfer_outbound"] || "Stops (outbound)" }), _jsx(View, { style: styles.sectionContent, children: transferLabels.map((label, i) => (_jsx(RadioOption, { label: label, selected: getValue("transfer", "all") === transferValues[i], onPress: () => handleInputChange("transfer", transferValues[i]), color: ac }, i))) })] })), has("transferReturn") && (_jsxs(View, { style: styles.section, children: [_jsx(SectionTitle, { title: translations["filter_transfer_return"] || "Stops (return)" }), _jsx(View, { style: styles.sectionContent, children: transferLabels.map((label, i) => (_jsx(RadioOption, { label: label, selected: getValue("transferReturn", "all") === transferValues[i], onPress: () => handleInputChange("transferReturn", transferValues[i]), color: ac }, i))) })] })), has("charter") && (_jsxs(View, { style: styles.section, children: [_jsx(SectionTitle, { title: translations["filter_charter"] || "Charter" }), _jsxs(View, { style: styles.toggleRow, children: [_jsx(Text, { style: styles.toggleLabel, children: translations["filter_charter_toggle"] || "Charter" }), _jsx(Switch, { value: !!getValue("charter", 0), onValueChange: (val) => handleInputChange("charter", val ? 1 : 0), trackColor: { false: Colors.border, true: ac }, thumbColor: Colors.white, ios_backgroundColor: Colors.border })] })] })), has("changeAirport") && (_jsxs(View, { style: styles.section, children: [_jsx(SectionTitle, { title: translations["filter_no_airport_change"] || "No airport change" }), _jsxs(View, { style: styles.toggleRow, children: [_jsx(Text, { style: styles.toggleLabel, children: translations["filter_no_airport_change"] ||
|
|
25
|
+
"No airport change" }), _jsx(Switch, { value: !!getValue("changeAirport", 0), onValueChange: (val) => handleInputChange("changeAirport", val ? 1 : 0), trackColor: { false: Colors.border, true: ac }, thumbColor: Colors.white, ios_backgroundColor: Colors.border })] })] })), (has("departureMin") || has("departureMax")) && (_jsx(RangeBlock, { title: translations["filter_departure_outbound"] ||
|
|
26
|
+
"Departure time (outbound)", minName: "departureMin", maxName: "departureMax", maxValue: maxDayMinutes, step: 15, open: openSections.includes("departure"), toggle: () => toggleSection("departure"), getValue: getValue, handleRangeChange: handleRangeChange, getRangeStyle: getRangeStyle, formatter: formatTime, isShow: false, onDragStart: onRangeDragStart, onDragEnd: onRangeDragEnd, accentColor: ac })), (has("arrivalMin") || has("arrivalMax")) && (_jsx(RangeBlock, { title: translations["filter_arrival_outbound"] ||
|
|
27
|
+
"Arrival time (outbound)", minName: "arrivalMin", maxName: "arrivalMax", maxValue: maxDayMinutes, step: 15, open: openSections.includes("arrival"), toggle: () => toggleSection("arrival"), getValue: getValue, handleRangeChange: handleRangeChange, getRangeStyle: getRangeStyle, formatter: formatTime, isShow: false, onDragStart: onRangeDragStart, onDragEnd: onRangeDragEnd, accentColor: ac })), (has("returnMin") || has("returnMax")) && (_jsx(RangeBlock, { title: translations["filter_departure_return"] ||
|
|
28
|
+
"Departure time (return)", minName: "returnMin", maxName: "returnMax", maxValue: maxDayMinutes, step: 15, open: openSections.includes("return"), toggle: () => toggleSection("return"), getValue: getValue, handleRangeChange: handleRangeChange, getRangeStyle: getRangeStyle, formatter: formatTime, isShow: false, onDragStart: onRangeDragStart, onDragEnd: onRangeDragEnd, accentColor: ac })), (has("returnArrivalMin") || has("returnArrivalMax")) && (_jsx(RangeBlock, { title: translations["filter_arrival_return"] ||
|
|
29
|
+
"Arrival time (return)", minName: "returnArrivalMin", maxName: "returnArrivalMax", maxValue: maxDayMinutes, step: 15, open: openSections.includes("returnArrival"), toggle: () => toggleSection("returnArrival"), getValue: getValue, handleRangeChange: handleRangeChange, getRangeStyle: getRangeStyle, formatter: formatTime, isShow: false, onDragStart: onRangeDragStart, onDragEnd: onRangeDragEnd, accentColor: ac })), (has("outboundStopTimeMin") || has("outboundStopTimeMax")) && (_jsx(RangeBlock, { title: translations["filter_stop_duration_outbound"] ||
|
|
30
|
+
"Stop duration (outbound)", minName: "outboundStopTimeMin", maxName: "outboundStopTimeMax", maxValue: maxDayMinutes, step: 15, open: openSections.includes("outbound"), toggle: () => toggleSection("outbound"), getValue: getValue, handleRangeChange: handleRangeChange, getRangeStyle: getRangeStyle, formatter: formatDuration, isShow: false, onDragStart: onRangeDragStart, onDragEnd: onRangeDragEnd, accentColor: ac })), (has("inboundStopTimeMin") || has("inboundStopTimeMax")) && (_jsx(RangeBlock, { title: translations["filter_stop_duration_inbound"] ||
|
|
31
|
+
"Stop duration (return)", minName: "inboundStopTimeMin", maxName: "inboundStopTimeMax", maxValue: maxDayMinutes, step: 15, open: openSections.includes("inbound"), toggle: () => toggleSection("inbound"), getValue: getValue, handleRangeChange: handleRangeChange, getRangeStyle: getRangeStyle, formatter: formatDuration, isShow: false, onDragStart: onRangeDragStart, onDragEnd: onRangeDragEnd, accentColor: ac })), (has("durationMin") || has("durationMax")) && (_jsx(RangeBlock, { title: translations["filter_total_stop_duration"] ||
|
|
32
|
+
"Total stop duration", minName: "durationMin", maxName: "durationMax", maxValue: maxDayMinutes, step: 15, open: openSections.includes("duration"), toggle: () => toggleSection("duration"), getValue: getValue, handleRangeChange: handleRangeChange, getRangeStyle: getRangeStyle, formatter: formatDuration, isShow: false, onDragStart: onRangeDragStart, onDragEnd: onRangeDragEnd, accentColor: ac })), has("airline") && (_jsxs(View, { style: styles.section, children: [_jsx(SectionTitle, { title: translations["filter_airlines"] || "Airlines" }), _jsxs(View, { style: styles.sectionContent, children: [Object.entries(airlines).map(([code, name]) => (_jsx(CheckboxOption, { label: name, checked: Array.isArray(getValue("airline", [])) &&
|
|
32
33
|
getValue("airline", []).includes(code), onPress: () => handleInputChange("airline", {
|
|
33
34
|
code,
|
|
34
35
|
checked: !(Array.isArray(getValue("airline", [])) &&
|
|
35
36
|
getValue("airline", []).includes(code)),
|
|
36
|
-
}) }, code))), _jsx(TouchableOpacity, { style: styles.clearButton, onPress: () => handleInputChange("airline", []), activeOpacity: 0.7, children: _jsx(Text, { style: styles.clearButtonText, children: translations["clear_all"] || "Clear all" }) })] })] }))] }));
|
|
37
|
+
}), color: ac }, code))), _jsx(TouchableOpacity, { style: styles.clearButton, onPress: () => handleInputChange("airline", []), activeOpacity: 0.7, children: _jsx(Text, { style: styles.clearButtonText, children: translations["clear_all"] || "Clear all" }) })] })] }))] }));
|
|
37
38
|
};
|
|
38
39
|
const styles = StyleSheet.create({
|
|
39
40
|
container: {
|
|
@@ -13,7 +13,8 @@ const FlightFiltersDynamic = ({ filters = [], airlines = {}, loading = false, se
|
|
|
13
13
|
const [openSections, setOpenSections] = useState([]);
|
|
14
14
|
const [isPopupOpen, setIsPopupOpen] = useState(false);
|
|
15
15
|
const [scrollEnabled, setScrollEnabled] = useState(true);
|
|
16
|
-
const { currency, dateTimeFormat } = useWidget();
|
|
16
|
+
const { currency, dateTimeFormat, colors } = useWidget();
|
|
17
|
+
const btnColor = colors.primary || Colors.primary;
|
|
17
18
|
const currencySigns = {
|
|
18
19
|
USD: "$",
|
|
19
20
|
EUR: "\u20AC",
|
|
@@ -122,7 +123,7 @@ const FlightFiltersDynamic = ({ filters = [], airlines = {}, loading = false, se
|
|
|
122
123
|
}
|
|
123
124
|
if (!filters?.length || !sessionId)
|
|
124
125
|
return null;
|
|
125
|
-
return (_jsxs(View, { children: [_jsxs(TouchableOpacity, { style: styles.filterButton, onPress: () => setIsPopupOpen(true), activeOpacity: 0.7, children: [_jsxs(Svg, { width: 24, height: 20, viewBox: "0 0 24 20", fill: "none", children: [_jsx(Path, { d: "M18.5 4.1665H5.5C4.67157 4.1665 4 4.72615 4 5.4165C4 6.10686 4.67157 6.6665 5.5 6.6665H18.5C19.3284 6.6665 20 6.10686 20 5.4165C20 4.72615 19.3284 4.1665 18.5 4.1665Z", fill:
|
|
126
|
+
return (_jsxs(View, { children: [_jsxs(TouchableOpacity, { style: styles.filterButton, onPress: () => setIsPopupOpen(true), activeOpacity: 0.7, children: [_jsxs(Svg, { width: 24, height: 20, viewBox: "0 0 24 20", fill: "none", children: [_jsx(Path, { d: "M18.5 4.1665H5.5C4.67157 4.1665 4 4.72615 4 5.4165C4 6.10686 4.67157 6.6665 5.5 6.6665H18.5C19.3284 6.6665 20 6.10686 20 5.4165C20 4.72615 19.3284 4.1665 18.5 4.1665Z", fill: btnColor }), _jsx(Path, { opacity: 0.3, fillRule: "evenodd", clipRule: "evenodd", d: "M7.5 9.1665H16.5C17.3284 9.1665 18 9.72615 18 10.4165C18 11.1069 17.3284 11.6665 16.5 11.6665H7.5C6.67157 11.6665 6 11.1069 6 10.4165C6 9.72615 6.67157 9.1665 7.5 9.1665ZM10.5 14.1665H13.5C14.3284 14.1665 15 14.7261 15 15.4165C15 16.1069 14.3284 16.6665 13.5 16.6665H10.5C9.67157 16.6665 9 16.1069 9 15.4165C9 14.7261 9.67157 14.1665 10.5 14.1665Z", fill: btnColor })] }), _jsx(Text, { style: styles.filterButtonText, children: translations["filters_title"] || "Filters" })] }), _jsx(Modal, { visible: isPopupOpen, animationType: "slide", presentationStyle: "fullScreen", onRequestClose: () => setIsPopupOpen(false), children: _jsxs(View, { style: [styles.modalContainer, { paddingTop: insets.top }], children: [_jsx(TouchableOpacity, { style: styles.closeButton, onPress: () => setIsPopupOpen(false), hitSlop: { top: 8, bottom: 8, left: 8, right: 8 }, children: _jsx(CloseIcon, { size: 16 }) }), _jsx(View, { style: styles.modalHeader, children: _jsx(Text, { style: styles.modalTitle, children: translations["filters_title"] || "Filters" }) }), _jsx(ScrollView, { style: styles.modalContent, contentContainerStyle: styles.modalContentInner, showsVerticalScrollIndicator: false, scrollEnabled: scrollEnabled, children: _jsx(FiltersContent, { has: has, openSections: openSections, toggleSection: toggleSection, getValue: getValue, handleRangeChange: handleRangeChange, handleInputChange: handleInputChange, getRangeStyle: getRangeStyle, formatTime: formatTime, formatDuration: formatDuration, airlines: airlines, translations: translations, currencySign: currencySign, dateTimeFormat: dateTimeFormat, maxDayMinutes: MAX_DAY_MINUTES, onRangeDragStart: () => setScrollEnabled(false), onRangeDragEnd: () => setScrollEnabled(true), accentColor: btnColor }) }), _jsxs(View, { style: styles.modalFooter, children: [_jsx(TouchableOpacity, { style: styles.resetButton, onPress: handleReset, activeOpacity: 0.7, children: _jsx(Text, { style: styles.resetButtonText, children: translations["filter_reset_btn"] || "Reset" }) }), _jsx(TouchableOpacity, { style: [styles.applyButton, { backgroundColor: btnColor }], onPress: handleApply, activeOpacity: 0.8, children: _jsx(Text, { style: styles.applyButtonText, children: translations["filter_apply_btn"] || "Apply" }) })] })] }) })] }));
|
|
126
127
|
};
|
|
127
128
|
const styles = StyleSheet.create({
|
|
128
129
|
loadingContainer: {
|
|
@@ -4,7 +4,8 @@ import { View, Text, PanResponder, StyleSheet, } from "react-native";
|
|
|
4
4
|
import { Colors, Spacing, FontSize, BorderRadius } from "../../../../theme/colors";
|
|
5
5
|
const THUMB_SIZE = 28;
|
|
6
6
|
const TRACK_HEIGHT = 4;
|
|
7
|
-
const RangeBlock = ({ title, minName, maxName, maxValue, step, getValue, handleRangeChange, formatter, onDragStart, onDragEnd, }) => {
|
|
7
|
+
const RangeBlock = ({ title, minName, maxName, maxValue, step, getValue, handleRangeChange, formatter, onDragStart, onDragEnd, accentColor, }) => {
|
|
8
|
+
const ac = accentColor || Colors.primary;
|
|
8
9
|
const rawMin = getValue(minName, 0);
|
|
9
10
|
const rawMax = getValue(maxName, maxValue);
|
|
10
11
|
const currentMin = typeof rawMin === "string" ? parseFloat(rawMin) : rawMin;
|
|
@@ -91,13 +92,14 @@ const RangeBlock = ({ title, minName, maxName, maxValue, step, getValue, handleR
|
|
|
91
92
|
{
|
|
92
93
|
left: `${minPercent}%`,
|
|
93
94
|
right: `${100 - maxPercent}%`,
|
|
95
|
+
backgroundColor: ac,
|
|
94
96
|
},
|
|
95
97
|
] }), _jsx(View, { style: [
|
|
96
98
|
styles.thumb,
|
|
97
|
-
{ left: `${minPercent}%`, marginLeft: -THUMB_SIZE / 2 },
|
|
99
|
+
{ left: `${minPercent}%`, marginLeft: -THUMB_SIZE / 2, borderColor: ac },
|
|
98
100
|
], hitSlop: { top: 12, bottom: 12, left: 12, right: 12 }, ...minPanResponder.panHandlers }), _jsx(View, { style: [
|
|
99
101
|
styles.thumb,
|
|
100
|
-
{ left: `${maxPercent}%`, marginLeft: -THUMB_SIZE / 2 },
|
|
102
|
+
{ left: `${maxPercent}%`, marginLeft: -THUMB_SIZE / 2, borderColor: ac },
|
|
101
103
|
], hitSlop: { top: 12, bottom: 12, left: 12, right: 12 }, ...maxPanResponder.panHandlers })] })] })] }));
|
|
102
104
|
};
|
|
103
105
|
const styles = StyleSheet.create({
|
|
@@ -101,7 +101,8 @@ const FlightCard = ({ item, loading, dictionaries, airlines, sessionId, translat
|
|
|
101
101
|
setTooltipData(null);
|
|
102
102
|
}, []);
|
|
103
103
|
const chevronRotation = useRef(new Animated.Value(0)).current;
|
|
104
|
-
const { config, dateTimeFormat } = useWidget();
|
|
104
|
+
const { config, dateTimeFormat, colors } = useWidget();
|
|
105
|
+
const btnColor = colors.primary || Colors.primary;
|
|
105
106
|
const formatDuration = (dur) => {
|
|
106
107
|
if (!dur)
|
|
107
108
|
return "\u2014";
|
|
@@ -239,7 +240,7 @@ const FlightCard = ({ item, loading, dictionaries, airlines, sessionId, translat
|
|
|
239
240
|
], children: itinerary?.transfers > 0
|
|
240
241
|
? `${itinerary.transfers} ${translations["transfers"] || "Stops"}`
|
|
241
242
|
: translations["direct_flight"] || "Direct" })] }), itinerary?.segments?.length ? (_jsx(FlightSegments, { segments: itinerary.segments, dictionaries: dictionaries, lang: lang, translations: translations, onShowTooltip: showTooltip, onDismissTooltip: dismissTooltip, activeTooltip: tooltipData?.label })) : (_jsx(Text, { style: styles.noSegmentsText, children: translations["no_segments"] || "No segment data" }))] }), _jsxs(View, { style: [styles.timeBlock, styles.timeBlockRight], children: [_jsx(Text, { style: styles.timeText, children: formatTimeByDateTimeFormat(lastSegment?.arrival?.at, dateTimeFormat) }), _jsx(Text, { style: styles.cityText, numberOfLines: 1, children: arrCity }), _jsx(Text, { style: styles.dateText, children: arrDate })] })] }) }, i));
|
|
242
|
-
}), _jsxs(View, { style: styles.priceSection, children: [_jsx(FlightBaggageToggle, { flight: flight, includeBaggage: includeBaggage, setIncludeBaggage: setIncludeBaggage, setDynamicPrice: setDynamicPrice, setBaggageText: setBaggageText, setBaggageImage: setBaggageImage, setBuyHref: setBuyHref, sessionId: sessionId, translations: translations, lang: lang }), _jsx(TouchableOpacity, { style: [styles.buyButton, loading && styles.buyButtonLoading], onPress: handleBuy, activeOpacity: 0.8, children: _jsxs(Text, { style: styles.buyButtonText, children: [translations["buy_for"] || "Buy for", " ", formattedText] }) }), availableSeats > 0 && (_jsxs(Text, { style: styles.seatsText, children: [translations["remaining"] || "Remaining", ": ", availableSeats, " ", translations["seats"] || "seats"] }))] }), _jsx(TouchableOpacity, { style: styles.detailsButton, onPress: toggleDetails, activeOpacity: 0.7, children: _jsx(Animated.View, { style: {
|
|
243
|
+
}), _jsxs(View, { style: styles.priceSection, children: [_jsx(FlightBaggageToggle, { flight: flight, includeBaggage: includeBaggage, setIncludeBaggage: setIncludeBaggage, setDynamicPrice: setDynamicPrice, setBaggageText: setBaggageText, setBaggageImage: setBaggageImage, setBuyHref: setBuyHref, sessionId: sessionId, translations: translations, lang: lang }), _jsx(TouchableOpacity, { style: [styles.buyButton, { backgroundColor: btnColor }, loading && styles.buyButtonLoading], onPress: handleBuy, activeOpacity: 0.8, children: _jsxs(Text, { style: styles.buyButtonText, children: [translations["buy_for"] || "Buy for", " ", formattedText] }) }), availableSeats > 0 && (_jsxs(Text, { style: styles.seatsText, children: [translations["remaining"] || "Remaining", ": ", availableSeats, " ", translations["seats"] || "seats"] }))] }), _jsx(TouchableOpacity, { style: styles.detailsButton, onPress: toggleDetails, activeOpacity: 0.7, children: _jsx(Animated.View, { style: {
|
|
243
244
|
transform: [{ rotate: chevronRotateInterpolation }],
|
|
244
245
|
}, children: _jsx(Svg, { width: 18, height: 18, viewBox: "0 0 24 24", fill: "none", children: _jsx(Path, { fillRule: "evenodd", clipRule: "evenodd", d: "M3.05727 7.05687C3.57797 6.53617 4.42219 6.53617 4.94289 7.05687L12.0001 14.1141L19.0573 7.05687C19.578 6.53617 20.4222 6.53617 20.9429 7.05687C21.4636 7.57756 21.4636 8.42178 20.9429 8.94248L12.9429 16.9425C12.4222 17.4632 11.578 17.4632 11.0573 16.9425L3.05727 8.94248C2.53657 8.42179 2.53657 7.57757 3.05727 7.05687Z", fill: "#5B5D6C" }) }) }) }), _jsx(FlightAdditionalInfo, { show: showDetails, itineraries: itineraries, airlines: {
|
|
245
246
|
[flight.airline]: {
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { View, Text, TouchableOpacity, StyleSheet } from "react-native";
|
|
3
|
+
import { useWidget } from "../../../../context/WidgetContext";
|
|
3
4
|
import { Colors, Spacing, FontSize, BorderRadius } from "../../../../theme/colors";
|
|
4
5
|
const FlightPriceBlock = ({ price, currencySign = "", loading = false, onBuy, availableSeats = 0, translations, }) => {
|
|
6
|
+
const { colors } = useWidget();
|
|
7
|
+
const btnColor = colors.primary || Colors.primary;
|
|
5
8
|
const formattedPrice = Number(price || 0).toFixed(2);
|
|
6
9
|
const formattedText = `${currencySign}${formattedPrice}`;
|
|
7
|
-
return (_jsxs(View, { style: styles.container, children: [_jsx(TouchableOpacity, { style: [styles.buyButton, loading && styles.buyButtonLoading], onPress: onBuy, activeOpacity: 0.8, disabled: loading, children: _jsxs(Text, { style: styles.buyButtonText, children: [translations["buy_for"] || "Buy for", " ", formattedText] }) }), availableSeats > 0 && (_jsxs(Text, { style: styles.seatsText, children: [translations["remaining"] || "Remaining", ": ", availableSeats, " ", translations["seats"] || "seats"] }))] }));
|
|
10
|
+
return (_jsxs(View, { style: styles.container, children: [_jsx(TouchableOpacity, { style: [styles.buyButton, { backgroundColor: btnColor }, loading && styles.buyButtonLoading], onPress: onBuy, activeOpacity: 0.8, disabled: loading, children: _jsxs(Text, { style: styles.buyButtonText, children: [translations["buy_for"] || "Buy for", " ", formattedText] }) }), availableSeats > 0 && (_jsxs(Text, { style: styles.seatsText, children: [translations["remaining"] || "Remaining", ": ", availableSeats, " ", translations["seats"] || "seats"] }))] }));
|
|
8
11
|
};
|
|
9
12
|
const styles = StyleSheet.create({
|
|
10
13
|
container: {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useState, useRef } from "react";
|
|
3
3
|
import { View, Text, Animated, StyleSheet } from "react-native";
|
|
4
|
+
import { useWidget } from "../../../../context/WidgetContext";
|
|
4
5
|
import { Colors, Spacing, FontSize, BorderRadius } from "../../../../theme/colors";
|
|
5
6
|
const AIRLINES = [
|
|
6
7
|
"Lufthansa",
|
|
@@ -79,6 +80,8 @@ function shuffleArray(arr) {
|
|
|
79
80
|
return a;
|
|
80
81
|
}
|
|
81
82
|
const ProvidersLoader = ({ active, type = "flights", translations, }) => {
|
|
83
|
+
const { colors } = useWidget();
|
|
84
|
+
const btnColor = colors.primary || Colors.primary;
|
|
82
85
|
const configMap = {
|
|
83
86
|
flights: {
|
|
84
87
|
title: translations["loader_flights_title"] || "Searching flights",
|
|
@@ -179,7 +182,7 @@ const ProvidersLoader = ({ active, type = "flights", translations, }) => {
|
|
|
179
182
|
inputRange: [0, 1],
|
|
180
183
|
outputRange: ["0%", "100%"],
|
|
181
184
|
});
|
|
182
|
-
return (_jsx(Animated.View, { style: [styles.container, { opacity: fadeAnim }], children: _jsxs(View, { style: styles.content, children: [_jsxs(View, { style: styles.headerRow, children: [_jsx(View, { style: styles.counterBadge, children: _jsx(Text, { style: styles.counterText, children: counter }) }), _jsxs(View, { style: styles.textBlock, children: [_jsx(Text, { style: styles.title, children: config.title }), _jsxs(Animated.Text, { style: [styles.subtitle, { opacity: nameOpacity }], children: [config.subtitlePrefix, " ", names[index], "..."] })] })] }), _jsx(View, { style: styles.progressBackground, children: _jsx(Animated.View, { style: [styles.progressBar, { width: progressWidth }] }) })] }) }));
|
|
185
|
+
return (_jsx(Animated.View, { style: [styles.container, { opacity: fadeAnim }], children: _jsxs(View, { style: styles.content, children: [_jsxs(View, { style: styles.headerRow, children: [_jsx(View, { style: [styles.counterBadge, { backgroundColor: btnColor + '1A' }], children: _jsx(Text, { style: [styles.counterText, { color: btnColor }], children: counter }) }), _jsxs(View, { style: styles.textBlock, children: [_jsx(Text, { style: styles.title, children: config.title }), _jsxs(Animated.Text, { style: [styles.subtitle, { opacity: nameOpacity }], children: [config.subtitlePrefix, " ", names[index], "..."] })] })] }), _jsx(View, { style: styles.progressBackground, children: _jsx(Animated.View, { style: [styles.progressBar, { width: progressWidth, backgroundColor: btnColor }] }) })] }) }));
|
|
183
186
|
};
|
|
184
187
|
const styles = StyleSheet.create({
|
|
185
188
|
container: {
|
|
@@ -3,13 +3,17 @@ import { useState, useEffect } from "react";
|
|
|
3
3
|
import { View, Text, TouchableOpacity, Modal, ScrollView, StyleSheet, } from "react-native";
|
|
4
4
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
5
5
|
import Svg, { Path } from "react-native-svg";
|
|
6
|
+
import { useWidget } from "../../../context/WidgetContext";
|
|
6
7
|
import { Colors, Spacing, BorderRadius, FontSize } from "../../../theme/colors";
|
|
7
8
|
import { CloseIcon } from "../../../store";
|
|
8
9
|
import { formStyles } from "../../../theme/formStyles";
|
|
9
|
-
const ChevronDown = () => (_jsx(Svg, { width: 20, height: 20, viewBox: "0 0 20 20", fill: "none", children: _jsx(Path, { d: "M5 8L10 12.5L15 8", stroke:
|
|
10
|
+
const ChevronDown = ({ color = Colors.primary }) => (_jsx(Svg, { width: 20, height: 20, viewBox: "0 0 20 20", fill: "none", children: _jsx(Path, { d: "M5 8L10 12.5L15 8", stroke: color, strokeWidth: 2, strokeLinecap: "round" }) }));
|
|
10
11
|
const MAX_GUESTS = 20;
|
|
11
12
|
const AGE_OPTIONS = Array.from({ length: 18 }, (_, i) => i);
|
|
12
13
|
const GuestSelector = ({ value = [{ adults: 2, children: 0, childrenAges: [] }], onChange, translations, }) => {
|
|
14
|
+
const { colors } = useWidget();
|
|
15
|
+
const btnColor = colors.primary || Colors.primary;
|
|
16
|
+
const inputBg = colors.input || Colors.inputBg;
|
|
13
17
|
const insets = useSafeAreaInsets();
|
|
14
18
|
const [rooms, setRooms] = useState(value);
|
|
15
19
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
@@ -64,29 +68,30 @@ const GuestSelector = ({ value = [{ adults: 2, children: 0, childrenAges: [] }],
|
|
|
64
68
|
const handleSave = () => {
|
|
65
69
|
setIsModalOpen(false);
|
|
66
70
|
};
|
|
67
|
-
return (_jsxs(View, { children: [_jsxs(TouchableOpacity, { style: formStyles.inputTrigger, onPress: () => setIsModalOpen(true), activeOpacity: 0.8, children: [_jsx(Text, { style: formStyles.label, children: translations["hotel_form_guests"] || "Guests" }), _jsxs(View, { style: formStyles.inputTriggerRow, children: [_jsx(Text, { style: styles.selectorText, numberOfLines: 1, children: buttonText }), _jsx(ChevronDown, {})] })] }), _jsx(Modal, { visible: isModalOpen, animationType: "slide", presentationStyle: "fullScreen", onRequestClose: () => setIsModalOpen(false), children: _jsxs(View, { style: [styles.modalContainer, { paddingTop: insets.top }], children: [_jsx(TouchableOpacity, { style: styles.closeButton, onPress: () => setIsModalOpen(false), hitSlop: { top: 8, bottom: 8, left: 8, right: 8 }, children: _jsx(CloseIcon, { size: 16 }) }), _jsx(View, { style: styles.modalHeaderRow, children: _jsx(Text, { style: styles.modalTitle, children: translations["hotel_form_guests"] || "Guests" }) }), _jsxs(ScrollView, { style: styles.scrollView, contentContainerStyle: styles.scrollContent, showsVerticalScrollIndicator: false, children: [rooms.map((room, index) => (_jsxs(View, { style: styles.roomBlock, children: [_jsxs(View, { style: styles.roomHeader, children: [_jsxs(Text, { style: styles.roomTitle, children: [translations["hotel_room_label"] || "Room", " ", index + 1] }), rooms.length > 1 && (_jsx(TouchableOpacity, { onPress: () => removeRoom(index), activeOpacity: 0.7, children: _jsx(Text, { style: styles.removeText, children: translations["hotel_remove_room"] || "Remove" }) }))] }), _jsxs(View, { style: styles.counterRow, children: [_jsx(Text, { style: styles.counterLabel, children: translations["hotel_adults_title"] || "Adults" }), _jsxs(View, { style: styles.counterControls, children: [_jsx(TouchableOpacity, { style: [
|
|
71
|
+
return (_jsxs(View, { children: [_jsxs(TouchableOpacity, { style: [formStyles.inputTrigger, { backgroundColor: inputBg }], onPress: () => setIsModalOpen(true), activeOpacity: 0.8, children: [_jsx(Text, { style: formStyles.label, children: translations["hotel_form_guests"] || "Guests" }), _jsxs(View, { style: formStyles.inputTriggerRow, children: [_jsx(Text, { style: styles.selectorText, numberOfLines: 1, children: buttonText }), _jsx(ChevronDown, { color: btnColor })] })] }), _jsx(Modal, { visible: isModalOpen, animationType: "slide", presentationStyle: "fullScreen", onRequestClose: () => setIsModalOpen(false), children: _jsxs(View, { style: [styles.modalContainer, { paddingTop: insets.top }], children: [_jsx(TouchableOpacity, { style: styles.closeButton, onPress: () => setIsModalOpen(false), hitSlop: { top: 8, bottom: 8, left: 8, right: 8 }, children: _jsx(CloseIcon, { size: 16 }) }), _jsx(View, { style: styles.modalHeaderRow, children: _jsx(Text, { style: styles.modalTitle, children: translations["hotel_form_guests"] || "Guests" }) }), _jsxs(ScrollView, { style: styles.scrollView, contentContainerStyle: styles.scrollContent, showsVerticalScrollIndicator: false, children: [rooms.map((room, index) => (_jsxs(View, { style: styles.roomBlock, children: [_jsxs(View, { style: styles.roomHeader, children: [_jsxs(Text, { style: styles.roomTitle, children: [translations["hotel_room_label"] || "Room", " ", index + 1] }), rooms.length > 1 && (_jsx(TouchableOpacity, { onPress: () => removeRoom(index), activeOpacity: 0.7, children: _jsx(Text, { style: styles.removeText, children: translations["hotel_remove_room"] || "Remove" }) }))] }), _jsxs(View, { style: styles.counterRow, children: [_jsx(Text, { style: styles.counterLabel, children: translations["hotel_adults_title"] || "Adults" }), _jsxs(View, { style: styles.counterControls, children: [_jsx(TouchableOpacity, { style: [
|
|
68
72
|
styles.counterButton,
|
|
69
73
|
room.adults <= 1 && styles.counterButtonDisabled,
|
|
70
|
-
], onPress: () => updateRoom(index, "adults", -1), disabled: room.adults <= 1, activeOpacity: 0.7, children: _jsx(Text, { style: styles.counterButtonText, children: "-" }) }), _jsx(Text, { style: styles.counterValue, children: room.adults }), _jsx(TouchableOpacity, { style: [
|
|
74
|
+
], onPress: () => updateRoom(index, "adults", -1), disabled: room.adults <= 1, activeOpacity: 0.7, children: _jsx(Text, { style: [styles.counterButtonText, { color: btnColor }], children: "-" }) }), _jsx(Text, { style: styles.counterValue, children: room.adults }), _jsx(TouchableOpacity, { style: [
|
|
71
75
|
styles.counterButton,
|
|
72
76
|
totalGuests >= MAX_GUESTS && styles.counterButtonDisabled,
|
|
73
|
-
], onPress: () => updateRoom(index, "adults", 1), disabled: totalGuests >= MAX_GUESTS, activeOpacity: 0.7, children: _jsx(Text, { style: styles.counterButtonText, children: "+" }) })] })] }), _jsxs(View, { style: styles.counterRow, children: [_jsx(Text, { style: styles.counterLabel, children: translations["hotel_children_title"] || "Children" }), _jsxs(View, { style: styles.counterControls, children: [_jsx(TouchableOpacity, { style: [
|
|
77
|
+
], onPress: () => updateRoom(index, "adults", 1), disabled: totalGuests >= MAX_GUESTS, activeOpacity: 0.7, children: _jsx(Text, { style: [styles.counterButtonText, { color: btnColor }], children: "+" }) })] })] }), _jsxs(View, { style: styles.counterRow, children: [_jsx(Text, { style: styles.counterLabel, children: translations["hotel_children_title"] || "Children" }), _jsxs(View, { style: styles.counterControls, children: [_jsx(TouchableOpacity, { style: [
|
|
74
78
|
styles.counterButton,
|
|
75
79
|
room.children <= 0 && styles.counterButtonDisabled,
|
|
76
|
-
], onPress: () => updateRoom(index, "children", -1), disabled: room.children <= 0, activeOpacity: 0.7, children: _jsx(Text, { style: styles.counterButtonText, children: "-" }) }), _jsx(Text, { style: styles.counterValue, children: room.children }), _jsx(TouchableOpacity, { style: [
|
|
80
|
+
], onPress: () => updateRoom(index, "children", -1), disabled: room.children <= 0, activeOpacity: 0.7, children: _jsx(Text, { style: [styles.counterButtonText, { color: btnColor }], children: "-" }) }), _jsx(Text, { style: styles.counterValue, children: room.children }), _jsx(TouchableOpacity, { style: [
|
|
77
81
|
styles.counterButton,
|
|
78
82
|
totalGuests >= MAX_GUESTS && styles.counterButtonDisabled,
|
|
79
|
-
], onPress: () => updateRoom(index, "children", 1), disabled: totalGuests >= MAX_GUESTS, activeOpacity: 0.7, children: _jsx(Text, { style: styles.counterButtonText, children: "+" }) })] })] }), room.children > 0 && (_jsxs(View, { style: styles.agesContainer, children: [_jsx(Text, { style: styles.agesTitle, children: translations["hotel_children_age_title"] ||
|
|
83
|
+
], onPress: () => updateRoom(index, "children", 1), disabled: totalGuests >= MAX_GUESTS, activeOpacity: 0.7, children: _jsx(Text, { style: [styles.counterButtonText, { color: btnColor }], children: "+" }) })] })] }), room.children > 0 && (_jsxs(View, { style: styles.agesContainer, children: [_jsx(Text, { style: styles.agesTitle, children: translations["hotel_children_age_title"] ||
|
|
80
84
|
"Specify children ages" }), _jsx(View, { style: styles.agesGrid, children: room.childrenAges.map((age, childIdx) => (_jsxs(View, { style: styles.ageSelector, children: [_jsxs(Text, { style: styles.ageLabel, children: [translations["hotel_child_label"] || "Child", " ", childIdx + 1] }), _jsxs(View, { style: styles.ageButtonsRow, children: [_jsx(TouchableOpacity, { style: [
|
|
81
85
|
styles.ageAdjustButton,
|
|
82
86
|
age <= 0 && styles.counterButtonDisabled,
|
|
83
|
-
], onPress: () => age > 0 && updateChildAge(index, childIdx, age - 1), disabled: age <= 0, activeOpacity: 0.7, children: _jsx(Text, { style: styles.counterButtonText, children: "-" }) }), _jsx(Text, { style: styles.ageValue, children: age }), _jsx(TouchableOpacity, { style: [
|
|
87
|
+
], onPress: () => age > 0 && updateChildAge(index, childIdx, age - 1), disabled: age <= 0, activeOpacity: 0.7, children: _jsx(Text, { style: [styles.counterButtonText, { color: btnColor }], children: "-" }) }), _jsx(Text, { style: styles.ageValue, children: age }), _jsx(TouchableOpacity, { style: [
|
|
84
88
|
styles.ageAdjustButton,
|
|
85
89
|
age >= 17 && styles.counterButtonDisabled,
|
|
86
|
-
], onPress: () => age < 17 && updateChildAge(index, childIdx, age + 1), disabled: age >= 17, activeOpacity: 0.7, children: _jsx(Text, { style: styles.counterButtonText, children: "+" }) })] })] }, childIdx))) })] }))] }, index))), _jsx(TouchableOpacity, { style: [
|
|
90
|
+
], onPress: () => age < 17 && updateChildAge(index, childIdx, age + 1), disabled: age >= 17, activeOpacity: 0.7, children: _jsx(Text, { style: [styles.counterButtonText, { color: btnColor }], children: "+" }) })] })] }, childIdx))) })] }))] }, index))), _jsx(TouchableOpacity, { style: [
|
|
87
91
|
styles.addRoomButton,
|
|
92
|
+
{ borderColor: btnColor },
|
|
88
93
|
totalGuests >= MAX_GUESTS && styles.addRoomButtonDisabled,
|
|
89
|
-
], onPress: addRoom, disabled: totalGuests >= MAX_GUESTS, activeOpacity: 0.7, children: _jsxs(Text, { style: styles.addRoomText, children: ["+ ", translations["hotel_add_room"] || "Add room"] }) })] }), _jsx(View, { style: styles.modalFooter, children: _jsx(TouchableOpacity, { style: styles.saveButton, onPress: handleSave, activeOpacity: 0.8, children: _jsx(Text, { style: styles.saveButtonText, children: translations["hotel_save_button"] || "Done" }) }) })] }) })] }));
|
|
94
|
+
], onPress: addRoom, disabled: totalGuests >= MAX_GUESTS, activeOpacity: 0.7, children: _jsxs(Text, { style: [styles.addRoomText, { color: btnColor }], children: ["+ ", translations["hotel_add_room"] || "Add room"] }) })] }), _jsx(View, { style: styles.modalFooter, children: _jsx(TouchableOpacity, { style: [styles.saveButton, { backgroundColor: btnColor }], onPress: handleSave, activeOpacity: 0.8, children: _jsx(Text, { style: styles.saveButtonText, children: translations["hotel_save_button"] || "Done" }) }) })] }) })] }));
|
|
90
95
|
};
|
|
91
96
|
const styles = StyleSheet.create({
|
|
92
97
|
selectorText: {
|
|
@@ -6,6 +6,7 @@ import Svg, { Circle, Path, Rect } from "react-native-svg";
|
|
|
6
6
|
import { SvgUri } from "react-native-svg";
|
|
7
7
|
import { useHotelLocations } from "../../hooks/useHotelLocations";
|
|
8
8
|
import { CloseIcon } from "../../../store";
|
|
9
|
+
import { useWidget } from "../../../context/WidgetContext";
|
|
9
10
|
import { Colors, Spacing, FontSize } from "../../../theme/colors";
|
|
10
11
|
import { formStyles } from "../../../theme/formStyles";
|
|
11
12
|
const POPULAR_HOTEL_LOCATIONS = [
|
|
@@ -18,8 +19,11 @@ const POPULAR_HOTEL_LOCATIONS = [
|
|
|
18
19
|
const FLAG_BASE = "https://cdn.travel-code.com/images/remaster/flags";
|
|
19
20
|
const RegionIcon = () => (_jsxs(Svg, { width: 20, height: 20, viewBox: "0 0 20 20", fill: "none", children: [_jsx(Path, { d: "M15.625 8.125C15.625 6.63316 15.0324 5.20242 13.9775 4.14752C12.9226 3.09263 11.4918 2.5 10 2.5C8.50816 2.5 7.07742 3.09263 6.02252 4.14752C4.96763 5.20242 4.375 6.63316 4.375 8.125C4.375 10.4325 6.22125 13.44 10 17.0425C13.7788 13.44 15.625 10.4325 15.625 8.125ZM10 18.75C5.41625 14.5838 3.125 11.0412 3.125 8.125C3.125 6.30164 3.84933 4.55295 5.13864 3.26364C6.42795 1.97433 8.17664 1.25 10 1.25C11.8234 1.25 13.572 1.97433 14.8614 3.26364C16.1507 4.55295 16.875 6.30164 16.875 8.125C16.875 11.0412 14.5838 14.5838 10 18.75Z", fill: "#ABABC8", stroke: "#ABABC8", strokeWidth: 0.25 }), _jsx(Path, { d: "M10 10C10.4973 10 10.9742 9.80246 11.3258 9.45083C11.6775 9.09919 11.875 8.62228 11.875 8.125C11.875 7.62772 11.6775 7.15081 11.3258 6.79917C10.9742 6.44754 10.4973 6.25 10 6.25C9.50272 6.25 9.02581 6.44754 8.67417 6.79917C8.32254 7.15081 8.125 7.62772 8.125 8.125C8.125 8.62228 8.32254 9.09919 8.67417 9.45083C9.02581 9.80246 9.50272 10 10 10ZM10 11.25C9.1712 11.25 8.37634 10.9208 7.79029 10.3347C7.20424 9.74866 6.875 8.9538 6.875 8.125C6.875 7.2962 7.20424 6.50134 7.79029 5.91529C8.37634 5.32924 9.1712 5 10 5C10.8288 5 11.6237 5.32924 12.2097 5.91529C12.7958 6.50134 13.125 7.2962 13.125 8.125C13.125 8.9538 12.7958 9.74866 12.2097 10.3347C11.6237 10.9208 10.8288 11.25 10 11.25Z", fill: "#ABABC8", stroke: "#ABABC8", strokeWidth: 0.25 })] }));
|
|
20
21
|
const HotelBuildingIcon = () => (_jsxs(Svg, { width: 24, height: 24, viewBox: "0 0 24 24", fill: "none", children: [_jsx(Path, { d: "M16.8153 18.2691V3.72727C16.8153 2.22104 15.5942 1 14.088 1H5.72727C4.22104 1 3 2.22104 3 3.72727V22.5863", stroke: "#ABABC8", strokeWidth: 0.681818, strokeLinecap: "round" }), _jsx(Rect, { x: 7.32031, y: 5.31738, width: 1.72691, height: 1.72691, rx: 0.170455, fill: "#ABABC8" }), _jsx(Rect, { x: 7.32031, y: 8.771, width: 1.72691, height: 1.72691, rx: 0.170455, fill: "#ABABC8" }), _jsx(Rect, { x: 7.32031, y: 12.2249, width: 1.72691, height: 1.72691, rx: 0.170455, fill: "#ABABC8" }), _jsx(Rect, { x: 7.32031, y: 15.6787, width: 1.72691, height: 1.72691, rx: 0.170455, fill: "#ABABC8" }), _jsx(Rect, { x: 7.32031, y: 19.1323, width: 1.72691, height: 1.72691, rx: 0.170455, fill: "#ABABC8" }), _jsx(Path, { d: "M13.6822 5.31714C12.5525 5.31714 11.6367 6.23292 11.6367 7.36259V21.9044C11.6367 22.2809 11.942 22.5862 12.3185 22.5862H15.0977V19.3028C15.0977 19.2086 15.174 19.1323 15.2681 19.1323H18.381C18.4752 19.1323 18.5515 19.2086 18.5515 19.3028V22.5862H21.3163C21.6929 22.5862 21.9982 22.2809 21.9982 21.9044V7.36259C21.9982 6.23292 21.0824 5.31714 19.9527 5.31714H13.6822ZM14.2305 8.07793C14.2305 7.98379 14.3068 7.90747 14.4009 7.90747H15.7869C15.8811 7.90747 15.9574 7.98379 15.9574 8.07793V9.46392C15.9574 9.55806 15.8811 9.63438 15.7869 9.63438H14.4009C14.3068 9.63438 14.2305 9.55806 14.2305 9.46392V8.07793ZM17.8538 7.90747C17.7597 7.90747 17.6834 7.98379 17.6834 8.07793V9.46392C17.6834 9.55806 17.7597 9.63438 17.8538 9.63438H19.2398C19.334 9.63438 19.4103 9.55806 19.4103 9.46392V8.07793C19.4103 7.98379 19.334 7.90747 19.2398 7.90747H17.8538ZM14.2305 11.5318C14.2305 11.4376 14.3068 11.3613 14.4009 11.3613H15.7869C15.8811 11.3613 15.9574 11.4376 15.9574 11.5318V12.9178C15.9574 13.0119 15.8811 13.0882 15.7869 13.0882H14.4009C14.3068 13.0882 14.2305 13.0119 14.2305 12.9178V11.5318ZM17.8538 11.3613C17.7597 11.3613 17.6834 11.4376 17.6834 11.5318V12.9178C17.6834 13.0119 17.7597 13.0882 17.8538 13.0882H19.2398C19.334 13.0882 19.4103 13.0119 19.4103 12.9178V11.5318C19.4103 11.4376 19.334 11.3613 19.2398 11.3613H17.8538ZM14.2305 14.9856C14.2305 14.8915 14.3068 14.8152 14.4009 14.8152H15.7869C15.8811 14.8152 15.9574 14.8915 15.9574 14.9856V16.3716C15.9574 16.4657 15.8811 16.5421 15.7869 16.5421H14.4009C14.3068 16.5421 14.2305 16.4657 14.2305 16.3716V14.9856ZM17.8538 14.8152C17.7597 14.8152 17.6834 14.8915 17.6834 14.9856V16.3716C17.6834 16.4657 17.7597 16.5421 17.8538 16.5421H19.2398C19.334 16.5421 19.4103 16.4657 19.4103 16.3716V14.9856C19.4103 14.8915 19.334 14.8152 19.2398 14.8152H17.8538Z", fill: "#ABABC8", fillRule: "evenodd", clipRule: "evenodd" })] }));
|
|
21
|
-
const SearchIcon = () => (_jsxs(Svg, { width: 19, height: 18, viewBox: "0 0 19 18", fill: "none", children: [_jsx(Circle, { cx: 7, cy: 7, r: 6, stroke:
|
|
22
|
+
const SearchIcon = ({ color = Colors.primary }) => (_jsxs(Svg, { width: 19, height: 18, viewBox: "0 0 19 18", fill: "none", children: [_jsx(Circle, { cx: 7, cy: 7, r: 6, stroke: color, strokeWidth: 2 }), _jsx(Path, { d: "M12 11L17.2857 16.0902", stroke: color, strokeWidth: 2, strokeLinecap: "round" })] }));
|
|
22
23
|
const HotelLocation = ({ label, value, onChange, lang = "ru", translations, defaultItem, }) => {
|
|
24
|
+
const { colors } = useWidget();
|
|
25
|
+
const btnColor = colors.primary || Colors.primary;
|
|
26
|
+
const inputBg = colors.input || Colors.inputBg;
|
|
23
27
|
const getLabel = (item) => lang === "en" ? item.nameEn || item.name : item.name;
|
|
24
28
|
const getAddress = (item) => lang === "en" ? item.addressEn || item.address : item.address;
|
|
25
29
|
const insets = useSafeAreaInsets();
|
|
@@ -73,7 +77,7 @@ const HotelLocation = ({ label, value, onChange, lang = "ru", translations, defa
|
|
|
73
77
|
const countryName = getAddress(loc);
|
|
74
78
|
return (_jsx(TouchableOpacity, { style: styles.locationItem, onPress: () => handleSelect(loc), activeOpacity: 0.7, children: _jsxs(View, { style: styles.locationItemContent, children: [_jsx(View, { style: styles.iconContainer, children: isHotel ? _jsx(HotelBuildingIcon, {}) : _jsx(RegionIcon, {}) }), _jsxs(View, { style: styles.locationInfo, children: [_jsx(Text, { style: styles.locationCity, numberOfLines: 1, children: cityName }), _jsxs(View, { style: styles.addressRow, children: [_jsx(SvgUri, { uri: `${FLAG_BASE}/${loc.countryCode}.svg`, width: 20, height: 14 }), _jsx(Text, { style: styles.locationAddress, numberOfLines: 1, children: countryName })] })] })] }) }));
|
|
75
79
|
};
|
|
76
|
-
return (_jsxs(_Fragment, { children: [_jsxs(TouchableOpacity, { style: formStyles.inputTrigger, onPress: () => setIsModalOpen(true), activeOpacity: 0.8, children: [_jsx(Text, { style: formStyles.label, children: label }), _jsx(View, { style: formStyles.inputTriggerRow, children: _jsx(Text, { style: [formStyles.inputTriggerText, !query && formStyles.inputTriggerPlaceholder], numberOfLines: 1, children: query || label }) })] }), _jsx(Modal, { visible: isModalOpen, animationType: "slide", presentationStyle: "fullScreen", onRequestClose: handleClose, children: _jsxs(View, { style: [styles.modalContainer, { paddingTop: insets.top }], children: [_jsx(TouchableOpacity, { style: styles.closeButton, onPress: handleClose, hitSlop: { top: 8, bottom: 8, left: 8, right: 8 }, children: _jsx(CloseIcon, { size: 16 }) }), _jsxs(View, { style: styles.searchHeader, children: [_jsx(SearchIcon, {}), _jsx(TextInput, { autoFocus: true, style: styles.searchInput, value: query, onChangeText: setQuery, placeholder: translations["location_popup_placeholder"] ||
|
|
80
|
+
return (_jsxs(_Fragment, { children: [_jsxs(TouchableOpacity, { style: [formStyles.inputTrigger, { backgroundColor: inputBg }], onPress: () => setIsModalOpen(true), activeOpacity: 0.8, children: [_jsx(Text, { style: formStyles.label, children: label }), _jsx(View, { style: formStyles.inputTriggerRow, children: _jsx(Text, { style: [formStyles.inputTriggerText, !query && formStyles.inputTriggerPlaceholder], numberOfLines: 1, children: query || label }) })] }), _jsx(Modal, { visible: isModalOpen, animationType: "slide", presentationStyle: "fullScreen", onRequestClose: handleClose, children: _jsxs(View, { style: [styles.modalContainer, { paddingTop: insets.top }], children: [_jsx(TouchableOpacity, { style: styles.closeButton, onPress: handleClose, hitSlop: { top: 8, bottom: 8, left: 8, right: 8 }, children: _jsx(CloseIcon, { size: 16 }) }), _jsxs(View, { style: styles.searchHeader, children: [_jsx(SearchIcon, { color: btnColor }), _jsx(TextInput, { autoFocus: true, style: styles.searchInput, value: query, onChangeText: setQuery, placeholder: translations["location_popup_placeholder"] ||
|
|
77
81
|
"Enter city or hotel", placeholderTextColor: Colors.textTertiary, returnKeyType: "search" })] }), _jsxs(View, { style: styles.modalContent, children: [loading && (_jsxs(View, { style: styles.loadingContainer, children: [_jsx(ActivityIndicator, { size: "small", color: Colors.primary }), _jsx(Text, { style: styles.loadingText, children: translations["location_popup_loading"] || "Loading..." })] })), !loading && isSearching && !hasSearchResults && (_jsx(Text, { style: styles.noResults, children: translations["location_popup_no_results"] ||
|
|
78
82
|
"Nothing found" })), !loading && showPopular && (_jsxs(_Fragment, { children: [_jsx(Text, { style: styles.sectionTitle, children: translations["popular_locations"] || "Popular destinations" }), _jsx(FlatList, { data: POPULAR_HOTEL_LOCATIONS, renderItem: ({ item }) => renderLocationItem(item, false), keyExtractor: (item) => item.id, keyboardShouldPersistTaps: "handled", showsVerticalScrollIndicator: false })] })), !loading && isSearching && hasSearchResults && (_jsx(SectionList, { sections: sections, renderSectionHeader: ({ section }) => (_jsx(Text, { style: styles.sectionTitle, children: section.title })), renderItem: ({ item, section }) => renderLocationItem(item, section.type === "hotels"), keyExtractor: (item) => item.id, keyboardShouldPersistTaps: "handled", showsVerticalScrollIndicator: false, stickySectionHeadersEnabled: false }))] })] }) })] }));
|
|
79
83
|
};
|
|
@@ -7,12 +7,16 @@ import HotelsSkeleton from "./HotelsSkeleton";
|
|
|
7
7
|
import HotelList from "./components/HotelList";
|
|
8
8
|
import HotelEmptyState from "./components/HotelEmptyState";
|
|
9
9
|
import HotelMap, { pinsFromOffers } from "./components/HotelMap";
|
|
10
|
+
import { useHotelMapData } from "../../hooks/useHotelMapData";
|
|
10
11
|
import ProvidersLoader from "../flight/flight-results/ProvidersLoader";
|
|
11
12
|
import Pagination from "../Pagination";
|
|
12
13
|
import { HotelFilters, QUICK_FILTERS } from "./hotel-filters";
|
|
14
|
+
import { useWidget } from "../../../context/WidgetContext";
|
|
13
15
|
import { Colors, Spacing, FontSize, BorderRadius } from "../../../theme/colors";
|
|
14
|
-
const HotelResults = ({ offers, allHotels, loading, isStreaming = false, isPageLoading = false, error, translations, lang, searchParams = null, total = 0, offset = 0, limit = 20, fetchPage, filterSearch, currency = "USD", headerComponent, }) => {
|
|
16
|
+
const HotelResults = ({ offers, allHotels, loading, isStreaming = false, isPageLoading = false, error, translations, lang, searchParams = null, total = 0, offset = 0, limit = 20, fetchPage, filterSearch, currency = "USD", headerComponent, streamCompleted = false, }) => {
|
|
15
17
|
const insets = useSafeAreaInsets();
|
|
18
|
+
const { colors } = useWidget();
|
|
19
|
+
const btnColor = colors.primary || Colors.primary;
|
|
16
20
|
const [hasSearched, setHasSearched] = useState(false);
|
|
17
21
|
const [localOffers, setLocalOffers] = useState(offers ?? []);
|
|
18
22
|
const [isMapOpen, setIsMapOpen] = useState(false);
|
|
@@ -43,7 +47,7 @@ const HotelResults = ({ offers, allHotels, loading, isStreaming = false, isPageL
|
|
|
43
47
|
if (offers && offers.length > 0) {
|
|
44
48
|
setLocalOffers(offers);
|
|
45
49
|
}
|
|
46
|
-
else if (
|
|
50
|
+
else if (offers?.length === 0) {
|
|
47
51
|
setLocalOffers([]);
|
|
48
52
|
}
|
|
49
53
|
}, [offers, loading, isStreaming, isPageLoading]);
|
|
@@ -66,11 +70,33 @@ const HotelResults = ({ offers, allHotels, loading, isStreaming = false, isPageL
|
|
|
66
70
|
}
|
|
67
71
|
return { boards, payments, starRating };
|
|
68
72
|
};
|
|
73
|
+
const hasAppliedFilters = activeFilters.length > 0 || popupValues !== null;
|
|
74
|
+
const shouldFetchMap = !!searchParams && streamCompleted && (hasSearched || hasAppliedFilters);
|
|
75
|
+
const { pins: apiPins } = useHotelMapData(searchParams, shouldFetchMap);
|
|
76
|
+
const hasFullDataset = (allHotels?.length ?? 0) > 0 && (allHotels?.length ?? 0) >= total;
|
|
77
|
+
const useStreamPins = hasFullDataset && !hasAppliedFilters && (allHotels ?? []).some((h) => h.latitude && h.longitude);
|
|
78
|
+
const apiMapPins = useMemo(() => {
|
|
79
|
+
if (apiPins.length === 0)
|
|
80
|
+
return [];
|
|
81
|
+
return apiPins.map((p) => ({
|
|
82
|
+
id: Number(p.id),
|
|
83
|
+
hotelId: Number(p.id),
|
|
84
|
+
lat: p.latitude,
|
|
85
|
+
lng: p.longitude,
|
|
86
|
+
price: p.total ?? 0,
|
|
87
|
+
currency,
|
|
88
|
+
name: "",
|
|
89
|
+
soldOut: p.soldOut,
|
|
90
|
+
}));
|
|
91
|
+
}, [apiPins, currency]);
|
|
69
92
|
const mapPins = useMemo(() => {
|
|
70
|
-
|
|
93
|
+
const streamPins = allHotels && allHotels.length > 0 ? pinsFromOffers(allHotels, currency) : [];
|
|
94
|
+
if (streamPins.length === 0 && apiMapPins.length === 0)
|
|
71
95
|
return [];
|
|
72
|
-
|
|
73
|
-
|
|
96
|
+
if (hasAppliedFilters)
|
|
97
|
+
return apiMapPins;
|
|
98
|
+
return useStreamPins ? streamPins : apiMapPins;
|
|
99
|
+
}, [allHotels, currency, useStreamPins, apiMapPins, hasAppliedFilters]);
|
|
74
100
|
const hasResults = localOffers.length > 0;
|
|
75
101
|
const showMapButton = hasResults && mapPins.length > 0 && !loadingInitial;
|
|
76
102
|
const isEmptyResult = hasSearched && !loadingInitial && !loadingRefetch && !loading && !isStreaming && localOffers.length === 0;
|
|
@@ -246,7 +272,7 @@ const HotelResults = ({ offers, allHotels, loading, isStreaming = false, isPageL
|
|
|
246
272
|
return null;
|
|
247
273
|
return (_jsx(View, { style: styles.paginationWrapper, children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: handlePageChange }) }));
|
|
248
274
|
};
|
|
249
|
-
return (_jsxs(View, { style: styles.container, children: [loadingRefetch && (_jsxs(View, { style: styles.overlayLoader, children: [_jsx(ActivityIndicator, { size: "large", color:
|
|
275
|
+
return (_jsxs(View, { style: styles.container, children: [loadingRefetch && (_jsxs(View, { style: styles.overlayLoader, children: [_jsx(ActivityIndicator, { size: "large", color: btnColor }), _jsx(Text, { style: styles.overlayText, children: translations["loading"] || "Loading..." })] })), _jsx(FlatList, { ref: listRef, data: !loadingInitial && hasResults && !isErrorState ? localOffers : [], renderItem: ({ item, index }) => (_jsx(HotelList, { offers: [item], translations: translations, lang: lang, searchParams: searchParams, onToggleDescription: () => handleToggleDescription(index) })), keyExtractor: (item, index) => String(item.id || item.propertyId || index), ListHeaderComponent: renderHeader(), ListFooterComponent: renderFooter(), contentContainerStyle: styles.listContent, showsVerticalScrollIndicator: false, removeClippedSubviews: true, maxToRenderPerBatch: 10, windowSize: 5 }), showMapButton && (_jsxs(TouchableOpacity, { style: [styles.mapButton, { backgroundColor: btnColor }], onPress: () => setIsMapOpen(true), activeOpacity: 0.8, children: [_jsxs(Svg, { width: 18, height: 18, viewBox: "0 0 24 24", fill: "none", stroke: Colors.white, strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx(Polygon, { points: "1 6 1 22 8 18 16 22 23 18 23 2 16 6 8 2 1 6" }), _jsx(Line, { x1: 8, y1: 2, x2: 8, y2: 18 }), _jsx(Line, { x1: 16, y1: 6, x2: 16, y2: 22 })] }), _jsx(Text, { style: styles.mapButtonText, children: translations["map_button"] || "Map" })] })), isMapOpen && (_jsxs(View, { style: StyleSheet.absoluteFill, children: [_jsx(HotelMap, { pins: mapPins, allHotels: allHotels || [], streamCompleted: !isStreaming && !loading, loading: loading || isStreaming, translations: translations, lang: lang, searchParams: searchParams, onHotelSelect: (hotelId) => {
|
|
250
276
|
}, onRefreshSearch: () => {
|
|
251
277
|
if (searchParams && filterSearch) {
|
|
252
278
|
filterSearch(searchParams);
|
|
@@ -11,9 +11,43 @@ const starStyles = StyleSheet.create({
|
|
|
11
11
|
color: "#F5A623",
|
|
12
12
|
},
|
|
13
13
|
});
|
|
14
|
-
const
|
|
14
|
+
const getBookingUrl = (hotel, searchParams, basketLink, token, lang) => {
|
|
15
|
+
if (!hotel)
|
|
16
|
+
return "#";
|
|
17
|
+
const HOST = basketLink || "https://travel-code.com";
|
|
18
|
+
if (!searchParams) {
|
|
19
|
+
return `${HOST}${hotel.url || "#"}&accessToken=${token}&language=${lang}`;
|
|
20
|
+
}
|
|
21
|
+
const propertyId = hotel.propertyId || hotel.id;
|
|
22
|
+
if (!propertyId) {
|
|
23
|
+
return `${HOST}${hotel.url || "#"}&accessToken=${token}&language=${lang}`;
|
|
24
|
+
}
|
|
25
|
+
const partnerId = hotel.partnerId || hotel.pId || 0;
|
|
26
|
+
const guestsClean = (searchParams.guests || []).map((r) => {
|
|
27
|
+
const g = { adults: r.adults };
|
|
28
|
+
if (r.children > 0) {
|
|
29
|
+
g.children = r.children;
|
|
30
|
+
if (r.childrenAges?.length)
|
|
31
|
+
g.childrenAges = r.childrenAges;
|
|
32
|
+
}
|
|
33
|
+
return g;
|
|
34
|
+
});
|
|
35
|
+
const guestsB64 = btoa(JSON.stringify(guestsClean));
|
|
36
|
+
const params = new URLSearchParams({
|
|
37
|
+
checkinDate: searchParams.checkin || "",
|
|
38
|
+
checkoutDate: searchParams.checkout || "",
|
|
39
|
+
guests: guestsB64,
|
|
40
|
+
nationality: searchParams.countryCode || "",
|
|
41
|
+
partner: "0",
|
|
42
|
+
cpPartner: String(partnerId),
|
|
43
|
+
location: String(searchParams.location || ""),
|
|
44
|
+
});
|
|
45
|
+
return `${HOST}/hotels/${propertyId}?${params.toString()}&accessToken=${token}&language=${lang}`;
|
|
46
|
+
};
|
|
47
|
+
const HotelCard = ({ hotel, index, total, translations, lang, searchParams, onToggleDescription, }) => {
|
|
15
48
|
const token = useApiToken();
|
|
16
|
-
const { config } = useWidget();
|
|
49
|
+
const { config, colors } = useWidget();
|
|
50
|
+
const btnColor = colors.primary || Colors.primary;
|
|
17
51
|
const name = hotel.hotelNames?.mainName || hotel.propertyName || hotel.name || "Unnamed";
|
|
18
52
|
const address = hotel.address || "";
|
|
19
53
|
const description = hotel.description || "";
|
|
@@ -26,8 +60,8 @@ const HotelCard = ({ hotel, index, total, translations, lang, onToggleDescriptio
|
|
|
26
60
|
const isSoldOut = hotel.soldOut === true;
|
|
27
61
|
const currencySign = hotel.currencySign || "$";
|
|
28
62
|
const heroImages = hotel.heroImages || [];
|
|
29
|
-
const
|
|
30
|
-
const url =
|
|
63
|
+
const basketLink = config?.additional?.basket_link?.trim() || "https://travel-code.com";
|
|
64
|
+
const url = getBookingUrl(hotel, searchParams, basketLink, token, lang);
|
|
31
65
|
const handleOpenUrl = () => {
|
|
32
66
|
Linking.openURL(url).catch(() => { });
|
|
33
67
|
};
|
|
@@ -42,7 +76,7 @@ const HotelCard = ({ hotel, index, total, translations, lang, onToggleDescriptio
|
|
|
42
76
|
"Only 1 room left at this price" })), hotel.availableRooms > 1 && hotel.availableRooms < 5 && (_jsx(Text, { style: styles.warningText, children: translations["hotels_only_few_rooms"]
|
|
43
77
|
? translations["hotels_only_few_rooms"].replace("{availableRooms}", String(hotel.availableRooms))
|
|
44
78
|
: `Only ${hotel.availableRooms} rooms left at this price` }))] }), _jsx(View, { style: styles.priceSection, children: isSoldOut ? (_jsx(View, { style: styles.soldOutBlock, children: _jsx(Text, { style: styles.soldOutText, children: translations["hotels_sold_out"] || "Sold Out" }) })) : (_jsxs(_Fragment, { children: [hotel.refundable && (_jsx(Text, { style: styles.refundableText, children: translations["hotels_refundable"] || "Free cancellation" })), _jsxs(View, { style: styles.priceBlock, children: [_jsxs(Text, { style: styles.nightlyPrice, children: [currencySign, nightlyPrice, _jsxs(Text, { style: styles.perNightLabel, children: [" ", translations["hotels_price_per_night"] || "/ night"] })] }), _jsxs(Text, { style: styles.totalPriceText, children: [translations["hotels_total_price"] || "Total:", " ", currencySign, totalPrice] }), _jsx(Text, { style: styles.taxesText, children: translations["hotels_taxes_included"] ||
|
|
45
|
-
"(incl. taxes & fees)" })] }), ratingValue ? (_jsxs(View, { style: styles.ratingBlock, children: [_jsx(View, { style: [styles.ratingBadge, { backgroundColor: parseFloat(ratingValue) >= 4 ? '#1a8917' : Colors.textTertiary }], children: _jsx(Text, { style: styles.ratingBadgeText, children: ratingValue }) }), _jsxs(View, { children: [_jsx(Text, { style: styles.ratingLabel, children: ratingText }), reviewsCount > 0 && (_jsxs(Text, { style: styles.ratingCount, children: [reviewsCount, " ", translations["hotels_reviews"] || "reviews"] }))] })] })) : null, _jsx(TouchableOpacity, { style: styles.viewButton, onPress: handleOpenUrl, activeOpacity: 0.8, children: _jsx(Text, { style: styles.viewButtonText, children: translations["hotels_view_button"] || "View deal" }) })] })) })] }));
|
|
79
|
+
"(incl. taxes & fees)" })] }), ratingValue ? (_jsxs(View, { style: styles.ratingBlock, children: [_jsx(View, { style: [styles.ratingBadge, { backgroundColor: parseFloat(ratingValue) >= 4 ? '#1a8917' : Colors.textTertiary }], children: _jsx(Text, { style: styles.ratingBadgeText, children: ratingValue }) }), _jsxs(View, { children: [_jsx(Text, { style: styles.ratingLabel, children: ratingText }), reviewsCount > 0 && (_jsxs(Text, { style: styles.ratingCount, children: [reviewsCount, " ", translations["hotels_reviews"] || "reviews"] }))] })] })) : null, _jsx(TouchableOpacity, { style: [styles.viewButton, { backgroundColor: btnColor }], onPress: handleOpenUrl, activeOpacity: 0.8, children: _jsx(Text, { style: styles.viewButtonText, children: translations["hotels_view_button"] || "View deal" }) })] })) })] }));
|
|
46
80
|
};
|
|
47
81
|
const styles = StyleSheet.create({
|
|
48
82
|
card: {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { View, StyleSheet } from "react-native";
|
|
3
3
|
import HotelCard from "./HotelCard";
|
|
4
|
-
const HotelList = ({ offers, translations, lang, onToggleDescription, }) => {
|
|
5
|
-
return (_jsx(View, { style: styles.container, children: offers.map((hotel, index) => (_jsx(HotelCard, { hotel: hotel, index: index, total: offers.length, translations: translations, lang: lang, onToggleDescription: () => onToggleDescription(index) }, String(hotel.id || hotel.propertyId || index)))) }));
|
|
4
|
+
const HotelList = ({ offers, translations, lang, searchParams, onToggleDescription, }) => {
|
|
5
|
+
return (_jsx(View, { style: styles.container, children: offers.map((hotel, index) => (_jsx(HotelCard, { hotel: hotel, index: index, total: offers.length, translations: translations, lang: lang, searchParams: searchParams, onToggleDescription: () => onToggleDescription(index) }, String(hotel.id || hotel.propertyId || index)))) }));
|
|
6
6
|
};
|
|
7
7
|
const styles = StyleSheet.create({
|
|
8
8
|
container: {
|