All checks were successful
Build Docker Image / build (push) Successful in 4m14s
- 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.
214 lines
5.9 KiB
TypeScript
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
|
|
}
|
|
}
|