import type { SuppliersListQueryResult, NearestSuppliersQueryResult } from '~/composables/graphql/public/geo-generated' import { SuppliersListDocument, NearestSuppliersDocument } from '~/composables/graphql/public/geo-generated' const PAGE_SIZE = 24 // Types from codegen type SupplierItem = NonNullable[number]> type NearestSupplierItem = NonNullable[number]> // Shared state across list and map views const items = ref>([]) const total = ref(0) 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 useCatalogSuppliers() { const { execute } = useGraphQL() const itemsWithCoords = computed(() => items.value.filter((s): s is NearestSupplierItem => 'latitude' in s && 'longitude' in s && s.latitude != null && s.longitude != null) ) 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 nearestSuppliers (product-only list) if (filterProductUuid.value) { const data = await execute( NearestSuppliersDocument, { lat: 0, lon: 0, productUuid: filterProductUuid.value, limit: 500 // Increased limit for global search }, 'public', 'geo' ) items.value = (data?.nearestSuppliers || []).filter((s): s is NearestSupplierItem => s !== null) total.value = items.value.length isInitialized.value = true return } // Default: fetch all suppliers from GEO (graph-based) const data = await execute( SuppliersListDocument, { limit: PAGE_SIZE, offset, ...(filterBounds.value && { west: filterBounds.value.west, south: filterBounds.value.south, east: filterBounds.value.east, north: filterBounds.value.north }) }, 'public', 'geo' ) const next = (data?.suppliersList || []).filter((s): s is SupplierItem => s !== null) items.value = replace ? next : items.value.concat(next) // suppliersList doesn't return total count, estimate from fetched items if (replace) { total.value = next.length < PAGE_SIZE ? next.length : next.length + PAGE_SIZE } else if (next.length < PAGE_SIZE) { total.value = items.value.length } isInitialized.value = true } finally { isLoading.value = false } } const loadMore = async () => { if (isLoadingMore.value) return isLoadingMore.value = true try { await fetchPage(items.value.length) } finally { isLoadingMore.value = false } } // Initialize data if not already loaded const init = async () => { if (!isInitialized.value && items.value.length === 0) { await fetchPage(0, true) } } const setProductFilter = (uuid: string | null) => { if (filterProductUuid.value === uuid) return // Early return if unchanged filterProductUuid.value = uuid fetchPage(0, true) } const setBoundsFilter = (bounds: { west: number; south: number; east: number; north: number } | null) => { // Early return if bounds haven't changed const prev = filterBounds.value const same = prev === bounds || ( prev && bounds && prev.west === bounds.west && prev.south === bounds.south && prev.east === bounds.east && prev.north === bounds.north ) if (same) return filterBounds.value = bounds if (isInitialized.value) { fetchPage(0, true) } } return { items, total, isLoading, isLoadingMore, itemsWithCoords, canLoadMore, fetchPage, loadMore, init, setProductFilter, setBoundsFilter } }