Files
webapp/app/composables/useCatalogProducts.ts
Ruslan Bakiev 19aca61845
All checks were successful
Build Docker Image / build (push) Successful in 4m14s
fix(catalog): prevent unnecessary list reloads on map movement
- Remove currentMapBounds from watch - it changes on every map move
- Watch only filterByBounds and urlBounds (URL-based state)
- Add early return in setBoundsFilter if bounds haven't changed

This fixes the issue where the list was reloading on every map movement
even when the 'filter by bounds' checkbox was OFF.
2026-01-26 22:24:47 +07:00

214 lines
5.9 KiB
TypeScript

import {
ProductsListDocument,
GetNodeDocument,
NearestOffersDocument
} from '~/composables/graphql/public/geo-generated'
import {
GetSupplierProfileDocument
} from '~/composables/graphql/public/exchange-generated'
// Shared state
const items = ref<any[]>([])
const isLoading = ref(false)
const isLoadingMore = ref(false)
const isInitialized = ref(false)
// Filter state
const filterSupplierUuid = ref<string | null>(null)
const filterHubUuid = ref<string | null>(null)
const filterBounds = ref<{ west: number; south: number; east: number; north: number } | null>(null)
export function useCatalogProducts() {
const { execute } = useGraphQL()
// Products don't have server-side pagination yet, so we load all at once
const canLoadMore = computed(() => false)
const fetchProducts = async () => {
if (isLoading.value) return
isLoading.value = true
try {
let data
if (filterSupplierUuid.value) {
// Products from specific supplier - get supplier coordinates first
const supplierData = await execute(
GetSupplierProfileDocument,
{ uuid: filterSupplierUuid.value },
'public',
'exchange'
)
const supplier = supplierData?.getSupplierProfile
if (!supplier?.latitude || !supplier?.longitude) {
console.warn('Supplier has no coordinates')
items.value = []
} else {
// Get offers near supplier and group by product
const offersData = await execute(
NearestOffersDocument,
{
lat: supplier.latitude,
lon: supplier.longitude,
radius: 500
},
'public',
'geo'
)
// Group offers by product
const productsMap = new Map<string, any>()
offersData?.nearestOffers?.forEach((offer: any) => {
if (offer?.productUuid) {
if (!productsMap.has(offer.productUuid)) {
productsMap.set(offer.productUuid, {
uuid: offer.productUuid,
name: offer.productName,
offersCount: 0
})
}
productsMap.get(offer.productUuid)!.offersCount++
}
})
items.value = Array.from(productsMap.values())
}
} else if (filterHubUuid.value) {
// Products near hub - get hub coordinates first
const hubData = await execute(
GetNodeDocument,
{ uuid: filterHubUuid.value },
'public',
'geo'
)
const hub = hubData?.node
if (!hub?.latitude || !hub?.longitude) {
console.warn('Hub has no coordinates')
items.value = []
} else {
// Get offers near hub and group by product
const offersData = await execute(
NearestOffersDocument,
{
lat: hub.latitude,
lon: hub.longitude,
radius: 500
},
'public',
'geo'
)
// Group offers by product
const productsMap = new Map<string, any>()
offersData?.nearestOffers?.forEach((offer: any) => {
if (offer?.productUuid) {
if (!productsMap.has(offer.productUuid)) {
productsMap.set(offer.productUuid, {
uuid: offer.productUuid,
name: offer.productName,
offersCount: 0
})
}
productsMap.get(offer.productUuid)!.offersCount++
}
})
items.value = Array.from(productsMap.values())
}
} else {
// All products from graph
data = await execute(
ProductsListDocument,
{
limit: 500,
...(filterBounds.value && {
west: filterBounds.value.west,
south: filterBounds.value.south,
east: filterBounds.value.east,
north: filterBounds.value.north
})
},
'public',
'geo'
)
items.value = data?.productsList || []
}
isInitialized.value = true
} finally {
isLoading.value = false
}
}
const loadMore = async () => {
// No-op: products don't support pagination yet
}
const init = async () => {
if (!isInitialized.value && items.value.length === 0) {
await fetchProducts()
}
}
// Filter setters
const setSupplierFilter = (uuid: string | null) => {
if (filterSupplierUuid.value !== uuid) {
filterSupplierUuid.value = uuid
filterHubUuid.value = null // clear other filter
isInitialized.value = false
fetchProducts()
}
}
const setHubFilter = (uuid: string | null) => {
if (filterHubUuid.value !== uuid) {
filterHubUuid.value = uuid
filterSupplierUuid.value = null // clear other filter
isInitialized.value = false
fetchProducts()
}
}
const clearFilters = () => {
if (filterSupplierUuid.value || filterHubUuid.value) {
filterSupplierUuid.value = null
filterHubUuid.value = null
isInitialized.value = false
fetchProducts()
}
}
// Products are filtered by offer locations within bounds
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) {
fetchProducts()
}
}
return {
items,
isLoading,
isLoadingMore,
isInitialized,
canLoadMore,
fetchProducts,
loadMore,
init,
setSupplierFilter,
setHubFilter,
clearFilters,
setBoundsFilter
}
}