import { GetNodesDocument, GetHubCountriesDocument, NearestHubsDocument } from '~/composables/graphql/public/geo-generated' const PAGE_SIZE = 24 // Shared state across list and map views const items = ref([]) const total = ref(0) const selectedFilter = ref('all') const selectedCountry = ref('all') const countries = ref([]) const isLoading = ref(false) const isLoadingMore = ref(false) const isInitialized = ref(false) const filterProductUuid = ref(null) const filterBounds = ref<{ west: number; south: number; east: number; north: number } | null>(null) export function useCatalogHubs() { const { t } = useI18n() const { execute } = useGraphQL() const filters = computed(() => [ { id: 'all', label: t('catalogHubsSection.filters.all') }, { id: 'auto', label: t('catalogHubsSection.filters.auto') }, { id: 'rail', label: t('catalogHubsSection.filters.rail') }, { id: 'sea', label: t('catalogHubsSection.filters.sea') }, { id: 'air', label: t('catalogHubsSection.filters.air') } ]) const countryFilters = computed(() => [ { id: 'all', label: t('catalogHubsSection.filters.all_countries') }, ...countries.value.map(c => ({ id: c, label: c })) ]) const itemsWithCoords = computed(() => items.value.filter(h => h.latitude && h.longitude) ) const itemsByCountry = computed(() => { const grouped = new Map() items.value.forEach(hub => { const country = hub.country || t('catalogMap.labels.country_unknown') if (!grouped.has(country)) grouped.set(country, []) grouped.get(country)!.push(hub) }) return Array.from(grouped.entries()) .map(([name, hubsList]) => ({ name, hubs: hubsList })) .sort((a, b) => a.name.localeCompare(b.name)) }) const canLoadMore = computed(() => items.value.length < total.value) const fetchPage = async (offset: number, replace = false) => { if (replace) isLoading.value = true try { // If filtering by product, use nearestHubs with global search // (center point 0,0 with very large radius to cover entire globe) if (filterProductUuid.value) { const data = await execute( NearestHubsDocument, { lat: 0, lon: 0, radius: 20000, // 20000 km radius covers entire Earth productUuid: filterProductUuid.value, limit: 500 // Increased limit for global search }, 'public', 'geo' ) const next = data?.nearestHubs || [] items.value = next total.value = next.length isInitialized.value = true return } // Default: fetch all hubs with filters const transportType = selectedFilter.value === 'all' ? null : selectedFilter.value const country = selectedCountry.value === 'all' ? null : selectedCountry.value const bounds = filterBounds.value // Validate bounds coordinates const validBounds = bounds && typeof bounds.west === 'number' && isFinite(bounds.west) && typeof bounds.south === 'number' && isFinite(bounds.south) && typeof bounds.east === 'number' && isFinite(bounds.east) && typeof bounds.north === 'number' && isFinite(bounds.north) const data = await execute( GetNodesDocument, { limit: PAGE_SIZE, offset, transportType, country, ...(validBounds ? { west: bounds.west, south: bounds.south, east: bounds.east, north: bounds.north } : {}) }, 'public', 'geo' ) const next = data?.nodes || [] items.value = replace ? next : items.value.concat(next) total.value = data?.nodesCount ?? total.value isInitialized.value = true } finally { isLoading.value = false } } const loadCountries = async () => { try { const data = await execute(GetHubCountriesDocument, {}, 'public', 'geo') countries.value = (data?.hubCountries || []).filter((c): c is string => c !== null) } catch (e) { console.error('Failed to load hub countries', e) } } const loadMore = async () => { if (isLoadingMore.value) return isLoadingMore.value = true try { await fetchPage(items.value.length) } finally { isLoadingMore.value = false } } // При смене фильтра - перезагрузка watch([selectedFilter, selectedCountry], () => { if (isInitialized.value) { fetchPage(0, true) } }) const setProductFilter = (uuid: string | null) => { if (filterProductUuid.value === uuid) return // Early return if unchanged filterProductUuid.value = uuid if (isInitialized.value) { fetchPage(0, true) } } const setBoundsFilter = (bounds: { west: number; south: number; east: number; north: number } | null) => { filterBounds.value = bounds if (isInitialized.value) { fetchPage(0, true) } } // Initialize data if not already loaded const init = async () => { if (!isInitialized.value && items.value.length === 0) { await Promise.all([ fetchPage(0, true), loadCountries() ]) } } return { items, total, selectedFilter, selectedCountry, filters, countryFilters, isLoading, isLoadingMore, itemsWithCoords, itemsByCountry, canLoadMore, fetchPage, loadMore, init, setProductFilter, setBoundsFilter } }