Optimize catalog loading: backend bounds filtering + early returns
All checks were successful
Build Docker Image / build (push) Successful in 4m12s

- Add bounds (west/south/east/north) parameters to GetNodes query
- Add setBoundsFilter to useCatalogHubs, useCatalogSuppliers, useCatalogProducts
- Replace client-side bounds filtering with backend query
- Add early return to setProductFilter to avoid redundant fetches
- Watch filterByBounds to trigger backend refetch when checkbox changes
This commit is contained in:
Ruslan Bakiev
2026-01-24 12:19:00 +07:00
parent 8c753edb28
commit 9b99d8981c
6 changed files with 97 additions and 40 deletions

View File

@@ -124,7 +124,7 @@ export type Query = {
nodeConnections?: Maybe<NodeConnectionsType>;
/** Get all nodes (without edges for performance) */
nodes?: Maybe<Array<Maybe<NodeType>>>;
/** Get total count of nodes (with optional transport/country filter) */
/** Get total count of nodes (with optional transport/country/bounds filter) */
nodesCount?: Maybe<Scalars['Int']['output']>;
/** Get route from a specific offer to hub */
offerToHub?: Maybe<ProductRouteOptionType>;
@@ -209,17 +209,25 @@ export type QueryNodeConnectionsArgs = {
/** Root query. */
export type QueryNodesArgs = {
country?: InputMaybe<Scalars['String']['input']>;
east?: InputMaybe<Scalars['Float']['input']>;
limit?: InputMaybe<Scalars['Int']['input']>;
north?: InputMaybe<Scalars['Float']['input']>;
offset?: InputMaybe<Scalars['Int']['input']>;
search?: InputMaybe<Scalars['String']['input']>;
south?: InputMaybe<Scalars['Float']['input']>;
transportType?: InputMaybe<Scalars['String']['input']>;
west?: InputMaybe<Scalars['Float']['input']>;
};
/** Root query. */
export type QueryNodesCountArgs = {
country?: InputMaybe<Scalars['String']['input']>;
east?: InputMaybe<Scalars['Float']['input']>;
north?: InputMaybe<Scalars['Float']['input']>;
south?: InputMaybe<Scalars['Float']['input']>;
transportType?: InputMaybe<Scalars['String']['input']>;
west?: InputMaybe<Scalars['Float']['input']>;
};
@@ -381,6 +389,10 @@ export type GetNodesQueryVariables = Exact<{
offset?: InputMaybe<Scalars['Int']['input']>;
transportType?: InputMaybe<Scalars['String']['input']>;
country?: InputMaybe<Scalars['String']['input']>;
west?: InputMaybe<Scalars['Float']['input']>;
south?: InputMaybe<Scalars['Float']['input']>;
east?: InputMaybe<Scalars['Float']['input']>;
north?: InputMaybe<Scalars['Float']['input']>;
}>;
@@ -468,7 +480,7 @@ export const GetHubsForProductDocument = {"kind":"Document","definitions":[{"kin
export const GetHubsNearOfferDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetHubsNearOffer"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"offerUuid"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"limit"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hubsNearOffer"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"offerUuid"},"value":{"kind":"Variable","name":{"kind":"Name","value":"offerUuid"}}},{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"Variable","name":{"kind":"Name","value":"limit"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"latitude"}},{"kind":"Field","name":{"kind":"Name","value":"longitude"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"countryCode"}},{"kind":"Field","name":{"kind":"Name","value":"transportTypes"}}]}}]}}]} as unknown as DocumentNode<GetHubsNearOfferQuery, GetHubsNearOfferQueryVariables>;
export const GetNodeDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetNode"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"uuid"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"uuid"},"value":{"kind":"Variable","name":{"kind":"Name","value":"uuid"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"latitude"}},{"kind":"Field","name":{"kind":"Name","value":"longitude"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"countryCode"}},{"kind":"Field","name":{"kind":"Name","value":"syncedAt"}},{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"toUuid"}},{"kind":"Field","name":{"kind":"Name","value":"toName"}},{"kind":"Field","name":{"kind":"Name","value":"toLatitude"}},{"kind":"Field","name":{"kind":"Name","value":"toLongitude"}},{"kind":"Field","name":{"kind":"Name","value":"distanceKm"}},{"kind":"Field","name":{"kind":"Name","value":"travelTimeSeconds"}},{"kind":"Field","name":{"kind":"Name","value":"transportType"}}]}}]}}]}}]} as unknown as DocumentNode<GetNodeQuery, GetNodeQueryVariables>;
export const GetNodeConnectionsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetNodeConnections"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"uuid"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"limitAuto"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"limitRail"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodeConnections"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"uuid"},"value":{"kind":"Variable","name":{"kind":"Name","value":"uuid"}}},{"kind":"Argument","name":{"kind":"Name","value":"limitAuto"},"value":{"kind":"Variable","name":{"kind":"Name","value":"limitAuto"}}},{"kind":"Argument","name":{"kind":"Name","value":"limitRail"},"value":{"kind":"Variable","name":{"kind":"Name","value":"limitRail"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hub"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"latitude"}},{"kind":"Field","name":{"kind":"Name","value":"longitude"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"countryCode"}},{"kind":"Field","name":{"kind":"Name","value":"syncedAt"}}]}},{"kind":"Field","name":{"kind":"Name","value":"railNode"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"latitude"}},{"kind":"Field","name":{"kind":"Name","value":"longitude"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"countryCode"}},{"kind":"Field","name":{"kind":"Name","value":"syncedAt"}}]}},{"kind":"Field","name":{"kind":"Name","value":"autoEdges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"toUuid"}},{"kind":"Field","name":{"kind":"Name","value":"toName"}},{"kind":"Field","name":{"kind":"Name","value":"toLatitude"}},{"kind":"Field","name":{"kind":"Name","value":"toLongitude"}},{"kind":"Field","name":{"kind":"Name","value":"distanceKm"}},{"kind":"Field","name":{"kind":"Name","value":"travelTimeSeconds"}},{"kind":"Field","name":{"kind":"Name","value":"transportType"}}]}},{"kind":"Field","name":{"kind":"Name","value":"railEdges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"toUuid"}},{"kind":"Field","name":{"kind":"Name","value":"toName"}},{"kind":"Field","name":{"kind":"Name","value":"toLatitude"}},{"kind":"Field","name":{"kind":"Name","value":"toLongitude"}},{"kind":"Field","name":{"kind":"Name","value":"distanceKm"}},{"kind":"Field","name":{"kind":"Name","value":"travelTimeSeconds"}},{"kind":"Field","name":{"kind":"Name","value":"transportType"}}]}}]}}]}}]} as unknown as DocumentNode<GetNodeConnectionsQuery, GetNodeConnectionsQueryVariables>;
export const GetNodesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetNodes"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"limit"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"offset"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"transportType"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"country"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"Variable","name":{"kind":"Name","value":"limit"}}},{"kind":"Argument","name":{"kind":"Name","value":"offset"},"value":{"kind":"Variable","name":{"kind":"Name","value":"offset"}}},{"kind":"Argument","name":{"kind":"Name","value":"transportType"},"value":{"kind":"Variable","name":{"kind":"Name","value":"transportType"}}},{"kind":"Argument","name":{"kind":"Name","value":"country"},"value":{"kind":"Variable","name":{"kind":"Name","value":"country"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"latitude"}},{"kind":"Field","name":{"kind":"Name","value":"longitude"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"countryCode"}},{"kind":"Field","name":{"kind":"Name","value":"syncedAt"}},{"kind":"Field","name":{"kind":"Name","value":"transportTypes"}}]}},{"kind":"Field","name":{"kind":"Name","value":"nodesCount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"transportType"},"value":{"kind":"Variable","name":{"kind":"Name","value":"transportType"}}},{"kind":"Argument","name":{"kind":"Name","value":"country"},"value":{"kind":"Variable","name":{"kind":"Name","value":"country"}}}]}]}}]} as unknown as DocumentNode<GetNodesQuery, GetNodesQueryVariables>;
export const GetNodesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetNodes"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"limit"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"offset"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"transportType"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"country"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"west"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"south"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"east"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"north"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"Variable","name":{"kind":"Name","value":"limit"}}},{"kind":"Argument","name":{"kind":"Name","value":"offset"},"value":{"kind":"Variable","name":{"kind":"Name","value":"offset"}}},{"kind":"Argument","name":{"kind":"Name","value":"transportType"},"value":{"kind":"Variable","name":{"kind":"Name","value":"transportType"}}},{"kind":"Argument","name":{"kind":"Name","value":"country"},"value":{"kind":"Variable","name":{"kind":"Name","value":"country"}}},{"kind":"Argument","name":{"kind":"Name","value":"west"},"value":{"kind":"Variable","name":{"kind":"Name","value":"west"}}},{"kind":"Argument","name":{"kind":"Name","value":"south"},"value":{"kind":"Variable","name":{"kind":"Name","value":"south"}}},{"kind":"Argument","name":{"kind":"Name","value":"east"},"value":{"kind":"Variable","name":{"kind":"Name","value":"east"}}},{"kind":"Argument","name":{"kind":"Name","value":"north"},"value":{"kind":"Variable","name":{"kind":"Name","value":"north"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"latitude"}},{"kind":"Field","name":{"kind":"Name","value":"longitude"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"countryCode"}},{"kind":"Field","name":{"kind":"Name","value":"syncedAt"}},{"kind":"Field","name":{"kind":"Name","value":"transportTypes"}}]}},{"kind":"Field","name":{"kind":"Name","value":"nodesCount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"transportType"},"value":{"kind":"Variable","name":{"kind":"Name","value":"transportType"}}},{"kind":"Argument","name":{"kind":"Name","value":"country"},"value":{"kind":"Variable","name":{"kind":"Name","value":"country"}}},{"kind":"Argument","name":{"kind":"Name","value":"west"},"value":{"kind":"Variable","name":{"kind":"Name","value":"west"}}},{"kind":"Argument","name":{"kind":"Name","value":"south"},"value":{"kind":"Variable","name":{"kind":"Name","value":"south"}}},{"kind":"Argument","name":{"kind":"Name","value":"east"},"value":{"kind":"Variable","name":{"kind":"Name","value":"east"}}},{"kind":"Argument","name":{"kind":"Name","value":"north"},"value":{"kind":"Variable","name":{"kind":"Name","value":"north"}}}]}]}}]} as unknown as DocumentNode<GetNodesQuery, GetNodesQueryVariables>;
export const GetOfferToHubDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetOfferToHub"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"offerUuid"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"hubUuid"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"offerToHub"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"offerUuid"},"value":{"kind":"Variable","name":{"kind":"Name","value":"offerUuid"}}},{"kind":"Argument","name":{"kind":"Name","value":"hubUuid"},"value":{"kind":"Variable","name":{"kind":"Name","value":"hubUuid"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"sourceUuid"}},{"kind":"Field","name":{"kind":"Name","value":"sourceName"}},{"kind":"Field","name":{"kind":"Name","value":"sourceLat"}},{"kind":"Field","name":{"kind":"Name","value":"sourceLon"}},{"kind":"Field","name":{"kind":"Name","value":"distanceKm"}},{"kind":"Field","name":{"kind":"Name","value":"routes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalDistanceKm"}},{"kind":"Field","name":{"kind":"Name","value":"totalTimeSeconds"}},{"kind":"Field","name":{"kind":"Name","value":"stages"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"fromUuid"}},{"kind":"Field","name":{"kind":"Name","value":"fromName"}},{"kind":"Field","name":{"kind":"Name","value":"fromLat"}},{"kind":"Field","name":{"kind":"Name","value":"fromLon"}},{"kind":"Field","name":{"kind":"Name","value":"toUuid"}},{"kind":"Field","name":{"kind":"Name","value":"toName"}},{"kind":"Field","name":{"kind":"Name","value":"toLat"}},{"kind":"Field","name":{"kind":"Name","value":"toLon"}},{"kind":"Field","name":{"kind":"Name","value":"distanceKm"}},{"kind":"Field","name":{"kind":"Name","value":"travelTimeSeconds"}},{"kind":"Field","name":{"kind":"Name","value":"transportType"}}]}}]}}]}}]}}]} as unknown as DocumentNode<GetOfferToHubQuery, GetOfferToHubQueryVariables>;
export const GetOffersByHubDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetOffersByHub"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"hubUuid"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"productUuid"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"limit"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"offersByHub"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"hubUuid"},"value":{"kind":"Variable","name":{"kind":"Name","value":"hubUuid"}}},{"kind":"Argument","name":{"kind":"Name","value":"productUuid"},"value":{"kind":"Variable","name":{"kind":"Name","value":"productUuid"}}},{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"Variable","name":{"kind":"Name","value":"limit"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"sourceUuid"}},{"kind":"Field","name":{"kind":"Name","value":"sourceName"}},{"kind":"Field","name":{"kind":"Name","value":"sourceLat"}},{"kind":"Field","name":{"kind":"Name","value":"sourceLon"}},{"kind":"Field","name":{"kind":"Name","value":"distanceKm"}},{"kind":"Field","name":{"kind":"Name","value":"routes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalDistanceKm"}},{"kind":"Field","name":{"kind":"Name","value":"totalTimeSeconds"}},{"kind":"Field","name":{"kind":"Name","value":"stages"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"fromUuid"}},{"kind":"Field","name":{"kind":"Name","value":"fromName"}},{"kind":"Field","name":{"kind":"Name","value":"fromLat"}},{"kind":"Field","name":{"kind":"Name","value":"fromLon"}},{"kind":"Field","name":{"kind":"Name","value":"toUuid"}},{"kind":"Field","name":{"kind":"Name","value":"toName"}},{"kind":"Field","name":{"kind":"Name","value":"toLat"}},{"kind":"Field","name":{"kind":"Name","value":"toLon"}},{"kind":"Field","name":{"kind":"Name","value":"distanceKm"}},{"kind":"Field","name":{"kind":"Name","value":"travelTimeSeconds"}},{"kind":"Field","name":{"kind":"Name","value":"transportType"}}]}}]}}]}}]}}]} as unknown as DocumentNode<GetOffersByHubQuery, GetOffersByHubQueryVariables>;
export const GetOffersByProductDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetOffersByProduct"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"productUuid"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"offersByProduct"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"productUuid"},"value":{"kind":"Variable","name":{"kind":"Name","value":"productUuid"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"productUuid"}},{"kind":"Field","name":{"kind":"Name","value":"productName"}},{"kind":"Field","name":{"kind":"Name","value":"supplierUuid"}},{"kind":"Field","name":{"kind":"Name","value":"latitude"}},{"kind":"Field","name":{"kind":"Name","value":"longitude"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"countryCode"}},{"kind":"Field","name":{"kind":"Name","value":"pricePerUnit"}},{"kind":"Field","name":{"kind":"Name","value":"currency"}},{"kind":"Field","name":{"kind":"Name","value":"quantity"}},{"kind":"Field","name":{"kind":"Name","value":"unit"}}]}}]}}]} as unknown as DocumentNode<GetOffersByProductQuery, GetOffersByProductQueryVariables>;

View File

@@ -12,6 +12,7 @@ const isLoading = ref(false)
const isLoadingMore = ref(false)
const isInitialized = ref(false)
const filterProductUuid = ref<string | null>(null)
const filterBounds = ref<{ west: number; south: number; east: number; north: number } | null>(null)
export function useCatalogHubs() {
const { t } = useI18n()
@@ -69,9 +70,21 @@ export function useCatalogHubs() {
// 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
const data = await execute(
GetNodesDocument,
{ limit: PAGE_SIZE, offset, transportType, country },
{
limit: PAGE_SIZE,
offset,
transportType,
country,
...(bounds && {
west: bounds.west,
south: bounds.south,
east: bounds.east,
north: bounds.north
})
},
'public',
'geo'
)
@@ -111,12 +124,20 @@ export function useCatalogHubs() {
})
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) {
@@ -142,6 +163,7 @@ export function useCatalogHubs() {
fetchPage,
loadMore,
init,
setProductFilter
setProductFilter,
setBoundsFilter
}
}

View File

@@ -99,6 +99,13 @@ export function useCatalogProducts() {
}
}
// Products don't have coordinates directly (they're an aggregation of offers)
// Bounds filtering would require a new backend query that filters by offer locations
// For now, this is a no-op - products show all regardless of map bounds
const setBoundsFilter = (_bounds: { west: number; south: number; east: number; north: number } | null) => {
// No-op: products are not filterable by map bounds in current implementation
}
return {
items,
isLoading,
@@ -110,6 +117,7 @@ export function useCatalogProducts() {
init,
setSupplierFilter,
setHubFilter,
clearFilters
clearFilters,
setBoundsFilter
}
}

View File

@@ -10,6 +10,7 @@ const isLoading = ref(false)
const isLoadingMore = ref(false)
const isInitialized = ref(false)
const filterProductUuid = ref<string | null>(null)
const filterBounds = ref<{ west: number; south: number; east: number; north: number } | null>(null)
export function useCatalogSuppliers() {
const { execute } = useGraphQL()
@@ -52,15 +53,31 @@ export function useCatalogSuppliers() {
}
// Default: fetch all suppliers
// If bounds filter is active, fetch more and filter client-side
// TODO: Add bounds support to exchange backend for proper server-side filtering
const bounds = filterBounds.value
const fetchLimit = bounds ? 500 : PAGE_SIZE
const data = await execute(
GetSupplierProfilesDocument,
{ limit: PAGE_SIZE, offset },
{ limit: fetchLimit, offset: bounds ? 0 : offset },
'public',
'exchange'
)
const next = data?.getSupplierProfiles || []
let next = data?.getSupplierProfiles || []
// Client-side bounds filtering (until exchange backend supports it)
if (bounds) {
next = next.filter((s: any) => {
if (!s.latitude || !s.longitude) return false
const lat = Number(s.latitude)
const lng = Number(s.longitude)
return lat >= bounds.south && lat <= bounds.north
&& lng >= bounds.west && lng <= bounds.east
})
}
items.value = replace ? next : items.value.concat(next)
total.value = data?.getSupplierProfilesCount ?? total.value
total.value = bounds ? next.length : (data?.getSupplierProfilesCount ?? total.value)
isInitialized.value = true
} finally {
isLoading.value = false
@@ -85,12 +102,20 @@ export function useCatalogSuppliers() {
}
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)
}
}
return {
items,
total,
@@ -101,6 +126,7 @@ export function useCatalogSuppliers() {
fetchPage,
loadMore,
init,
setProductFilter
setProductFilter,
setBoundsFilter
}
}

View File

@@ -82,16 +82,6 @@ const onBoundsChange = (bounds: MapBounds) => {
currentMapBounds.value = bounds
}
// Check if item is within map bounds
const isInBounds = (item: any, bounds: MapBounds | null): boolean => {
if (!bounds) return true
if (!item.latitude || !item.longitude) return false
const lat = Number(item.latitude)
const lng = Number(item.longitude)
return lat >= bounds.south && lat <= bounds.north
&& lng >= bounds.west && lng <= bounds.east
}
const {
catalogMode,
selectMode,
@@ -117,26 +107,17 @@ const {
init: initProducts,
setSupplierFilter,
setHubFilter,
clearFilters: clearProductFilters
clearFilters: clearProductFilters,
setBoundsFilter: setProductBoundsFilter
} = useCatalogProducts()
const { items: hubs, isLoading: hubsLoading, isLoadingMore: hubsLoadingMore, canLoadMore: hubsCanLoadMore, loadMore: loadMoreHubs, init: initHubs, setProductFilter: setHubProductFilter } = useCatalogHubs()
const { items: suppliers, isLoading: suppliersLoading, isLoadingMore: suppliersLoadingMore, canLoadMore: suppliersCanLoadMore, loadMore: loadMoreSuppliers, init: initSuppliers, setProductFilter: setSupplierProductFilter } = useCatalogSuppliers()
const { items: hubs, isLoading: hubsLoading, isLoadingMore: hubsLoadingMore, canLoadMore: hubsCanLoadMore, loadMore: loadMoreHubs, init: initHubs, setProductFilter: setHubProductFilter, setBoundsFilter: setHubBoundsFilter } = useCatalogHubs()
const { items: suppliers, isLoading: suppliersLoading, isLoadingMore: suppliersLoadingMore, canLoadMore: suppliersCanLoadMore, loadMore: loadMoreSuppliers, init: initSuppliers, setProductFilter: setSupplierProductFilter, setBoundsFilter: setSupplierBoundsFilter } = useCatalogSuppliers()
// Filtered items by map bounds
const filteredProducts = computed(() => {
if (!filterByBounds.value || !currentMapBounds.value) return products.value
return products.value.filter((p: any) => isInBounds(p, currentMapBounds.value))
})
const filteredHubs = computed(() => {
if (!filterByBounds.value || !currentMapBounds.value) return hubs.value
return hubs.value.filter((h: any) => isInBounds(h, currentMapBounds.value))
})
const filteredSuppliers = computed(() => {
if (!filterByBounds.value || !currentMapBounds.value) return suppliers.value
return suppliers.value.filter((s: any) => isInBounds(s, currentMapBounds.value))
})
// Items are now filtered on the backend via setBoundsFilter
// These are simple pass-throughs to maintain template compatibility
const filteredProducts = computed(() => products.value)
const filteredHubs = computed(() => hubs.value)
const filteredSuppliers = computed(() => suppliers.value)
// Selection loading state
const selectionLoading = computed(() => {
@@ -202,6 +183,14 @@ watch(productId, (newProductId) => {
setSupplierProductFilter(newProductId || null)
}, { immediate: true })
// Apply bounds filter when "filter by map bounds" is enabled
watch([filterByBounds, currentMapBounds], ([enabled, bounds]) => {
const boundsToApply = enabled && bounds ? bounds : null
setHubBoundsFilter(boundsToApply)
setSupplierBoundsFilter(boundsToApply)
setProductBoundsFilter(boundsToApply)
})
// Offers data for quote results
const offers = ref<any[]>([])
const offersLoading = ref(false)

View File

@@ -1,5 +1,5 @@
query GetNodes($limit: Int, $offset: Int, $transportType: String, $country: String) {
nodes(limit: $limit, offset: $offset, transportType: $transportType, country: $country) {
query GetNodes($limit: Int, $offset: Int, $transportType: String, $country: String, $west: Float, $south: Float, $east: Float, $north: Float) {
nodes(limit: $limit, offset: $offset, transportType: $transportType, country: $country, west: $west, south: $south, east: $east, north: $north) {
uuid
name
latitude
@@ -9,5 +9,5 @@ query GetNodes($limit: Int, $offset: Int, $transportType: String, $country: Stri
syncedAt
transportTypes
}
nodesCount(transportType: $transportType, country: $country)
nodesCount(transportType: $transportType, country: $country, west: $west, south: $south, east: $east, north: $north)
}