Refactor catalog to use coordinate-based GraphQL endpoints
All checks were successful
Build Docker Image / build (push) Successful in 3m33s
All checks were successful
Build Docker Image / build (push) Successful in 3m33s
Replace entity-specific queries (GetProductsNearHub, GetOffersByHub, GetHubsForProduct, GetSuppliersForProduct) with unified coordinate-based endpoints (NearestHubs, NearestOffers, NearestSuppliers, RouteToCoordinate). This simplifies backend architecture from 18 to 8 core endpoints while maintaining identical UI/UX behavior. All composables and pages now use coordinates + client-side grouping instead of specialized backend queries. For global product filtering, uses center point (0,0) with 20000km radius. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -64,7 +64,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { GetOffersByHubDocument } from '~/composables/graphql/public/geo-generated'
|
import { GetNodeDocument, NearestOffersDocument, RouteToCoordinateDocument } from '~/composables/graphql/public/geo-generated'
|
||||||
import type { RouteStageItem } from '~/components/RouteStagesList.vue'
|
import type { RouteStageItem } from '~/components/RouteStagesList.vue'
|
||||||
import { GetOfferDocument, GetSupplierProfileByTeamDocument } from '~/composables/graphql/public/exchange-generated'
|
import { GetOfferDocument, GetSupplierProfileByTeamDocument } from '~/composables/graphql/public/exchange-generated'
|
||||||
|
|
||||||
@@ -106,17 +106,68 @@ type ProductRouteOption = {
|
|||||||
|
|
||||||
const fetchOffersByHub = async () => {
|
const fetchOffersByHub = async () => {
|
||||||
if (!productUuid.value || !destinationUuid.value) return null
|
if (!productUuid.value || !destinationUuid.value) return null
|
||||||
const { client } = useApolloClient('publicGeo')
|
|
||||||
|
|
||||||
const { data } = await client.query({
|
// 1. Get hub node to get coordinates
|
||||||
query: GetOffersByHubDocument,
|
const hubData = await execute(GetNodeDocument, { uuid: destinationUuid.value }, 'public', 'geo')
|
||||||
variables: {
|
const hub = hubData?.node
|
||||||
hubUuid: destinationUuid.value,
|
|
||||||
|
if (!hub?.latitude || !hub?.longitude) {
|
||||||
|
console.warn('Hub has no coordinates')
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Find offers near hub for this product
|
||||||
|
const offersData = await execute(
|
||||||
|
NearestOffersDocument,
|
||||||
|
{
|
||||||
|
lat: hub.latitude,
|
||||||
|
lon: hub.longitude,
|
||||||
productUuid: productUuid.value,
|
productUuid: productUuid.value,
|
||||||
|
radius: 500,
|
||||||
limit: 5
|
limit: 5
|
||||||
}
|
},
|
||||||
})
|
'public',
|
||||||
return data
|
'geo'
|
||||||
|
)
|
||||||
|
|
||||||
|
const offers = offersData?.nearestOffers || []
|
||||||
|
|
||||||
|
// 3. For each offer, get route to hub coordinates
|
||||||
|
const offersWithRoutes = await Promise.all(
|
||||||
|
offers.map(async (offer: any) => {
|
||||||
|
try {
|
||||||
|
const routeData = await execute(
|
||||||
|
RouteToCoordinateDocument,
|
||||||
|
{
|
||||||
|
offerUuid: offer.uuid,
|
||||||
|
lat: hub.latitude,
|
||||||
|
lon: hub.longitude
|
||||||
|
},
|
||||||
|
'public',
|
||||||
|
'geo'
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
sourceUuid: offer.uuid,
|
||||||
|
sourceName: offer.productName,
|
||||||
|
sourceLat: offer.latitude,
|
||||||
|
sourceLon: offer.longitude,
|
||||||
|
distanceKm: routeData?.routeToCoordinate?.distanceKm,
|
||||||
|
routes: routeData?.routeToCoordinate?.routes || []
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('No route found for offer:', offer.uuid, e)
|
||||||
|
return {
|
||||||
|
sourceUuid: offer.uuid,
|
||||||
|
sourceName: offer.productName,
|
||||||
|
sourceLat: offer.latitude,
|
||||||
|
sourceLon: offer.longitude,
|
||||||
|
routes: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
return { offersByHub: offersWithRoutes }
|
||||||
}
|
}
|
||||||
|
|
||||||
const { data: productRoutesData, pending, error } = await useAsyncData(
|
const { data: productRoutesData, pending, error } = await useAsyncData(
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { GetNodesDocument, GetHubCountriesDocument, GetHubsForProductDocument } from '~/composables/graphql/public/geo-generated'
|
import { GetNodesDocument, GetHubCountriesDocument, NearestHubsDocument } from '~/composables/graphql/public/geo-generated'
|
||||||
|
|
||||||
const PAGE_SIZE = 24
|
const PAGE_SIZE = 24
|
||||||
|
|
||||||
@@ -52,15 +52,22 @@ export function useCatalogHubs() {
|
|||||||
const fetchPage = async (offset: number, replace = false) => {
|
const fetchPage = async (offset: number, replace = false) => {
|
||||||
if (replace) isLoading.value = true
|
if (replace) isLoading.value = true
|
||||||
try {
|
try {
|
||||||
// If filtering by product, use specialized query
|
// If filtering by product, use nearestHubs with global search
|
||||||
|
// (center point 0,0 with very large radius to cover entire globe)
|
||||||
if (filterProductUuid.value) {
|
if (filterProductUuid.value) {
|
||||||
const data = await execute(
|
const data = await execute(
|
||||||
GetHubsForProductDocument,
|
NearestHubsDocument,
|
||||||
{ productUuid: filterProductUuid.value },
|
{
|
||||||
|
lat: 0,
|
||||||
|
lon: 0,
|
||||||
|
radius: 20000, // 20000 km radius covers entire Earth
|
||||||
|
productUuid: filterProductUuid.value,
|
||||||
|
limit: 500 // Increased limit for global search
|
||||||
|
},
|
||||||
'public',
|
'public',
|
||||||
'geo'
|
'geo'
|
||||||
)
|
)
|
||||||
const next = data?.hubsForProduct || []
|
const next = data?.nearestHubs || []
|
||||||
items.value = next
|
items.value = next
|
||||||
total.value = next.length
|
total.value = next.length
|
||||||
isInitialized.value = true
|
isInitialized.value = true
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
import type { InfoEntityType } from './useCatalogSearch'
|
import type { InfoEntityType } from './useCatalogSearch'
|
||||||
import {
|
import {
|
||||||
GetNodeDocument,
|
GetNodeDocument,
|
||||||
GetProductsNearHubDocument,
|
NearestOffersDocument,
|
||||||
GetProductsBySupplierDocument,
|
NearestHubsDocument,
|
||||||
GetHubsNearOfferDocument,
|
RouteToCoordinateDocument
|
||||||
GetOffersByHubDocument,
|
|
||||||
GetOffersBySupplierProductDocument
|
|
||||||
} from '~/composables/graphql/public/geo-generated'
|
} from '~/composables/graphql/public/geo-generated'
|
||||||
import {
|
import {
|
||||||
GetOfferDocument,
|
GetOfferDocument,
|
||||||
@@ -32,17 +30,41 @@ export function useCatalogInfo() {
|
|||||||
const nodeData = await execute(GetNodeDocument, { uuid }, 'public', 'geo')
|
const nodeData = await execute(GetNodeDocument, { uuid }, 'public', 'geo')
|
||||||
entity.value = nodeData?.node
|
entity.value = nodeData?.node
|
||||||
|
|
||||||
// Load products near hub
|
if (!entity.value?.latitude || !entity.value?.longitude) {
|
||||||
const productsData = await execute(
|
console.warn('Hub has no coordinates')
|
||||||
GetProductsNearHubDocument,
|
return
|
||||||
{ hubUuid: uuid, radiusKm: 500 },
|
}
|
||||||
|
|
||||||
|
// Load offers near hub and group by product
|
||||||
|
const offersData = await execute(
|
||||||
|
NearestOffersDocument,
|
||||||
|
{
|
||||||
|
lat: entity.value.latitude,
|
||||||
|
lon: entity.value.longitude,
|
||||||
|
radius: 500
|
||||||
|
},
|
||||||
'public',
|
'public',
|
||||||
'geo'
|
'geo'
|
||||||
)
|
)
|
||||||
relatedProducts.value = productsData?.productsNearHub || []
|
|
||||||
|
|
||||||
// Set default active tab to products
|
// Group offers by product
|
||||||
activeTab.value = 'products'
|
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++
|
||||||
|
}
|
||||||
|
})
|
||||||
|
relatedProducts.value = Array.from(productsMap.values())
|
||||||
|
|
||||||
|
// Set default active tab to offers (first step shows products)
|
||||||
|
activeTab.value = 'offers'
|
||||||
|
|
||||||
// Note: Suppliers loaded after product selection via loadOffersForHub
|
// Note: Suppliers loaded after product selection via loadOffersForHub
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -53,7 +75,7 @@ export function useCatalogInfo() {
|
|||||||
// Load supplier info: supplier details + products
|
// Load supplier info: supplier details + products
|
||||||
const loadSupplierInfo = async (uuid: string) => {
|
const loadSupplierInfo = async (uuid: string) => {
|
||||||
try {
|
try {
|
||||||
// Load supplier node details
|
// Load supplier node details (might be geo node)
|
||||||
const nodeData = await execute(GetNodeDocument, { uuid }, 'public', 'geo')
|
const nodeData = await execute(GetNodeDocument, { uuid }, 'public', 'geo')
|
||||||
entity.value = nodeData?.node
|
entity.value = nodeData?.node
|
||||||
|
|
||||||
@@ -72,17 +94,41 @@ export function useCatalogInfo() {
|
|||||||
// Supplier profile might not exist, ignore
|
// Supplier profile might not exist, ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load products from supplier
|
if (!entity.value?.latitude || !entity.value?.longitude) {
|
||||||
const productsData = await execute(
|
console.warn('Supplier has no coordinates')
|
||||||
GetProductsBySupplierDocument,
|
return
|
||||||
{ supplierUuid: uuid },
|
}
|
||||||
|
|
||||||
|
// Load offers near supplier and group by product
|
||||||
|
const offersData = await execute(
|
||||||
|
NearestOffersDocument,
|
||||||
|
{
|
||||||
|
lat: entity.value.latitude,
|
||||||
|
lon: entity.value.longitude,
|
||||||
|
radius: 500
|
||||||
|
},
|
||||||
'public',
|
'public',
|
||||||
'geo'
|
'geo'
|
||||||
)
|
)
|
||||||
relatedProducts.value = productsData?.productsBySupplier || []
|
|
||||||
|
|
||||||
// Set default active tab to products
|
// Group offers by product
|
||||||
activeTab.value = 'products'
|
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++
|
||||||
|
}
|
||||||
|
})
|
||||||
|
relatedProducts.value = Array.from(productsMap.values())
|
||||||
|
|
||||||
|
// Set default active tab to offers (first step shows products)
|
||||||
|
activeTab.value = 'offers'
|
||||||
|
|
||||||
// Note: Hubs will be loaded after product selection
|
// Note: Hubs will be loaded after product selection
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -97,14 +143,24 @@ export function useCatalogInfo() {
|
|||||||
const offerData = await execute(GetOfferDocument, { uuid }, 'public', 'exchange')
|
const offerData = await execute(GetOfferDocument, { uuid }, 'public', 'exchange')
|
||||||
entity.value = offerData?.getOffer
|
entity.value = offerData?.getOffer
|
||||||
|
|
||||||
// Load hubs near offer
|
if (!entity.value?.latitude || !entity.value?.longitude) {
|
||||||
|
console.warn('Offer has no coordinates')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load hubs near offer coordinates
|
||||||
const hubsData = await execute(
|
const hubsData = await execute(
|
||||||
GetHubsNearOfferDocument,
|
NearestHubsDocument,
|
||||||
{ offerUuid: uuid, limit: 12 },
|
{
|
||||||
|
lat: entity.value.latitude,
|
||||||
|
lon: entity.value.longitude,
|
||||||
|
radius: 1000,
|
||||||
|
limit: 12
|
||||||
|
},
|
||||||
'public',
|
'public',
|
||||||
'geo'
|
'geo'
|
||||||
)
|
)
|
||||||
relatedHubs.value = hubsData?.hubsNearOffer || []
|
relatedHubs.value = hubsData?.nearestHubs || []
|
||||||
|
|
||||||
// If offer has supplier UUID, load supplier profile
|
// If offer has supplier UUID, load supplier profile
|
||||||
if (entity.value?.teamUuid) {
|
if (entity.value?.teamUuid) {
|
||||||
@@ -143,19 +199,73 @@ export function useCatalogInfo() {
|
|||||||
// Load offers for hub after product selection
|
// Load offers for hub after product selection
|
||||||
const loadOffersForHub = async (hubUuid: string, productUuid: string) => {
|
const loadOffersForHub = async (hubUuid: string, productUuid: string) => {
|
||||||
try {
|
try {
|
||||||
|
const hub = entity.value
|
||||||
|
if (!hub?.latitude || !hub?.longitude) {
|
||||||
|
console.warn('Hub has no coordinates')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. Find offers near hub for this product
|
||||||
const offersData = await execute(
|
const offersData = await execute(
|
||||||
GetOffersByHubDocument,
|
NearestOffersDocument,
|
||||||
{ hubUuid, productUuid, limit: 12 },
|
{
|
||||||
|
lat: hub.latitude,
|
||||||
|
lon: hub.longitude,
|
||||||
|
productUuid,
|
||||||
|
radius: 500,
|
||||||
|
limit: 12
|
||||||
|
},
|
||||||
'public',
|
'public',
|
||||||
'geo'
|
'geo'
|
||||||
)
|
)
|
||||||
relatedOffers.value = offersData?.offersByHub || []
|
|
||||||
|
|
||||||
// Extract unique suppliers from offers
|
const offers = offersData?.nearestOffers || []
|
||||||
|
|
||||||
|
// 2. For each offer, get route to hub
|
||||||
|
const offersWithRoutes = await Promise.all(
|
||||||
|
offers.map(async (offer: any) => {
|
||||||
|
try {
|
||||||
|
const routeData = await execute(
|
||||||
|
RouteToCoordinateDocument,
|
||||||
|
{
|
||||||
|
offerUuid: offer.uuid,
|
||||||
|
lat: hub.latitude,
|
||||||
|
lon: hub.longitude
|
||||||
|
},
|
||||||
|
'public',
|
||||||
|
'geo'
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
...offer,
|
||||||
|
sourceUuid: offer.uuid,
|
||||||
|
sourceName: offer.productName,
|
||||||
|
sourceLat: offer.latitude,
|
||||||
|
sourceLon: offer.longitude,
|
||||||
|
distanceKm: routeData?.routeToCoordinate?.distanceKm,
|
||||||
|
routes: routeData?.routeToCoordinate?.routes || []
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Route might not exist
|
||||||
|
console.warn('No route found for offer:', offer.uuid, e)
|
||||||
|
return {
|
||||||
|
...offer,
|
||||||
|
sourceUuid: offer.uuid,
|
||||||
|
sourceName: offer.productName,
|
||||||
|
sourceLat: offer.latitude,
|
||||||
|
sourceLon: offer.longitude,
|
||||||
|
routes: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
relatedOffers.value = offersWithRoutes
|
||||||
|
|
||||||
|
// Extract unique suppliers from offers (use supplierUuid from offers)
|
||||||
const supplierUuids = new Set<string>()
|
const supplierUuids = new Set<string>()
|
||||||
relatedOffers.value.forEach((offer: any) => {
|
offersWithRoutes.forEach((offer: any) => {
|
||||||
if (offer.teamUuid) {
|
if (offer.supplierUuid) {
|
||||||
supplierUuids.add(offer.teamUuid)
|
supplierUuids.add(offer.supplierUuid)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -185,32 +295,53 @@ export function useCatalogInfo() {
|
|||||||
// Load offers for supplier after product selection
|
// Load offers for supplier after product selection
|
||||||
const loadOffersForSupplier = async (supplierUuid: string, productUuid: string) => {
|
const loadOffersForSupplier = async (supplierUuid: string, productUuid: string) => {
|
||||||
try {
|
try {
|
||||||
|
const supplier = entity.value
|
||||||
|
if (!supplier?.latitude || !supplier?.longitude) {
|
||||||
|
console.warn('Supplier has no coordinates')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find offers near supplier for this product
|
||||||
const offersData = await execute(
|
const offersData = await execute(
|
||||||
GetOffersBySupplierProductDocument,
|
NearestOffersDocument,
|
||||||
{ supplierUuid, productUuid },
|
{
|
||||||
|
lat: supplier.latitude,
|
||||||
|
lon: supplier.longitude,
|
||||||
|
productUuid,
|
||||||
|
radius: 500,
|
||||||
|
limit: 12
|
||||||
|
},
|
||||||
'public',
|
'public',
|
||||||
'geo'
|
'geo'
|
||||||
)
|
)
|
||||||
relatedOffers.value = offersData?.offersBySupplierProduct || []
|
|
||||||
|
|
||||||
// Load hubs for each offer and aggregate (limit to 12)
|
relatedOffers.value = offersData?.nearestOffers || []
|
||||||
|
|
||||||
|
// Load hubs near each offer and aggregate (limit to 12)
|
||||||
const allHubs = new Map<string, any>()
|
const allHubs = new Map<string, any>()
|
||||||
for (const offer of relatedOffers.value.slice(0, 3)) {
|
for (const offer of relatedOffers.value.slice(0, 3)) {
|
||||||
// Check first 3 offers
|
// Check first 3 offers
|
||||||
|
if (!offer.latitude || !offer.longitude) continue
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const hubsData = await execute(
|
const hubsData = await execute(
|
||||||
GetHubsNearOfferDocument,
|
NearestHubsDocument,
|
||||||
{ offerUuid: offer.uuid, limit: 5 },
|
{
|
||||||
|
lat: offer.latitude,
|
||||||
|
lon: offer.longitude,
|
||||||
|
radius: 1000,
|
||||||
|
limit: 5
|
||||||
|
},
|
||||||
'public',
|
'public',
|
||||||
'geo'
|
'geo'
|
||||||
)
|
)
|
||||||
hubsData?.hubsNearOffer?.forEach((hub: any) => {
|
hubsData?.nearestHubs?.forEach((hub: any) => {
|
||||||
if (!allHubs.has(hub.uuid)) {
|
if (!allHubs.has(hub.uuid)) {
|
||||||
allHubs.set(hub.uuid, hub)
|
allHubs.set(hub.uuid, hub)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Hubs might not exist for this offer
|
console.warn('Error loading hubs for offer:', offer.uuid, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (allHubs.size >= 12) break
|
if (allHubs.size >= 12) break
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
import {
|
import {
|
||||||
GetProductsDocument,
|
GetProductsDocument,
|
||||||
GetProductsBySupplierDocument,
|
GetNodeDocument,
|
||||||
GetProductsNearHubDocument
|
NearestOffersDocument
|
||||||
} from '~/composables/graphql/public/geo-generated'
|
} from '~/composables/graphql/public/geo-generated'
|
||||||
|
import {
|
||||||
|
GetSupplierProfileDocument
|
||||||
|
} from '~/composables/graphql/public/exchange-generated'
|
||||||
|
|
||||||
// Shared state
|
// Shared state
|
||||||
const items = ref<any[]>([])
|
const items = ref<any[]>([])
|
||||||
@@ -27,23 +30,89 @@ export function useCatalogProducts() {
|
|||||||
let data
|
let data
|
||||||
|
|
||||||
if (filterSupplierUuid.value) {
|
if (filterSupplierUuid.value) {
|
||||||
// Products from specific supplier
|
// Products from specific supplier - get supplier coordinates first
|
||||||
data = await execute(
|
const supplierData = await execute(
|
||||||
GetProductsBySupplierDocument,
|
GetSupplierProfileDocument,
|
||||||
{ supplierUuid: filterSupplierUuid.value },
|
{ supplierUuid: filterSupplierUuid.value },
|
||||||
'public',
|
'public',
|
||||||
'geo'
|
'exchange'
|
||||||
)
|
)
|
||||||
items.value = data?.productsBySupplier || []
|
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) {
|
} else if (filterHubUuid.value) {
|
||||||
// Products near hub
|
// Products near hub - get hub coordinates first
|
||||||
data = await execute(
|
const hubData = await execute(
|
||||||
GetProductsNearHubDocument,
|
GetNodeDocument,
|
||||||
{ hubUuid: filterHubUuid.value },
|
{ uuid: filterHubUuid.value },
|
||||||
'public',
|
'public',
|
||||||
'geo'
|
'geo'
|
||||||
)
|
)
|
||||||
items.value = data?.productsNearHub || []
|
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 {
|
} else {
|
||||||
// All products
|
// All products
|
||||||
data = await execute(
|
data = await execute(
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { GetSupplierProfilesDocument } from '~/composables/graphql/public/exchange-generated'
|
import { GetSupplierProfilesDocument } from '~/composables/graphql/public/exchange-generated'
|
||||||
import { GetSuppliersForProductDocument } from '~/composables/graphql/public/geo-generated'
|
import { NearestSuppliersDocument } from '~/composables/graphql/public/geo-generated'
|
||||||
|
|
||||||
const PAGE_SIZE = 24
|
const PAGE_SIZE = 24
|
||||||
|
|
||||||
@@ -24,29 +24,22 @@ export function useCatalogSuppliers() {
|
|||||||
const fetchPage = async (offset: number, replace = false) => {
|
const fetchPage = async (offset: number, replace = false) => {
|
||||||
if (replace) isLoading.value = true
|
if (replace) isLoading.value = true
|
||||||
try {
|
try {
|
||||||
// If filtering by product, use specialized query
|
// If filtering by product, use nearestSuppliers with global search
|
||||||
|
// (center point 0,0 with very large radius to cover entire globe)
|
||||||
if (filterProductUuid.value) {
|
if (filterProductUuid.value) {
|
||||||
const data = await execute(
|
const data = await execute(
|
||||||
GetSuppliersForProductDocument,
|
NearestSuppliersDocument,
|
||||||
{ productUuid: filterProductUuid.value },
|
{
|
||||||
|
lat: 0,
|
||||||
|
lon: 0,
|
||||||
|
radius: 20000, // 20000 km radius covers entire Earth
|
||||||
|
productUuid: filterProductUuid.value,
|
||||||
|
limit: 500 // Increased limit for global search
|
||||||
|
},
|
||||||
'public',
|
'public',
|
||||||
'geo'
|
'geo'
|
||||||
)
|
)
|
||||||
const supplierUuids = (data?.suppliersForProduct || []).map(s => s?.uuid).filter(Boolean)
|
items.value = data?.nearestSuppliers || []
|
||||||
// Fetch full profiles for these suppliers
|
|
||||||
if (supplierUuids.length > 0) {
|
|
||||||
const profilesData = await execute(
|
|
||||||
GetSupplierProfilesDocument,
|
|
||||||
{ limit: supplierUuids.length, offset: 0 },
|
|
||||||
'public',
|
|
||||||
'exchange'
|
|
||||||
)
|
|
||||||
// Filter to only include suppliers that match the product filter
|
|
||||||
const allProfiles = profilesData?.getSupplierProfiles || []
|
|
||||||
items.value = allProfiles.filter(p => supplierUuids.includes(p.uuid))
|
|
||||||
} else {
|
|
||||||
items.value = []
|
|
||||||
}
|
|
||||||
total.value = items.value.length
|
total.value = items.value.length
|
||||||
isInitialized.value = true
|
isInitialized.value = true
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -43,7 +43,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { GetNodeConnectionsDocument, GetProductsNearHubDocument } from '~/composables/graphql/public/geo-generated'
|
import { GetNodeConnectionsDocument, NearestOffersDocument } from '~/composables/graphql/public/geo-generated'
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
layout: 'topnav'
|
layout: 'topnav'
|
||||||
@@ -122,17 +122,45 @@ const getMockPriceHistory = (uuid: string): number[] => {
|
|||||||
|
|
||||||
// Initial load
|
// Initial load
|
||||||
try {
|
try {
|
||||||
const [{ data: connectionsData }, { data: productsData }] = await Promise.all([
|
// First load hub connections to get coordinates
|
||||||
useServerQuery('hub-connections', GetNodeConnectionsDocument, { uuid: hubId.value }, 'public', 'geo'),
|
const { data: connectionsData } = await useServerQuery(
|
||||||
useServerQuery('products-near-hub', GetProductsNearHubDocument, { hubUuid: hubId.value, radiusKm: 500 }, 'public', 'geo')
|
'hub-connections',
|
||||||
])
|
GetNodeConnectionsDocument,
|
||||||
|
{ uuid: hubId.value },
|
||||||
|
'public',
|
||||||
|
'geo'
|
||||||
|
)
|
||||||
|
|
||||||
hub.value = connectionsData.value?.nodeConnections?.hub || null
|
hub.value = connectionsData.value?.nodeConnections?.hub || null
|
||||||
|
|
||||||
// Get products near this hub (from geo)
|
// Load offers near hub and group by product
|
||||||
products.value = (productsData.value?.productsNearHub || [])
|
if (hub.value?.latitude && hub.value?.longitude) {
|
||||||
.filter((p): p is { uuid: string; name?: string | null } => p !== null && !!p.uuid)
|
const { data: offersData } = await useServerQuery(
|
||||||
.map(p => ({ uuid: p.uuid!, name: p.name || '' }))
|
'offers-near-hub',
|
||||||
|
NearestOffersDocument,
|
||||||
|
{
|
||||||
|
lat: hub.value.latitude,
|
||||||
|
lon: hub.value.longitude,
|
||||||
|
radius: 500
|
||||||
|
},
|
||||||
|
'public',
|
||||||
|
'geo'
|
||||||
|
)
|
||||||
|
|
||||||
|
// Group offers by product
|
||||||
|
const productsMap = new Map<string, { uuid: string; name: string }>()
|
||||||
|
offersData.value?.nearestOffers?.forEach((offer: any) => {
|
||||||
|
if (offer?.productUuid) {
|
||||||
|
if (!productsMap.has(offer.productUuid)) {
|
||||||
|
productsMap.set(offer.productUuid, {
|
||||||
|
uuid: offer.productUuid,
|
||||||
|
name: offer.productName || ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
products.value = Array.from(productsMap.values())
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading hub:', error)
|
console.error('Error loading hub:', error)
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
Reference in New Issue
Block a user