@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,80 @@
1
+ import { useState, useCallback } from "react";
2
+ import { useApiToken } from "../../utils/getToken";
3
+ import AsyncStorage from "@react-native-async-storage/async-storage";
4
+ const CACHE_KEY = "hotelDetailsCache";
5
+ const CACHE_TTL = 1000 * 60 * 60;
6
+ export const useHotelDetails = (searchParams) => {
7
+ const token = useApiToken();
8
+ const [loading, setLoading] = useState(false);
9
+ const [hotelData, setHotelData] = useState(null);
10
+ const [error, setError] = useState(null);
11
+ const readCache = async () => {
12
+ try {
13
+ const raw = await AsyncStorage.getItem(CACHE_KEY);
14
+ const cache = JSON.parse(raw || "{}");
15
+ for (const key in cache) {
16
+ if (Date.now() - cache[key]._ts > CACHE_TTL) {
17
+ delete cache[key];
18
+ }
19
+ }
20
+ return cache;
21
+ }
22
+ catch {
23
+ return {};
24
+ }
25
+ };
26
+ const writeCache = async (cache) => AsyncStorage.setItem(CACHE_KEY, JSON.stringify(cache));
27
+ const clearCache = () => AsyncStorage.removeItem(CACHE_KEY);
28
+ const fetchHotelDetails = useCallback(async (hotelId) => {
29
+ if (!searchParams)
30
+ return null;
31
+ setLoading(true);
32
+ setError(null);
33
+ const cache = await readCache();
34
+ const cachedEntry = cache[hotelId];
35
+ if (cachedEntry && Object.keys(cachedEntry).length > 1) {
36
+ const cached = cachedEntry;
37
+ console.log("[useHotelDetails] cached data for", hotelId, ":", JSON.stringify(cached, null, 2));
38
+ await new Promise((res) => setTimeout(res, 300));
39
+ setHotelData(cached);
40
+ setLoading(false);
41
+ return cached;
42
+ }
43
+ try {
44
+ const response = await fetch("https://api.travel-code.com/v1/search/hotels/list", {
45
+ method: "POST",
46
+ headers: {
47
+ "Content-Type": "application/json",
48
+ Authorization: `Bearer ${token}`,
49
+ },
50
+ body: JSON.stringify({
51
+ ...searchParams,
52
+ id: [hotelId],
53
+ }),
54
+ });
55
+ const data = await response.json();
56
+ console.log("[useHotelDetails] /list response:", JSON.stringify(data, null, 2));
57
+ const hotel = data?.hotels?.[0] || data?.data?.[0] || null;
58
+ if (!hotel) {
59
+ setError("Failed to load hotel data");
60
+ setHotelData(null);
61
+ return null;
62
+ }
63
+ setHotelData(hotel);
64
+ await writeCache({
65
+ ...cache,
66
+ [hotelId]: { ...hotel, _ts: Date.now() },
67
+ });
68
+ return hotel;
69
+ }
70
+ catch (err) {
71
+ console.error("Hotel details error:", err);
72
+ setError("Request error");
73
+ return null;
74
+ }
75
+ finally {
76
+ setLoading(false);
77
+ }
78
+ }, [searchParams, token]);
79
+ return { hotelData, loading, error, fetchHotelDetails, clearCache };
80
+ };
@@ -0,0 +1,5 @@
1
+ export declare function useHotelLocations(query: string): {
2
+ results: any[];
3
+ loading: boolean;
4
+ fetchLocations: (term: string) => Promise<void>;
5
+ };
@@ -0,0 +1,42 @@
1
+ import { useState, useEffect } from "react";
2
+ import { useApiToken } from "../../utils/getToken";
3
+ const API_URL = "https://api.travel-code.com/v1/data/hotel-locations";
4
+ export function useHotelLocations(query) {
5
+ const token = useApiToken();
6
+ const [results, setResults] = useState([]);
7
+ const [loading, setLoading] = useState(false);
8
+ const fetchLocations = async (term) => {
9
+ try {
10
+ setLoading(true);
11
+ const res = await fetch(`${API_URL}?search=${encodeURIComponent(term)}`, {
12
+ headers: {
13
+ "Content-Type": "application/json",
14
+ Authorization: `Bearer ${token}`,
15
+ },
16
+ });
17
+ if (!res.ok)
18
+ throw new Error(`Ошибка ${res.status}`);
19
+ const data = await res.json();
20
+ setResults(data.items || []);
21
+ }
22
+ catch (err) {
23
+ console.warn("⚠️ Ошибка загрузки локаций:", err);
24
+ setResults([]);
25
+ }
26
+ finally {
27
+ setLoading(false);
28
+ }
29
+ };
30
+ useEffect(() => {
31
+ if (!query || query.length < 2) {
32
+ setResults([]);
33
+ setLoading(false);
34
+ return;
35
+ }
36
+ const debounced = setTimeout(() => {
37
+ fetchLocations(query);
38
+ }, 400);
39
+ return () => clearTimeout(debounced);
40
+ }, [query]);
41
+ return { results, loading, fetchLocations };
42
+ }
@@ -0,0 +1,12 @@
1
+ export type HotelPin = {
2
+ id: string;
3
+ latitude: number;
4
+ longitude: number;
5
+ total: number | null;
6
+ soldOut: boolean;
7
+ };
8
+ export declare const useHotelMapData: (params: Record<string, any>, shouldFetch?: boolean) => {
9
+ pins: HotelPin[];
10
+ loading: boolean;
11
+ fetchFailed: boolean;
12
+ };
@@ -0,0 +1,78 @@
1
+ import { useState, useEffect, useRef, useCallback } from "react";
2
+ import { useApiToken } from "../../utils/getToken";
3
+ const API_BASE = "https://api.travel-code.com/v1/search/hotels/map";
4
+ export const useHotelMapData = (params, shouldFetch = true) => {
5
+ const token = useApiToken();
6
+ const [pins, setPins] = useState([]);
7
+ const [loading, setLoading] = useState(false);
8
+ const [fetchFailed, setFetchFailed] = useState(false);
9
+ const lastParamsRef = useRef("");
10
+ const fetchPins = useCallback(async () => {
11
+ console.log("%c[useHotelMapData] 🚀 СТАРТ ЗАПРОСА", "color:#fff;background:#43a047;padding:3px 6px;border-radius:4px", { params });
12
+ setLoading(true);
13
+ setFetchFailed(false);
14
+ try {
15
+ const response = await fetch(API_BASE, {
16
+ method: "POST",
17
+ headers: {
18
+ "Content-Type": "application/json",
19
+ Authorization: `Bearer ${token}`,
20
+ },
21
+ body: JSON.stringify(params),
22
+ });
23
+ console.log("%c[useHotelMapData] 📥 ОТВЕТ ПОЛУЧЕН", "color:#fff;background:#5e35b1;padding:3px 6px;border-radius:4px", { status: response.status });
24
+ if (!response.ok)
25
+ throw new Error(`Ошибка ${response.status}`);
26
+ const result = await response.json();
27
+ console.log("%c[useHotelMapData] 🟢 pins:", "color:#fff;background:#00897b;padding:3px 6px;border-radius:4px", result?.data?.length);
28
+ if (result?.data?.length) {
29
+ const formatted = result.data
30
+ .filter((item) => {
31
+ const lat = parseFloat(item.latitude);
32
+ const lng = parseFloat(item.longitude);
33
+ return lat && lng && lat !== 0 && lng !== 0;
34
+ })
35
+ .map((item) => ({
36
+ id: String(item.propertyId),
37
+ latitude: parseFloat(item.latitude),
38
+ longitude: parseFloat(item.longitude),
39
+ total: typeof item.total === "number" ? item.total : null,
40
+ soldOut: item.soldOut === true ||
41
+ item.total === null,
42
+ }));
43
+ setPins(formatted);
44
+ }
45
+ else {
46
+ console.log("%c[useHotelMapData] ⚠️ Нет данных", "color:#fb8c00");
47
+ setPins([]);
48
+ }
49
+ }
50
+ catch (err) {
51
+ console.error("%c[useHotelMapData] 🔥 ОШИБКА", "background:red;color:white", err);
52
+ setFetchFailed(true);
53
+ }
54
+ finally {
55
+ console.log("%c[useHotelMapData] ⏹ ЗАПРОС ЗАВЕРШЕН", "color:#fff;background:#6d4c41;padding:3px 6px;border-radius:4px");
56
+ setLoading(false);
57
+ }
58
+ }, [params, token]);
59
+ useEffect(() => {
60
+ console.log("%c[useHotelMapData] EFFECT TRIGGERED", "color:#fff;background:#1e88e5;padding:3px 6px;border-radius:4px", { params, shouldFetch });
61
+ if (!shouldFetch) {
62
+ console.log("%c[useHotelMapData] ❌ shouldFetch = false → запрос НЕ отправлен", "color:#ef5350");
63
+ return;
64
+ }
65
+ if (!params || Object.keys(params).length === 0) {
66
+ console.log("%c[useHotelMapData] ❌ Пустые params → запрос НЕ отправлен", "color:#ef5350");
67
+ return;
68
+ }
69
+ const key = JSON.stringify(params);
70
+ if (key === lastParamsRef.current) {
71
+ console.log("%c[useHotelMapData] 🔁 Те же params → запрос пропущен", "color:#ffa726");
72
+ return;
73
+ }
74
+ lastParamsRef.current = key;
75
+ fetchPins();
76
+ }, [params, shouldFetch, fetchPins]);
77
+ return { pins, loading, fetchFailed };
78
+ };
@@ -0,0 +1,11 @@
1
+ export declare const useHotelOffers: () => {
2
+ offers: any[];
3
+ loading: boolean;
4
+ error: string | null;
5
+ total: number;
6
+ offset: number;
7
+ limit: number;
8
+ fetchHotelOffers: (params: any) => Promise<void>;
9
+ fetchPage: (page: number, baseParams: Record<string, any>) => Promise<void>;
10
+ reset: () => void;
11
+ };
@@ -0,0 +1,122 @@
1
+ import { useState, useRef } from "react";
2
+ import { useApiToken } from "../../utils/getToken";
3
+ const POLL_INTERVAL = 1000;
4
+ const POLL_TIMEOUT = 180000;
5
+ export const useHotelOffers = () => {
6
+ const token = useApiToken();
7
+ const [offers, setOffers] = useState([]);
8
+ const [loading, setLoading] = useState(false);
9
+ const [error, setError] = useState(null);
10
+ const [total, setTotal] = useState(0);
11
+ const [offset, setOffset] = useState(0);
12
+ const [limit, setLimit] = useState(20);
13
+ const pollTimer = useRef(null);
14
+ const applyResult = (data, params) => {
15
+ setOffers(Array.isArray(data?.data) ? data.data : []);
16
+ setTotal(Number(data?.count || 0));
17
+ setOffset(params.offset ?? 0);
18
+ setLimit(params.limit ?? 20);
19
+ };
20
+ const reset = () => {
21
+ if (pollTimer.current) {
22
+ clearTimeout(pollTimer.current);
23
+ }
24
+ setOffers([]);
25
+ setLoading(false);
26
+ setError(null);
27
+ setTotal(0);
28
+ setOffset(0);
29
+ setLimit(20);
30
+ };
31
+ const pollTask = async (taskId, params) => {
32
+ const start = Date.now();
33
+ return new Promise((resolve, reject) => {
34
+ const check = async () => {
35
+ try {
36
+ const pollBody = {
37
+ ...params,
38
+ taskId,
39
+ };
40
+ const response = await fetch("https://api.travel-code.com/v1/search/hotels/task", {
41
+ method: "POST",
42
+ headers: {
43
+ "Content-Type": "application/json",
44
+ Authorization: `Bearer ${token}`,
45
+ },
46
+ body: JSON.stringify(pollBody),
47
+ });
48
+ if (!response.ok) {
49
+ reject(new Error("Ошибка при запросе /task"));
50
+ return;
51
+ }
52
+ const json = await response.json();
53
+ if (json.status === "done" && json.data) {
54
+ applyResult(json, params);
55
+ resolve();
56
+ return;
57
+ }
58
+ if (json.status === "lost" || json.status === "timeout") {
59
+ reject(new Error("Поиск завершился со статусом lost/timeout"));
60
+ return;
61
+ }
62
+ if (Date.now() - start > POLL_TIMEOUT) {
63
+ reject(new Error("Истекло время ожидания taskId"));
64
+ return;
65
+ }
66
+ pollTimer.current = setTimeout(check, POLL_INTERVAL);
67
+ }
68
+ catch (e) {
69
+ reject(e);
70
+ }
71
+ };
72
+ check();
73
+ });
74
+ };
75
+ const fetchHotelOffers = async (params) => {
76
+ try {
77
+ setLoading(true);
78
+ setError(null);
79
+ const response = await fetch("https://api.travel-code.com/v1/search/hotels", {
80
+ method: "POST",
81
+ headers: {
82
+ "Content-Type": "application/json",
83
+ Authorization: `Bearer ${token}`,
84
+ },
85
+ body: JSON.stringify(params),
86
+ });
87
+ if (!response.ok)
88
+ throw new Error("Ошибка при запуске поиска отелей");
89
+ const json = await response.json();
90
+ if (json.data) {
91
+ applyResult(json, params);
92
+ return;
93
+ }
94
+ if (json.taskId) {
95
+ await pollTask(json.taskId, params);
96
+ return;
97
+ }
98
+ throw new Error("Неизвестный формат ответа от API");
99
+ }
100
+ catch (err) {
101
+ setError(err.message);
102
+ }
103
+ finally {
104
+ setLoading(false);
105
+ }
106
+ };
107
+ const fetchPage = async (page, baseParams) => {
108
+ const newOffset = (page - 1) * limit;
109
+ await fetchHotelOffers({ ...baseParams, offset: newOffset, limit });
110
+ };
111
+ return {
112
+ offers,
113
+ loading,
114
+ error,
115
+ total,
116
+ offset,
117
+ limit,
118
+ fetchHotelOffers,
119
+ fetchPage,
120
+ reset
121
+ };
122
+ };
@@ -0,0 +1,18 @@
1
+ export declare function useHotelSearchSSE(): {
2
+ offers: any[];
3
+ allHotels: any[];
4
+ loading: boolean;
5
+ isStreaming: boolean;
6
+ isPageLoading: boolean;
7
+ error: string | null;
8
+ total: number;
9
+ offset: number;
10
+ limit: number;
11
+ cacheKey: string | null;
12
+ streamCompleted: boolean;
13
+ searchParams: Record<string, any> | null;
14
+ startSearch: (params: Record<string, any>) => void;
15
+ filterSearch: (params: Record<string, any>) => void;
16
+ fetchPage: (page: number) => void;
17
+ reset: () => void;
18
+ };