@devtravelcode/widget-native 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. package/LICENSE +18 -0
  2. package/dist/App.d.ts +10 -0
  3. package/dist/App.js +192 -0
  4. package/dist/TravelHubWidget.d.ts +12 -0
  5. package/dist/TravelHubWidget.js +58 -0
  6. package/dist/components/ErrorBoundary.d.ts +18 -0
  7. package/dist/components/ErrorBoundary.js +63 -0
  8. package/dist/components/ui/Modal.d.ts +11 -0
  9. package/dist/components/ui/Modal.js +66 -0
  10. package/dist/components/ui/Skeleton.d.ts +8 -0
  11. package/dist/components/ui/Skeleton.js +33 -0
  12. package/dist/components/ui/badge.d.ts +11 -0
  13. package/dist/components/ui/badge.js +54 -0
  14. package/dist/components/ui/button.d.ts +14 -0
  15. package/dist/components/ui/button.js +101 -0
  16. package/dist/components/ui/card.d.ts +16 -0
  17. package/dist/components/ui/card.js +51 -0
  18. package/dist/components/ui/input.d.ts +8 -0
  19. package/dist/components/ui/input.js +61 -0
  20. package/dist/components/ui/label.d.ts +8 -0
  21. package/dist/components/ui/label.js +17 -0
  22. package/dist/context/WidgetContext.d.ts +29 -0
  23. package/dist/context/WidgetContext.js +64 -0
  24. package/dist/icons/CarIcon.d.ts +4 -0
  25. package/dist/icons/CarIcon.js +3 -0
  26. package/dist/icons/CloseIcon.d.ts +7 -0
  27. package/dist/icons/CloseIcon.js +3 -0
  28. package/dist/icons/FlightIcon.d.ts +4 -0
  29. package/dist/icons/FlightIcon.js +3 -0
  30. package/dist/icons/HotelIcon.d.ts +4 -0
  31. package/dist/icons/HotelIcon.js +3 -0
  32. package/dist/index.d.ts +2 -0
  33. package/dist/index.js +1 -0
  34. package/dist/lib/utils.d.ts +2 -0
  35. package/dist/lib/utils.js +3 -0
  36. package/dist/locales/widgetTranslations.d.ts +1 -0
  37. package/dist/locales/widgetTranslations.js +732 -0
  38. package/dist/modules/hooks/useAirportSearch.d.ts +21 -0
  39. package/dist/modules/hooks/useAirportSearch.js +48 -0
  40. package/dist/modules/hooks/useCarOffers.d.ts +7 -0
  41. package/dist/modules/hooks/useCarOffers.js +47 -0
  42. package/dist/modules/hooks/useCountriesByCodes.d.ts +4 -0
  43. package/dist/modules/hooks/useCountriesByCodes.js +60 -0
  44. package/dist/modules/hooks/useCountriesSearch.d.ts +11 -0
  45. package/dist/modules/hooks/useCountriesSearch.js +55 -0
  46. package/dist/modules/hooks/useDefaultHotelLocation.d.ts +7 -0
  47. package/dist/modules/hooks/useDefaultHotelLocation.js +44 -0
  48. package/dist/modules/hooks/useDetectNationality.d.ts +1 -0
  49. package/dist/modules/hooks/useDetectNationality.js +20 -0
  50. package/dist/modules/hooks/useFlightSearch.d.ts +20 -0
  51. package/dist/modules/hooks/useFlightSearch.js +243 -0
  52. package/dist/modules/hooks/useHotelDetails.d.ts +7 -0
  53. package/dist/modules/hooks/useHotelDetails.js +80 -0
  54. package/dist/modules/hooks/useHotelLocations.d.ts +5 -0
  55. package/dist/modules/hooks/useHotelLocations.js +42 -0
  56. package/dist/modules/hooks/useHotelMapData.d.ts +12 -0
  57. package/dist/modules/hooks/useHotelMapData.js +78 -0
  58. package/dist/modules/hooks/useHotelOffers.d.ts +11 -0
  59. package/dist/modules/hooks/useHotelOffers.js +122 -0
  60. package/dist/modules/hooks/useHotelSearchSSE.d.ts +18 -0
  61. package/dist/modules/hooks/useHotelSearchSSE.js +411 -0
  62. package/dist/modules/hooks/useSearchExpiration.d.ts +6 -0
  63. package/dist/modules/hooks/useSearchExpiration.js +35 -0
  64. package/dist/modules/hooks/useTranslationsHub.d.ts +5 -0
  65. package/dist/modules/hooks/useTranslationsHub.js +48 -0
  66. package/dist/modules/navbar/Navbar.d.ts +8 -0
  67. package/dist/modules/navbar/Navbar.js +69 -0
  68. package/dist/modules/search-form/AgeSelector.d.ts +9 -0
  69. package/dist/modules/search-form/AgeSelector.js +109 -0
  70. package/dist/modules/search-form/CustomSelect.d.ts +14 -0
  71. package/dist/modules/search-form/CustomSelect.js +94 -0
  72. package/dist/modules/search-form/FiltersSkeleton.d.ts +6 -0
  73. package/dist/modules/search-form/FiltersSkeleton.js +35 -0
  74. package/dist/modules/search-form/NationalityInput.d.ts +18 -0
  75. package/dist/modules/search-form/NationalityInput.js +172 -0
  76. package/dist/modules/search-form/Pagination.d.ts +8 -0
  77. package/dist/modules/search-form/Pagination.js +84 -0
  78. package/dist/modules/search-form/SearchExpiredPopup.d.ts +10 -0
  79. package/dist/modules/search-form/SearchExpiredPopup.js +91 -0
  80. package/dist/modules/search-form/SkeletonWidget.d.ts +2 -0
  81. package/dist/modules/search-form/SkeletonWidget.js +33 -0
  82. package/dist/modules/search-form/TimePicker.d.ts +9 -0
  83. package/dist/modules/search-form/TimePicker.js +182 -0
  84. package/dist/modules/search-form/cars/CarEmptyState.d.ts +6 -0
  85. package/dist/modules/search-form/cars/CarEmptyState.js +42 -0
  86. package/dist/modules/search-form/cars/CarFiltersSkeleton.d.ts +6 -0
  87. package/dist/modules/search-form/cars/CarFiltersSkeleton.js +35 -0
  88. package/dist/modules/search-form/cars/CarOffersList.d.ts +43 -0
  89. package/dist/modules/search-form/cars/CarOffersList.js +214 -0
  90. package/dist/modules/search-form/cars/CarResults.d.ts +25 -0
  91. package/dist/modules/search-form/cars/CarResults.js +116 -0
  92. package/dist/modules/search-form/cars/CarSearchForm.d.ts +19 -0
  93. package/dist/modules/search-form/cars/CarSearchForm.js +173 -0
  94. package/dist/modules/search-form/cars/CarSkeleton.d.ts +6 -0
  95. package/dist/modules/search-form/cars/CarSkeleton.js +46 -0
  96. package/dist/modules/search-form/cars/CarTopOffersSlider.d.ts +20 -0
  97. package/dist/modules/search-form/cars/CarTopOffersSlider.js +100 -0
  98. package/dist/modules/search-form/cars/car-filters/CarFilters.d.ts +8 -0
  99. package/dist/modules/search-form/cars/car-filters/CarFilters.js +318 -0
  100. package/dist/modules/search-form/cars/car-filters/FilterSection.d.ts +9 -0
  101. package/dist/modules/search-form/cars/car-filters/FilterSection.js +30 -0
  102. package/dist/modules/search-form/cars/car-filters/index.d.ts +6 -0
  103. package/dist/modules/search-form/cars/car-filters/index.js +3 -0
  104. package/dist/modules/search-form/cars/car-filters/useCarFilters.d.ts +24 -0
  105. package/dist/modules/search-form/cars/car-filters/useCarFilters.js +121 -0
  106. package/dist/modules/search-form/flight/AirlinesSelector.d.ts +8 -0
  107. package/dist/modules/search-form/flight/AirlinesSelector.js +235 -0
  108. package/dist/modules/search-form/flight/CabinClassSelector.d.ts +9 -0
  109. package/dist/modules/search-form/flight/CabinClassSelector.js +63 -0
  110. package/dist/modules/search-form/flight/DatePicker.d.ts +12 -0
  111. package/dist/modules/search-form/flight/DatePicker.js +536 -0
  112. package/dist/modules/search-form/flight/FlightSearchForm.d.ts +18 -0
  113. package/dist/modules/search-form/flight/FlightSearchForm.js +147 -0
  114. package/dist/modules/search-form/flight/LocationInput.d.ts +11 -0
  115. package/dist/modules/search-form/flight/LocationInput.js +294 -0
  116. package/dist/modules/search-form/flight/PassengersPicker.d.ts +16 -0
  117. package/dist/modules/search-form/flight/PassengersPicker.js +203 -0
  118. package/dist/modules/search-form/flight/SwitchLocationBtn.d.ts +9 -0
  119. package/dist/modules/search-form/flight/SwitchLocationBtn.js +48 -0
  120. package/dist/modules/search-form/flight/flight-filters/FiltersContent.d.ts +21 -0
  121. package/dist/modules/search-form/flight/flight-filters/FiltersContent.js +145 -0
  122. package/dist/modules/search-form/flight/flight-filters/FlightFiltersDynamic.d.ts +11 -0
  123. package/dist/modules/search-form/flight/flight-filters/FlightFiltersDynamic.js +213 -0
  124. package/dist/modules/search-form/flight/flight-filters/RangeBlock.d.ts +22 -0
  125. package/dist/modules/search-form/flight/flight-filters/RangeBlock.js +164 -0
  126. package/dist/modules/search-form/flight/flight-filters/index.d.ts +1 -0
  127. package/dist/modules/search-form/flight/flight-filters/index.js +1 -0
  128. package/dist/modules/search-form/flight/flight-results/FlightAdditionalInfo.d.ts +16 -0
  129. package/dist/modules/search-form/flight/flight-results/FlightAdditionalInfo.js +229 -0
  130. package/dist/modules/search-form/flight/flight-results/FlightBaggageToggle.d.ts +15 -0
  131. package/dist/modules/search-form/flight/flight-results/FlightBaggageToggle.js +110 -0
  132. package/dist/modules/search-form/flight/flight-results/FlightCard.d.ts +14 -0
  133. package/dist/modules/search-form/flight/flight-results/FlightCard.js +436 -0
  134. package/dist/modules/search-form/flight/flight-results/FlightInfo.d.ts +12 -0
  135. package/dist/modules/search-form/flight/flight-results/FlightInfo.js +48 -0
  136. package/dist/modules/search-form/flight/flight-results/FlightPriceBlock.d.ts +11 -0
  137. package/dist/modules/search-form/flight/flight-results/FlightPriceBlock.js +36 -0
  138. package/dist/modules/search-form/flight/flight-results/FlightResults.d.ts +22 -0
  139. package/dist/modules/search-form/flight/flight-results/FlightResults.js +109 -0
  140. package/dist/modules/search-form/flight/flight-results/FlightSegments.d.ts +40 -0
  141. package/dist/modules/search-form/flight/flight-results/FlightSegments.js +162 -0
  142. package/dist/modules/search-form/flight/flight-results/FlightsSkeleton.d.ts +6 -0
  143. package/dist/modules/search-form/flight/flight-results/FlightsSkeleton.js +52 -0
  144. package/dist/modules/search-form/flight/flight-results/ProvidersLoader.d.ts +9 -0
  145. package/dist/modules/search-form/flight/flight-results/ProvidersLoader.js +242 -0
  146. package/dist/modules/search-form/hotel/DatePicker.d.ts +1 -0
  147. package/dist/modules/search-form/hotel/DatePicker.js +1 -0
  148. package/dist/modules/search-form/hotel/GuestSelector.d.ts +13 -0
  149. package/dist/modules/search-form/hotel/GuestSelector.js +272 -0
  150. package/dist/modules/search-form/hotel/GuestSelectorMobilePopup.d.ts +2 -0
  151. package/dist/modules/search-form/hotel/GuestSelectorMobilePopup.js +1 -0
  152. package/dist/modules/search-form/hotel/HotelLocation.d.ts +17 -0
  153. package/dist/modules/search-form/hotel/HotelLocation.js +178 -0
  154. package/dist/modules/search-form/hotel/HotelLocationMobilePopup.d.ts +2 -0
  155. package/dist/modules/search-form/hotel/HotelLocationMobilePopup.js +1 -0
  156. package/dist/modules/search-form/hotel/HotelResults.d.ts +21 -0
  157. package/dist/modules/search-form/hotel/HotelResults.js +333 -0
  158. package/dist/modules/search-form/hotel/HotelSearchForm.d.ts +13 -0
  159. package/dist/modules/search-form/hotel/HotelSearchForm.js +130 -0
  160. package/dist/modules/search-form/hotel/HotelsSkeleton.d.ts +6 -0
  161. package/dist/modules/search-form/hotel/HotelsSkeleton.js +40 -0
  162. package/dist/modules/search-form/hotel/PassengersPicker.d.ts +2 -0
  163. package/dist/modules/search-form/hotel/PassengersPicker.js +1 -0
  164. package/dist/modules/search-form/hotel/components/HotelCard.d.ts +11 -0
  165. package/dist/modules/search-form/hotel/components/HotelCard.js +180 -0
  166. package/dist/modules/search-form/hotel/components/HotelEmptyState.d.ts +6 -0
  167. package/dist/modules/search-form/hotel/components/HotelEmptyState.js +34 -0
  168. package/dist/modules/search-form/hotel/components/HotelGallery.d.ts +6 -0
  169. package/dist/modules/search-form/hotel/components/HotelGallery.js +77 -0
  170. package/dist/modules/search-form/hotel/components/HotelList.d.ts +9 -0
  171. package/dist/modules/search-form/hotel/components/HotelList.js +12 -0
  172. package/dist/modules/search-form/hotel/components/HotelMap.d.ts +26 -0
  173. package/dist/modules/search-form/hotel/components/HotelMap.js +707 -0
  174. package/dist/modules/search-form/hotel/components/HotelPin.d.ts +7 -0
  175. package/dist/modules/search-form/hotel/components/HotelPin.js +1 -0
  176. package/dist/modules/search-form/hotel/hotel-filters/HotelFilters.d.ts +29 -0
  177. package/dist/modules/search-form/hotel/hotel-filters/HotelFilters.js +339 -0
  178. package/dist/modules/search-form/hotel/hotel-filters/HotelFiltersBar.d.ts +3 -0
  179. package/dist/modules/search-form/hotel/hotel-filters/HotelFiltersBar.js +2 -0
  180. package/dist/modules/search-form/hotel/hotel-filters/HotelFiltersContent.d.ts +2 -0
  181. package/dist/modules/search-form/hotel/hotel-filters/HotelFiltersContent.js +1 -0
  182. package/dist/modules/search-form/hotel/hotel-filters/HotelFiltersDynamic.d.ts +2 -0
  183. package/dist/modules/search-form/hotel/hotel-filters/HotelFiltersDynamic.js +1 -0
  184. package/dist/modules/search-form/hotel/hotel-filters/HotelFiltersMobilePopup.d.ts +2 -0
  185. package/dist/modules/search-form/hotel/hotel-filters/HotelFiltersMobilePopup.js +1 -0
  186. package/dist/modules/search-form/hotel/hotel-filters/RangeBlock.d.ts +1 -0
  187. package/dist/modules/search-form/hotel/hotel-filters/RangeBlock.js +1 -0
  188. package/dist/modules/search-form/hotel/hotel-filters/index.d.ts +1 -0
  189. package/dist/modules/search-form/hotel/hotel-filters/index.js +1 -0
  190. package/dist/modules/tabs/FlightsTab.d.ts +1 -0
  191. package/dist/modules/tabs/FlightsTab.js +1 -0
  192. package/dist/modules/tabs/HotelsTab.d.ts +1 -0
  193. package/dist/modules/tabs/HotelsTab.js +1 -0
  194. package/dist/store/index.d.ts +22 -0
  195. package/dist/store/index.js +19 -0
  196. package/dist/theme/colors.d.ts +56 -0
  197. package/dist/theme/colors.js +56 -0
  198. package/dist/theme/formStyles.d.ts +76 -0
  199. package/dist/theme/formStyles.js +78 -0
  200. package/dist/utils/applyWidgetColors.d.ts +1 -0
  201. package/dist/utils/applyWidgetColors.js +2 -0
  202. package/dist/utils/dateTime.d.ts +2 -0
  203. package/dist/utils/dateTime.js +23 -0
  204. package/dist/utils/fetchSSE.d.ts +23 -0
  205. package/dist/utils/fetchSSE.js +104 -0
  206. package/dist/utils/getToken.d.ts +3 -0
  207. package/dist/utils/getToken.js +12 -0
  208. package/package.json +64 -0
@@ -0,0 +1,436 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState, useRef, useCallback } from "react";
3
+ import { View, Text, Image, TouchableOpacity, Animated, Dimensions, Linking, StyleSheet, } from "react-native";
4
+ import Svg, { Path } from "react-native-svg";
5
+ import FlightSegments from "./FlightSegments";
6
+ import FlightAdditionalInfo from "./FlightAdditionalInfo";
7
+ import FlightBaggageToggle from "./FlightBaggageToggle";
8
+ import { useWidget } from "../../../../context/WidgetContext";
9
+ import { useApiToken } from "../../../../utils/getToken";
10
+ import { formatTimeByDateTimeFormat } from "../../../../utils/dateTime";
11
+ import { Colors, Spacing, FontSize, BorderRadius } from "../../../../theme/colors";
12
+ const SCREEN_W = Dimensions.get("window").width;
13
+ const SCREEN_MARGIN = 8;
14
+ const TooltipTrigger = ({ label, onShow, onDismiss, active, children }) => {
15
+ const ref = useRef(null);
16
+ const handlePress = () => {
17
+ if (active) {
18
+ onDismiss();
19
+ }
20
+ else {
21
+ ref.current?.measureInWindow((x, y, w, h) => {
22
+ onShow(label, x, y, w, h);
23
+ });
24
+ }
25
+ };
26
+ return (_jsx(View, { ref: ref, collapsable: false, children: _jsx(TouchableOpacity, { onPress: handlePress, activeOpacity: 0.7, children: children }) }));
27
+ };
28
+ const TooltipBubble = ({ label, triggerX, triggerY, triggerW, triggerH, cardX, cardY }) => {
29
+ const tooltipRef = useRef(null);
30
+ const [tooltipWidth, setTooltipWidth] = useState(0);
31
+ const [tooltipHeight, setTooltipHeight] = useState(0);
32
+ const relY = triggerY - cardY - tooltipHeight - 6;
33
+ const triggerCenterX = triggerX + triggerW / 2;
34
+ let left = triggerCenterX - cardX - tooltipWidth / 2;
35
+ if (triggerCenterX - tooltipWidth / 2 < SCREEN_MARGIN) {
36
+ left = SCREEN_MARGIN - cardX;
37
+ }
38
+ else if (triggerCenterX + tooltipWidth / 2 > SCREEN_W - SCREEN_MARGIN) {
39
+ left = SCREEN_W - SCREEN_MARGIN - tooltipWidth - cardX;
40
+ }
41
+ return (_jsx(View, { ref: tooltipRef, style: [
42
+ tooltipStyles.bubble,
43
+ {
44
+ top: tooltipHeight > 0 ? relY : -9999,
45
+ left: tooltipWidth > 0 ? left : -9999,
46
+ },
47
+ ], onLayout: (e) => {
48
+ setTooltipWidth(e.nativeEvent.layout.width);
49
+ setTooltipHeight(e.nativeEvent.layout.height);
50
+ }, children: _jsx(Text, { style: tooltipStyles.text, children: label }) }));
51
+ };
52
+ const tooltipStyles = StyleSheet.create({
53
+ bubble: {
54
+ position: "absolute",
55
+ zIndex: 9999,
56
+ backgroundColor: "#1F2937",
57
+ paddingHorizontal: 10,
58
+ paddingVertical: 6,
59
+ borderRadius: 6,
60
+ shadowColor: "#000",
61
+ shadowOffset: { width: 0, height: 2 },
62
+ shadowOpacity: 0.25,
63
+ shadowRadius: 4,
64
+ elevation: 10,
65
+ },
66
+ text: {
67
+ color: "#FFFFFF",
68
+ fontSize: 13,
69
+ fontWeight: "500",
70
+ },
71
+ });
72
+ const BASE_URL = "https://cdn.travel-code.com/prod";
73
+ const FlightCard = ({ item, loading, dictionaries, airlines, sessionId, translations, lang, currencySign, onSelect, }) => {
74
+ const token = useApiToken();
75
+ const flight = item.items[0];
76
+ const itineraries = flight.itineraries || [];
77
+ const price = flight.priceForSearch?.totalPriceNet ||
78
+ flight.totalPriceNet ||
79
+ flight.totalPriceModel ||
80
+ item.totalPriceNet;
81
+ const availableSeats = flight.availableSeats || 0;
82
+ const [showDetails, setShowDetails] = useState(false);
83
+ const [dynamicPrice, setDynamicPrice] = useState(price);
84
+ const [buyHref, setBuyHref] = useState(null);
85
+ const [includeBaggage, setIncludeBaggage] = useState(flight.includeBaggage || false);
86
+ const [baggageText, setBaggageText] = useState(undefined);
87
+ const [baggageImage, setBaggageImage] = useState(undefined);
88
+ const [tooltipData, setTooltipData] = useState(null);
89
+ const tooltipTimer = useRef(null);
90
+ const cardRef = useRef(null);
91
+ const cardLayoutRef = useRef({ x: 0, y: 0 });
92
+ const showTooltip = useCallback((label, x, y, w, h) => {
93
+ if (tooltipTimer.current)
94
+ clearTimeout(tooltipTimer.current);
95
+ setTooltipData({ label, x, y, w, h });
96
+ tooltipTimer.current = setTimeout(() => setTooltipData(null), 3000);
97
+ }, []);
98
+ const dismissTooltip = useCallback(() => {
99
+ if (tooltipTimer.current)
100
+ clearTimeout(tooltipTimer.current);
101
+ setTooltipData(null);
102
+ }, []);
103
+ const chevronRotation = useRef(new Animated.Value(0)).current;
104
+ const { config, dateTimeFormat } = useWidget();
105
+ const formatDuration = (dur) => {
106
+ if (!dur)
107
+ return "\u2014";
108
+ const [h, m] = dur.split(":").map(Number);
109
+ return `${h}${translations["hours_label"] || "h"} ${m
110
+ .toString()
111
+ .padStart(2, "0")}${translations["minutes_label"] || "m"}`;
112
+ };
113
+ const toggleDetails = useCallback(() => {
114
+ const toValue = showDetails ? 0 : 1;
115
+ Animated.timing(chevronRotation, {
116
+ toValue,
117
+ duration: 300,
118
+ useNativeDriver: true,
119
+ }).start();
120
+ setShowDetails(!showDetails);
121
+ }, [showDetails, chevronRotation]);
122
+ const getAirlinesList = () => {
123
+ const airlinesMap = {};
124
+ item.items?.forEach((flightItem) => {
125
+ flightItem.itineraries?.forEach((itinerary) => {
126
+ itinerary.segments?.forEach((segment) => {
127
+ const code = segment.operating?.iata_code || segment.carrier_code || null;
128
+ if (code && !airlinesMap[code]) {
129
+ const rawLogo = typeof segment.operating?.logo_circle === "string"
130
+ ? segment.operating.logo_circle.trim()
131
+ : null;
132
+ const airlineName = segment.operating?.[`name_${lang}`] ||
133
+ segment.operating?.name ||
134
+ airlines?.[code] ||
135
+ code;
136
+ airlinesMap[code] = {
137
+ logo: rawLogo
138
+ ? rawLogo.startsWith("http")
139
+ ? rawLogo
140
+ : `${BASE_URL}${rawLogo}`
141
+ : `${BASE_URL}/uploads/airlines/${code}.png`,
142
+ name: airlineName,
143
+ };
144
+ }
145
+ });
146
+ });
147
+ });
148
+ return Object.values(airlinesMap);
149
+ };
150
+ const getTransferWarnings = () => {
151
+ let transferTime = 0;
152
+ let transferToAnotherAirport = false;
153
+ let nightTransfer = false;
154
+ item.items?.forEach((flightItem) => {
155
+ flightItem.itineraries?.forEach((itinerary) => {
156
+ itinerary.segments?.forEach((segment, segIndex) => {
157
+ const next = itinerary.segments[segIndex + 1];
158
+ if (next) {
159
+ const dep = new Date(segment.arrival?.at);
160
+ const arr = new Date(next.departure?.at);
161
+ const diffMin = (arr.getTime() - dep.getTime()) / 60000;
162
+ if (diffMin > 0)
163
+ transferTime = Math.max(transferTime, diffMin);
164
+ if (diffMin > 360)
165
+ transferTime = diffMin;
166
+ const depAirport = segment.arrival?.iata_code;
167
+ const nextAirport = next.departure?.iata_code;
168
+ if (depAirport && nextAirport && depAirport !== nextAirport)
169
+ transferToAnotherAirport = true;
170
+ const depHour = new Date(segment.arrival?.at).getHours();
171
+ if (depHour >= 0 && depHour <= 5)
172
+ nightTransfer = true;
173
+ }
174
+ });
175
+ });
176
+ });
177
+ return { transferTime, transferToAnotherAirport, nightTransfer };
178
+ };
179
+ const airlinesList = getAirlinesList();
180
+ const { transferTime, transferToAnotherAirport, nightTransfer } = getTransferWarnings();
181
+ const handleBuy = () => {
182
+ const HOST_URL = config?.additional?.basket_link?.trim() || "https://travel-code.com";
183
+ const itemIds = Array.isArray(item?.items)
184
+ ? item.items.map((it) => it.id).join("-")
185
+ : flight.id || "";
186
+ const href = buyHref
187
+ ? buyHref
188
+ : itemIds
189
+ ? `${HOST_URL}/flights/add-to-cart?itemIds=${itemIds}&sessionId=${sessionId || "no-session"}&accessToken=${token}&language=${lang}`
190
+ : "";
191
+ if (href) {
192
+ Linking.openURL(href);
193
+ }
194
+ if (onSelect) {
195
+ onSelect(item);
196
+ }
197
+ };
198
+ const formattedPrice = Number(dynamicPrice || 0).toFixed(2);
199
+ const formattedText = `${currencySign}${formattedPrice}`;
200
+ const chevronRotateInterpolation = chevronRotation.interpolate({
201
+ inputRange: [0, 1],
202
+ outputRange: ["0deg", "180deg"],
203
+ });
204
+ const currentLang = lang || "ru";
205
+ return (_jsxs(View, { style: styles.card, ref: cardRef, onLayout: () => {
206
+ cardRef.current?.measureInWindow((x, y) => {
207
+ cardLayoutRef.current = { x, y };
208
+ });
209
+ }, children: [tooltipData && (_jsx(TooltipBubble, { label: tooltipData.label, triggerX: tooltipData.x, triggerY: tooltipData.y, triggerW: tooltipData.w, triggerH: tooltipData.h, cardX: cardLayoutRef.current.x, cardY: cardLayoutRef.current.y })), _jsxs(View, { style: styles.header, children: [_jsxs(View, { style: styles.airlinesRow, children: [airlinesList.map((airline, i) => (_jsx(TooltipTrigger, { label: airline.name, onShow: showTooltip, onDismiss: dismissTooltip, active: tooltipData?.label === airline.name, children: _jsx(Image, { source: { uri: airline.logo || `${BASE_URL}/images/airline-default.png` }, style: styles.airlineLogo, defaultSource: { uri: `${BASE_URL}/images/airline-default.png` } }) }, i))), flight?.type === "charter" && (_jsx(View, { style: styles.badgeCharter, children: _jsx(Text, { style: styles.badgeCharterText, children: translations["charter"] || "Charter" }) })), flight?.service === "SIRENA_3D" && (_jsx(View, { style: styles.badge3D, children: _jsx(Text, { style: styles.badge3DText, children: "3D" }) })), flight?.service === "FLY_DUBAI" && (_jsx(View, { style: styles.badgeSpecial, children: _jsx(Text, { style: styles.badgeSpecialText, children: translations["special_fare"] || "Special fare" }) })), flight?.outOfPolicy && (_jsx(View, { style: styles.badgePolicy, children: _jsx(Text, { style: styles.badgePolicyText, children: translations["violates_policy"] || "Violates travel policy" }) }))] }), _jsxs(View, { style: styles.warningIcons, children: [nightTransfer && (_jsx(TooltipTrigger, { label: translations["night_transfer"] || "Night transfer", onShow: showTooltip, onDismiss: dismissTooltip, active: tooltipData?.label === (translations["night_transfer"] || "Night transfer"), children: _jsx(View, { style: styles.warningBadge, children: _jsx(Svg, { width: 14, height: 14, viewBox: "0 0 24 24", fill: "none", children: _jsx(Path, { d: "M21 12.79A9 9 0 1111.21 3a7 7 0 109.79 9.79z", fill: "#5B5D6C" }) }) }) })), transferToAnotherAirport && (_jsx(TooltipTrigger, { label: translations["airport_change"] || "Airport change", onShow: showTooltip, onDismiss: dismissTooltip, active: tooltipData?.label === (translations["airport_change"] || "Airport change"), children: _jsx(View, { style: styles.warningBadge, children: _jsx(Svg, { width: 14, height: 14, viewBox: "0 0 24 24", fill: "none", children: _jsx(Path, { d: "M22 16.21v-1.895L14 8V4a2 2 0 10-4 0v4L2 14.316v1.895l8-2.526V18l-2 1.5V21l4-1 4 1v-1.5L14 18v-4.316l8 2.526z", fill: "#5B5D6C" }) }) }) })), transferTime > 360 && (_jsx(TooltipTrigger, { label: translations["long_transfer"] || "Long transfer", onShow: showTooltip, onDismiss: dismissTooltip, active: tooltipData?.label === (translations["long_transfer"] || "Long transfer"), children: _jsx(View, { style: styles.warningBadge, children: _jsx(Svg, { width: 14, height: 14, viewBox: "0 0 24 24", fill: "none", children: _jsx(Path, { d: "M12 2a10 10 0 100 20 10 10 0 000-20zm0 18a8 8 0 110-16 8 8 0 010 16zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67V7z", fill: "#BF3939" }) }) }) }))] })] }), itineraries.map((itinerary, i) => {
210
+ const firstSegment = itinerary?.segments?.[0];
211
+ const lastSegment = itinerary?.segments?.[itinerary?.segments?.length - 1];
212
+ const depIata = firstSegment?.departure?.iata_code;
213
+ const arrIata = lastSegment?.arrival?.iata_code;
214
+ const depAirport = dictionaries?.airports?.[depIata];
215
+ const arrAirport = dictionaries?.airports?.[arrIata];
216
+ const depCity = currentLang === "ru"
217
+ ? depAirport?.city?.title || depAirport?.title || depIata || ""
218
+ : depAirport?.city?.titleEn ||
219
+ depAirport?.titleEn ||
220
+ depIata ||
221
+ "";
222
+ const arrCity = currentLang === "ru"
223
+ ? arrAirport?.city?.title || arrAirport?.title || arrIata || ""
224
+ : arrAirport?.city?.titleEn ||
225
+ arrAirport?.titleEn ||
226
+ arrIata ||
227
+ "";
228
+ const depDate = firstSegment?.departure?.at
229
+ ? new Date(firstSegment.departure.at).toLocaleDateString(currentLang === "ru" ? "ru-RU" : "en-US", { weekday: "short", month: "long", day: "2-digit" })
230
+ : "";
231
+ const arrDate = lastSegment?.arrival?.at
232
+ ? new Date(lastSegment.arrival.at).toLocaleDateString(currentLang === "ru" ? "ru-RU" : "en-US", { weekday: "short", month: "long", day: "2-digit" })
233
+ : "";
234
+ return (_jsx(View, { style: [styles.itinerary, i > 0 && styles.itineraryDivider], children: _jsxs(View, { style: styles.timeInfoRow, children: [_jsxs(View, { style: styles.timeBlock, children: [_jsx(Text, { style: styles.timeText, children: formatTimeByDateTimeFormat(firstSegment?.departure?.at, dateTimeFormat) }), _jsx(Text, { style: styles.cityText, numberOfLines: 1, children: depCity }), _jsx(Text, { style: styles.dateText, children: depDate })] }), _jsxs(View, { style: styles.timelineCenter, children: [_jsxs(View, { style: styles.timelineDuration, children: [_jsxs(Text, { style: styles.durationText, children: [translations["in_flight"] || "In flight", ":", " ", formatDuration(itinerary?.duration)] }), _jsx(Text, { style: [
235
+ styles.transfersText,
236
+ itinerary?.transfers > 0
237
+ ? styles.transfersWithStops
238
+ : styles.transfersDirect,
239
+ ], children: itinerary?.transfers > 0
240
+ ? `${itinerary.transfers} ${translations["transfers"] || "Stops"}`
241
+ : 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
+ transform: [{ rotate: chevronRotateInterpolation }],
244
+ }, 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
+ [flight.airline]: {
246
+ logo: flight.operating?.logo_circle || `${BASE_URL}/images/airline-default.png`,
247
+ name: flight.airline,
248
+ },
249
+ }, includeBaggage: includeBaggage, baggageText: baggageText, baggageImage: baggageImage, translations: translations, lang: lang })] }));
250
+ };
251
+ const styles = StyleSheet.create({
252
+ card: {
253
+ backgroundColor: Colors.surface,
254
+ borderRadius: BorderRadius.lg,
255
+ marginHorizontal: Spacing.lg,
256
+ marginBottom: Spacing.lg,
257
+ padding: Spacing.xl,
258
+ shadowColor: Colors.black,
259
+ shadowOffset: { width: 0, height: 2 },
260
+ shadowOpacity: 0.08,
261
+ shadowRadius: 8,
262
+ elevation: 3,
263
+ },
264
+ header: {
265
+ flexDirection: "row",
266
+ justifyContent: "space-between",
267
+ alignItems: "center",
268
+ marginBottom: Spacing.sm,
269
+ },
270
+ airlinesRow: {
271
+ flexDirection: "row",
272
+ alignItems: "center",
273
+ flexWrap: "wrap",
274
+ flex: 1,
275
+ gap: Spacing.xs,
276
+ },
277
+ airlineLogo: {
278
+ width: 30,
279
+ height: 30,
280
+ borderRadius: BorderRadius.full,
281
+ },
282
+ badgeCharter: {
283
+ backgroundColor: "#FFF3CD",
284
+ paddingHorizontal: Spacing.sm,
285
+ paddingVertical: 2,
286
+ borderRadius: BorderRadius.sm,
287
+ },
288
+ badgeCharterText: {
289
+ fontSize: FontSize.xs,
290
+ color: "#856404",
291
+ fontWeight: "600",
292
+ },
293
+ badge3D: {
294
+ backgroundColor: Colors.primaryLight,
295
+ paddingHorizontal: Spacing.sm,
296
+ paddingVertical: 2,
297
+ borderRadius: BorderRadius.sm,
298
+ },
299
+ badge3DText: {
300
+ fontSize: FontSize.xs,
301
+ color: Colors.primary,
302
+ fontWeight: "700",
303
+ },
304
+ badgeSpecial: {
305
+ backgroundColor: Colors.primaryLight,
306
+ paddingHorizontal: Spacing.sm,
307
+ paddingVertical: 2,
308
+ borderRadius: BorderRadius.sm,
309
+ },
310
+ badgeSpecialText: {
311
+ fontSize: FontSize.xs,
312
+ color: Colors.primary,
313
+ fontWeight: "500",
314
+ },
315
+ badgePolicy: {
316
+ backgroundColor: "#FEE2E2",
317
+ paddingHorizontal: Spacing.sm,
318
+ paddingVertical: 2,
319
+ borderRadius: BorderRadius.sm,
320
+ },
321
+ badgePolicyText: {
322
+ fontSize: FontSize.xs,
323
+ color: Colors.error,
324
+ fontWeight: "500",
325
+ },
326
+ warningIcons: {
327
+ flexDirection: "row",
328
+ gap: Spacing.xs,
329
+ },
330
+ warningBadge: {
331
+ width: 30,
332
+ height: 30,
333
+ borderRadius: BorderRadius.full,
334
+ backgroundColor: Colors.surfaceSecondary,
335
+ justifyContent: "center",
336
+ alignItems: "center",
337
+ },
338
+ itinerary: {
339
+ marginBottom: Spacing.sm,
340
+ },
341
+ itineraryDivider: {
342
+ borderTopWidth: 1,
343
+ borderTopColor: "#E4E6EE",
344
+ paddingTop: Spacing.lg,
345
+ marginTop: Spacing.lg,
346
+ },
347
+ timeInfoRow: {
348
+ flexDirection: "row",
349
+ alignItems: "flex-start",
350
+ },
351
+ timeBlock: {
352
+ alignItems: "flex-start",
353
+ },
354
+ timeBlockRight: {
355
+ alignItems: "flex-end",
356
+ },
357
+ timeText: {
358
+ fontSize: FontSize.xl,
359
+ fontWeight: "600",
360
+ color: "#191C30",
361
+ lineHeight: 28,
362
+ marginBottom: Spacing.sm,
363
+ },
364
+ cityText: {
365
+ fontSize: FontSize.sm,
366
+ color: "#5B5D6C",
367
+ lineHeight: 20,
368
+ },
369
+ dateText: {
370
+ fontSize: FontSize.sm,
371
+ color: "#5B5D6C",
372
+ lineHeight: 20,
373
+ },
374
+ timelineCenter: {
375
+ flex: 1,
376
+ paddingHorizontal: Spacing.lg,
377
+ alignItems: "center",
378
+ },
379
+ timelineDuration: {
380
+ flexDirection: "column",
381
+ alignItems: "center",
382
+ gap: Spacing.sm,
383
+ marginBottom: Spacing.sm,
384
+ },
385
+ durationText: {
386
+ fontSize: FontSize.sm,
387
+ color: "#5B5D6C",
388
+ },
389
+ transfersText: {
390
+ fontSize: FontSize.sm,
391
+ fontWeight: "400",
392
+ },
393
+ transfersWithStops: {
394
+ color: "#BF3939",
395
+ },
396
+ transfersDirect: {
397
+ color: "#3D7B50",
398
+ },
399
+ noSegmentsText: {
400
+ fontSize: FontSize.xs,
401
+ color: Colors.textTertiary,
402
+ },
403
+ priceSection: {
404
+ borderTopWidth: 1,
405
+ borderTopColor: "#E4E6EE",
406
+ paddingTop: Spacing.md,
407
+ alignItems: "center",
408
+ },
409
+ buyButton: {
410
+ backgroundColor: Colors.primary,
411
+ borderRadius: BorderRadius.xl,
412
+ paddingVertical: Spacing.md,
413
+ paddingHorizontal: Spacing.xxl,
414
+ marginTop: Spacing.sm,
415
+ width: "100%",
416
+ alignItems: "center",
417
+ },
418
+ buyButtonLoading: {
419
+ opacity: 0.7,
420
+ },
421
+ buyButtonText: {
422
+ color: Colors.textOnPrimary,
423
+ fontSize: FontSize.md,
424
+ fontWeight: "600",
425
+ },
426
+ seatsText: {
427
+ fontSize: FontSize.sm,
428
+ color: Colors.textTertiary,
429
+ marginTop: Spacing.xs,
430
+ },
431
+ detailsButton: {
432
+ alignItems: "center",
433
+ paddingVertical: Spacing.sm,
434
+ },
435
+ });
436
+ export default FlightCard;
@@ -0,0 +1,12 @@
1
+ import React from "react";
2
+ type FlightInfoProps = {
3
+ departureCity?: string;
4
+ arrivalCity?: string;
5
+ departureTime?: string;
6
+ arrivalTime?: string;
7
+ duration?: string;
8
+ transfers?: number;
9
+ translations: Record<string, string>;
10
+ };
11
+ declare const FlightInfo: React.FC<FlightInfoProps>;
12
+ export default FlightInfo;
@@ -0,0 +1,48 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { View, Text, StyleSheet } from "react-native";
3
+ import { Colors, Spacing, FontSize } from "../../../../theme/colors";
4
+ const FlightInfo = ({ departureCity, arrivalCity, departureTime, arrivalTime, duration, transfers = 0, translations, }) => {
5
+ return (_jsx(View, { style: styles.container, children: _jsxs(View, { style: styles.row, children: [_jsxs(View, { style: styles.cityBlock, children: [departureTime ? (_jsx(Text, { style: styles.time, children: departureTime })) : null, departureCity ? (_jsx(Text, { style: styles.city, numberOfLines: 1, children: departureCity })) : null] }), _jsxs(View, { style: styles.middleBlock, children: [duration ? (_jsx(Text, { style: styles.duration, children: duration })) : null, _jsx(Text, { style: styles.transfers, children: transfers > 0
6
+ ? `${transfers} ${translations["transfers"] || "stops"}`
7
+ : translations["direct_flight"] || "Direct" })] }), _jsxs(View, { style: [styles.cityBlock, styles.cityBlockRight], children: [arrivalTime ? (_jsx(Text, { style: styles.time, children: arrivalTime })) : null, arrivalCity ? (_jsx(Text, { style: styles.city, numberOfLines: 1, children: arrivalCity })) : null] })] }) }));
8
+ };
9
+ const styles = StyleSheet.create({
10
+ container: {
11
+ paddingVertical: Spacing.xs,
12
+ },
13
+ row: {
14
+ flexDirection: "row",
15
+ alignItems: "center",
16
+ },
17
+ cityBlock: {
18
+ width: 70,
19
+ },
20
+ cityBlockRight: {
21
+ alignItems: "flex-end",
22
+ },
23
+ time: {
24
+ fontSize: FontSize.lg,
25
+ fontWeight: "700",
26
+ color: Colors.text,
27
+ },
28
+ city: {
29
+ fontSize: FontSize.xs,
30
+ color: Colors.textSecondary,
31
+ marginTop: 2,
32
+ },
33
+ middleBlock: {
34
+ flex: 1,
35
+ alignItems: "center",
36
+ },
37
+ duration: {
38
+ fontSize: FontSize.xs,
39
+ color: Colors.textSecondary,
40
+ },
41
+ transfers: {
42
+ fontSize: FontSize.xs,
43
+ color: Colors.primary,
44
+ fontWeight: "500",
45
+ marginTop: 2,
46
+ },
47
+ });
48
+ export default FlightInfo;
@@ -0,0 +1,11 @@
1
+ import React from "react";
2
+ type FlightPriceBlockProps = {
3
+ price: number | string;
4
+ currencySign?: string;
5
+ loading?: boolean;
6
+ onBuy: () => void;
7
+ availableSeats?: number;
8
+ translations: Record<string, string>;
9
+ };
10
+ declare const FlightPriceBlock: React.FC<FlightPriceBlockProps>;
11
+ export default FlightPriceBlock;
@@ -0,0 +1,36 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ import { View, Text, TouchableOpacity, StyleSheet } from "react-native";
3
+ import { Colors, Spacing, FontSize, BorderRadius } from "../../../../theme/colors";
4
+ const FlightPriceBlock = ({ price, currencySign = "", loading = false, onBuy, availableSeats = 0, translations, }) => {
5
+ const formattedPrice = Number(price || 0).toFixed(2);
6
+ 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"] }))] }));
8
+ };
9
+ const styles = StyleSheet.create({
10
+ container: {
11
+ alignItems: "center",
12
+ paddingVertical: Spacing.sm,
13
+ },
14
+ buyButton: {
15
+ backgroundColor: Colors.primary,
16
+ borderRadius: BorderRadius.md,
17
+ paddingVertical: Spacing.md,
18
+ paddingHorizontal: Spacing.xxl,
19
+ width: "100%",
20
+ alignItems: "center",
21
+ },
22
+ buyButtonLoading: {
23
+ opacity: 0.7,
24
+ },
25
+ buyButtonText: {
26
+ color: Colors.textOnPrimary,
27
+ fontSize: FontSize.lg,
28
+ fontWeight: "700",
29
+ },
30
+ seatsText: {
31
+ fontSize: FontSize.xs,
32
+ color: Colors.textTertiary,
33
+ marginTop: Spacing.xs,
34
+ },
35
+ });
36
+ export default FlightPriceBlock;
@@ -0,0 +1,22 @@
1
+ import React from "react";
2
+ type FlightResultsProps = {
3
+ results: any[];
4
+ loading: boolean;
5
+ isFiltering?: boolean;
6
+ total?: number;
7
+ limit?: number;
8
+ offset?: number;
9
+ fetchPage?: (page: number, filters?: Record<string, any>) => void;
10
+ dictionaries?: any;
11
+ airlines?: Record<string, string>;
12
+ sessionId?: string | null;
13
+ filtersData?: any;
14
+ isPageLoading?: boolean;
15
+ translations: Record<string, string>;
16
+ lang: string;
17
+ currencySign?: string;
18
+ onFlightSelect?: (item: any) => void;
19
+ headerComponent?: React.ReactElement;
20
+ };
21
+ declare const FlightResults: React.FC<FlightResultsProps>;
22
+ export default FlightResults;