Fix type safety in catalog composables + 3 InfoPanel bugs
All checks were successful
Build Docker Image / build (push) Successful in 3m42s
All checks were successful
Build Docker Image / build (push) Successful in 3m42s
- Add proper codegen types to all catalog composables: - useCatalogHubs: HubItem, NearestHubItem - useCatalogSuppliers: SupplierItem, NearestSupplierItem - useCatalogProducts: ProductItem - useCatalogOffers: OfferItem - useCatalogInfo: InfoEntity, ProductItem, HubItem, OfferItem - Fix InfoPanel bugs for offers: - Use locationLatitude/locationLongitude for offer coordinates - Enrich entity with supplierName after loading profile - Apply-to-filter now adds both product AND hub for offers - Filter null values from GraphQL array responses - Add type-safe coordinate helper (getEntityCoords) - Fix urlBounds type inference in useCatalogSearch
This commit is contained in:
@@ -1,9 +1,14 @@
|
||||
import type { HubsListQueryResult, NearestHubsQueryResult } from '~/composables/graphql/public/geo-generated'
|
||||
import { HubsListDocument, GetHubCountriesDocument, NearestHubsDocument } from '~/composables/graphql/public/geo-generated'
|
||||
|
||||
const PAGE_SIZE = 24
|
||||
|
||||
// Type from codegen
|
||||
type HubItem = NonNullable<NonNullable<HubsListQueryResult['hubsList']>[number]>
|
||||
type NearestHubItem = NonNullable<NonNullable<NearestHubsQueryResult['nearestHubs']>[number]>
|
||||
|
||||
// Shared state across list and map views
|
||||
const items = ref<any[]>([])
|
||||
const items = ref<Array<HubItem | NearestHubItem>>([])
|
||||
const total = ref(0)
|
||||
const selectedFilter = ref('all')
|
||||
const selectedCountry = ref('all')
|
||||
@@ -67,7 +72,7 @@ export function useCatalogHubs() {
|
||||
'public',
|
||||
'geo'
|
||||
)
|
||||
const next = data?.nearestHubs || []
|
||||
const next = (data?.nearestHubs || []).filter((h): h is NearestHubItem => h !== null)
|
||||
items.value = next
|
||||
total.value = next.length
|
||||
isInitialized.value = true
|
||||
@@ -95,7 +100,7 @@ export function useCatalogHubs() {
|
||||
'public',
|
||||
'geo'
|
||||
)
|
||||
const next = data?.hubsList || []
|
||||
const next = (data?.hubsList || []).filter((h): h is HubItem => h !== null)
|
||||
items.value = replace ? next : items.value.concat(next)
|
||||
// hubsList doesn't return total count, estimate from fetched items
|
||||
if (replace) {
|
||||
|
||||
@@ -1,24 +1,82 @@
|
||||
import type { InfoEntityType } from './useCatalogSearch'
|
||||
import type {
|
||||
GetNodeQueryResult,
|
||||
NearestHubsQueryResult,
|
||||
NearestOffersQueryResult
|
||||
} from '~/composables/graphql/public/geo-generated'
|
||||
import {
|
||||
GetNodeDocument,
|
||||
NearestOffersDocument,
|
||||
NearestHubsDocument
|
||||
} from '~/composables/graphql/public/geo-generated'
|
||||
import type {
|
||||
GetOfferQueryResult,
|
||||
GetSupplierProfileQueryResult
|
||||
} from '~/composables/graphql/public/exchange-generated'
|
||||
import {
|
||||
GetOfferDocument,
|
||||
GetSupplierProfileDocument
|
||||
} from '~/composables/graphql/public/exchange-generated'
|
||||
|
||||
// Types from codegen
|
||||
type NodeEntity = NonNullable<GetNodeQueryResult['node']>
|
||||
type OfferEntity = NonNullable<GetOfferQueryResult['getOffer']>
|
||||
type SupplierProfile = NonNullable<GetSupplierProfileQueryResult['getSupplierProfile']>
|
||||
type HubItem = NonNullable<NonNullable<NearestHubsQueryResult['nearestHubs']>[number]>
|
||||
type OfferItem = NonNullable<NonNullable<NearestOffersQueryResult['nearestOffers']>[number]>
|
||||
|
||||
// Product type (aggregated from offers)
|
||||
interface ProductItem {
|
||||
uuid: string
|
||||
name: string
|
||||
offersCount?: number
|
||||
}
|
||||
|
||||
// Extended entity type with optional supplierName
|
||||
// Using intersection to allow both coordinate patterns (node vs offer)
|
||||
interface InfoEntity {
|
||||
uuid?: string | null
|
||||
name?: string | null
|
||||
// Node coordinates
|
||||
latitude?: number | null
|
||||
longitude?: number | null
|
||||
// Offer coordinates (different field names)
|
||||
locationLatitude?: number | null
|
||||
locationLongitude?: number | null
|
||||
locationUuid?: string
|
||||
locationName?: string
|
||||
// Offer fields
|
||||
productUuid?: string
|
||||
productName?: string
|
||||
teamUuid?: string
|
||||
// Enriched field
|
||||
supplierName?: string
|
||||
// Allow any other fields
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
// Helper to get coordinates from entity (handles both node and offer patterns)
|
||||
function getEntityCoords(e: InfoEntity | null): { lat: number; lon: number } | null {
|
||||
if (!e) return null
|
||||
// Try offer coords first (locationLatitude/locationLongitude)
|
||||
const lat = e.locationLatitude ?? e.latitude
|
||||
const lon = e.locationLongitude ?? e.longitude
|
||||
if (lat != null && lon != null) {
|
||||
return { lat, lon }
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export function useCatalogInfo() {
|
||||
const { execute } = useGraphQL()
|
||||
|
||||
// State
|
||||
const entity = ref<any>(null)
|
||||
const entityType = ref<InfoEntityType | null>(null) // Track entity type explicitly
|
||||
const relatedProducts = ref<any[]>([])
|
||||
const relatedHubs = ref<any[]>([])
|
||||
const relatedSuppliers = ref<any[]>([])
|
||||
const relatedOffers = ref<any[]>([])
|
||||
// State with proper types
|
||||
const entity = ref<InfoEntity | null>(null)
|
||||
const entityType = ref<InfoEntityType | null>(null)
|
||||
const relatedProducts = ref<ProductItem[]>([])
|
||||
const relatedHubs = ref<HubItem[]>([])
|
||||
const relatedSuppliers = ref<SupplierProfile[]>([])
|
||||
const relatedOffers = ref<OfferItem[]>([])
|
||||
const selectedProduct = ref<string | null>(null)
|
||||
const activeTab = ref<string>('products')
|
||||
const isLoading = ref(false)
|
||||
@@ -34,9 +92,10 @@ export function useCatalogInfo() {
|
||||
try {
|
||||
// Load hub node details
|
||||
const nodeData = await execute(GetNodeDocument, { uuid }, 'public', 'geo')
|
||||
entity.value = nodeData?.node
|
||||
entity.value = nodeData?.node ?? null
|
||||
|
||||
if (!entity.value?.latitude || !entity.value?.longitude) {
|
||||
const coords = getEntityCoords(entity.value)
|
||||
if (!coords) {
|
||||
console.warn('Hub has no coordinates')
|
||||
return
|
||||
}
|
||||
@@ -52,40 +111,41 @@ export function useCatalogInfo() {
|
||||
execute(
|
||||
NearestOffersDocument,
|
||||
{
|
||||
lat: entity.value.latitude,
|
||||
lon: entity.value.longitude,
|
||||
lat: coords.lat,
|
||||
lon: coords.lon,
|
||||
radius: 500
|
||||
},
|
||||
'public',
|
||||
'geo'
|
||||
).then(offersData => {
|
||||
// Group offers by product
|
||||
const productsMap = new Map<string, any>()
|
||||
const suppliersMap = new Map<string, any>()
|
||||
const productsMap = new Map<string, ProductItem>()
|
||||
const suppliersMap = new Map<string, { uuid: string; name: string; latitude?: number | null; longitude?: number | null }>()
|
||||
|
||||
offersData?.nearestOffers?.forEach((offer: any) => {
|
||||
offersData?.nearestOffers?.forEach(offer => {
|
||||
if (!offer) return
|
||||
// Products
|
||||
if (offer?.productUuid) {
|
||||
if (!productsMap.has(offer.productUuid)) {
|
||||
if (offer.productUuid && offer.productName) {
|
||||
const existing = productsMap.get(offer.productUuid)
|
||||
if (existing) {
|
||||
existing.offersCount = (existing.offersCount || 0) + 1
|
||||
} else {
|
||||
productsMap.set(offer.productUuid, {
|
||||
uuid: offer.productUuid,
|
||||
name: offer.productName,
|
||||
offersCount: 0
|
||||
offersCount: 1
|
||||
})
|
||||
}
|
||||
productsMap.get(offer.productUuid)!.offersCount++
|
||||
}
|
||||
|
||||
// Suppliers (extract from offers)
|
||||
if (offer?.supplierUuid) {
|
||||
if (!suppliersMap.has(offer.supplierUuid)) {
|
||||
suppliersMap.set(offer.supplierUuid, {
|
||||
uuid: offer.supplierUuid,
|
||||
name: offer.supplierName || 'Supplier',
|
||||
latitude: offer.latitude,
|
||||
longitude: offer.longitude
|
||||
})
|
||||
}
|
||||
if (offer.supplierUuid && !suppliersMap.has(offer.supplierUuid)) {
|
||||
suppliersMap.set(offer.supplierUuid, {
|
||||
uuid: offer.supplierUuid,
|
||||
name: offer.supplierName || 'Supplier',
|
||||
latitude: offer.latitude,
|
||||
longitude: offer.longitude
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -101,7 +161,7 @@ export function useCatalogInfo() {
|
||||
.catch(() => suppliersMap.get(supplierId)) // Fallback to basic info
|
||||
)
|
||||
).then(profiles => {
|
||||
relatedSuppliers.value = profiles.filter(Boolean)
|
||||
relatedSuppliers.value = profiles.filter((p): p is SupplierProfile => p != null)
|
||||
isLoadingSuppliers.value = false
|
||||
})
|
||||
} else {
|
||||
@@ -123,7 +183,7 @@ export function useCatalogInfo() {
|
||||
try {
|
||||
// Load supplier node details (might be geo node)
|
||||
const nodeData = await execute(GetNodeDocument, { uuid }, 'public', 'geo')
|
||||
entity.value = nodeData?.node
|
||||
entity.value = nodeData?.node ?? null
|
||||
|
||||
// Also try to get supplier profile from exchange API for additional details
|
||||
try {
|
||||
@@ -164,17 +224,18 @@ export function useCatalogInfo() {
|
||||
'geo'
|
||||
).then(offersData => {
|
||||
// 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++
|
||||
const productsMap = new Map<string, ProductItem>()
|
||||
offersData?.nearestOffers?.forEach(offer => {
|
||||
if (!offer || !offer.productUuid || !offer.productName) return
|
||||
const existing = productsMap.get(offer.productUuid)
|
||||
if (existing) {
|
||||
existing.offersCount = (existing.offersCount || 0) + 1
|
||||
} else {
|
||||
productsMap.set(offer.productUuid, {
|
||||
uuid: offer.productUuid,
|
||||
name: offer.productName,
|
||||
offersCount: 1
|
||||
})
|
||||
}
|
||||
})
|
||||
relatedProducts.value = Array.from(productsMap.values())
|
||||
@@ -194,7 +255,7 @@ export function useCatalogInfo() {
|
||||
'public',
|
||||
'geo'
|
||||
).then(hubsData => {
|
||||
relatedHubs.value = hubsData?.nearestHubs || []
|
||||
relatedHubs.value = (hubsData?.nearestHubs || []).filter((h): h is HubItem => h !== null)
|
||||
}).finally(() => {
|
||||
isLoadingHubs.value = false
|
||||
})
|
||||
@@ -210,9 +271,10 @@ export function useCatalogInfo() {
|
||||
try {
|
||||
// Load offer details from exchange API
|
||||
const offerData = await execute(GetOfferDocument, { uuid }, 'public', 'exchange')
|
||||
entity.value = offerData?.getOffer
|
||||
entity.value = offerData?.getOffer ?? null
|
||||
|
||||
if (!entity.value?.latitude || !entity.value?.longitude) {
|
||||
const coords = getEntityCoords(entity.value)
|
||||
if (!coords) {
|
||||
console.warn('Offer has no coordinates')
|
||||
return
|
||||
}
|
||||
@@ -235,15 +297,15 @@ export function useCatalogInfo() {
|
||||
execute(
|
||||
NearestHubsDocument,
|
||||
{
|
||||
lat: entity.value.latitude,
|
||||
lon: entity.value.longitude,
|
||||
lat: coords.lat,
|
||||
lon: coords.lon,
|
||||
radius: 1000,
|
||||
limit: 12
|
||||
},
|
||||
'public',
|
||||
'geo'
|
||||
).then(hubsData => {
|
||||
relatedHubs.value = hubsData?.nearestHubs || []
|
||||
relatedHubs.value = (hubsData?.nearestHubs || []).filter((h): h is HubItem => h !== null)
|
||||
}).finally(() => {
|
||||
isLoadingHubs.value = false
|
||||
})
|
||||
@@ -257,9 +319,12 @@ export function useCatalogInfo() {
|
||||
'public',
|
||||
'exchange'
|
||||
).then(supplierData => {
|
||||
relatedSuppliers.value = supplierData?.getSupplierProfile
|
||||
? [supplierData.getSupplierProfile]
|
||||
: []
|
||||
const supplier = supplierData?.getSupplierProfile
|
||||
relatedSuppliers.value = supplier ? [supplier] : []
|
||||
// Enrich entity with supplier name for display
|
||||
if (supplier?.name && entity.value) {
|
||||
entity.value = { ...entity.value, supplierName: supplier.name }
|
||||
}
|
||||
}).catch(() => {
|
||||
// Supplier might not exist
|
||||
}).finally(() => {
|
||||
@@ -301,19 +366,19 @@ export function useCatalogInfo() {
|
||||
)
|
||||
|
||||
// Offers already include routes from backend
|
||||
relatedOffers.value = offersData?.nearestOffers || []
|
||||
relatedOffers.value = (offersData?.nearestOffers || []).filter((o): o is OfferItem => o !== null)
|
||||
isLoadingOffers.value = false
|
||||
|
||||
// Extract unique suppliers from offers (use supplierUuid from offers)
|
||||
const supplierUuids = new Set<string>()
|
||||
relatedOffers.value.forEach((offer: any) => {
|
||||
relatedOffers.value.forEach(offer => {
|
||||
if (offer.supplierUuid) {
|
||||
supplierUuids.add(offer.supplierUuid)
|
||||
}
|
||||
})
|
||||
|
||||
// Load supplier profiles (limit to 12)
|
||||
const suppliers: any[] = []
|
||||
const suppliers: SupplierProfile[] = []
|
||||
for (const uuid of Array.from(supplierUuids).slice(0, 12)) {
|
||||
try {
|
||||
const supplierData = await execute(
|
||||
@@ -366,11 +431,11 @@ export function useCatalogInfo() {
|
||||
'geo'
|
||||
)
|
||||
|
||||
relatedOffers.value = offersData?.nearestOffers || []
|
||||
relatedOffers.value = (offersData?.nearestOffers || []).filter((o): o is OfferItem => o !== null)
|
||||
isLoadingOffers.value = false
|
||||
|
||||
// Load hubs near each offer and aggregate (limit to 12)
|
||||
const allHubs = new Map<string, any>()
|
||||
const allHubs = new Map<string, HubItem>()
|
||||
for (const offer of relatedOffers.value.slice(0, 3)) {
|
||||
// Check first 3 offers
|
||||
if (!offer.latitude || !offer.longitude) continue
|
||||
@@ -387,8 +452,8 @@ export function useCatalogInfo() {
|
||||
'public',
|
||||
'geo'
|
||||
)
|
||||
hubsData?.nearestHubs?.forEach((hub: any) => {
|
||||
if (!allHubs.has(hub.uuid)) {
|
||||
hubsData?.nearestHubs?.forEach(hub => {
|
||||
if (hub && hub.uuid && !allHubs.has(hub.uuid)) {
|
||||
allHubs.set(hub.uuid, hub)
|
||||
}
|
||||
})
|
||||
@@ -415,10 +480,10 @@ export function useCatalogInfo() {
|
||||
if (!entity.value) return
|
||||
|
||||
// Use stored entity type instead of inferring from properties
|
||||
if (entityType.value === 'hub') {
|
||||
if (entityType.value === 'hub' && entity.value.uuid) {
|
||||
await loadOffersForHub(entity.value.uuid, productUuid)
|
||||
activeTab.value = 'offers'
|
||||
} else if (entityType.value === 'supplier') {
|
||||
} else if (entityType.value === 'supplier' && entity.value.uuid) {
|
||||
await loadOffersForSupplier(entity.value.uuid, productUuid)
|
||||
activeTab.value = 'offers'
|
||||
}
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import type { GetOffersQueryResult } from '~/composables/graphql/public/exchange-generated'
|
||||
import { GetOffersDocument } from '~/composables/graphql/public/exchange-generated'
|
||||
|
||||
const PAGE_SIZE = 24
|
||||
|
||||
// Type from codegen
|
||||
type OfferItem = NonNullable<NonNullable<GetOffersQueryResult['getOffers']>[number]>
|
||||
|
||||
// Shared state across list and map views
|
||||
const items = ref<any[]>([])
|
||||
const items = ref<OfferItem[]>([])
|
||||
const total = ref(0)
|
||||
const selectedProductUuid = ref<string | null>(null)
|
||||
const isLoading = ref(false)
|
||||
@@ -18,7 +22,7 @@ export function useCatalogOffers() {
|
||||
.filter(offer => offer.locationLatitude && offer.locationLongitude)
|
||||
.map(offer => ({
|
||||
uuid: offer.uuid,
|
||||
name: offer.productName || offer.title,
|
||||
name: offer.productName || offer.locationName,
|
||||
latitude: offer.locationLatitude,
|
||||
longitude: offer.locationLongitude,
|
||||
country: offer.locationCountry
|
||||
@@ -40,7 +44,7 @@ export function useCatalogOffers() {
|
||||
'public',
|
||||
'exchange'
|
||||
)
|
||||
const next = data?.getOffers || []
|
||||
const next = (data?.getOffers || []).filter((o): o is OfferItem => o !== null)
|
||||
items.value = replace ? next : items.value.concat(next)
|
||||
total.value = data?.getOffersCount ?? total.value
|
||||
isInitialized.value = true
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { ProductsListQueryResult } from '~/composables/graphql/public/geo-generated'
|
||||
import {
|
||||
ProductsListDocument,
|
||||
GetNodeDocument,
|
||||
@@ -7,8 +8,11 @@ import {
|
||||
GetSupplierProfileDocument
|
||||
} from '~/composables/graphql/public/exchange-generated'
|
||||
|
||||
// Type from codegen
|
||||
type ProductItem = NonNullable<NonNullable<ProductsListQueryResult['productsList']>[number]>
|
||||
|
||||
// Shared state
|
||||
const items = ref<any[]>([])
|
||||
const items = ref<ProductItem[]>([])
|
||||
const isLoading = ref(false)
|
||||
const isLoadingMore = ref(false)
|
||||
const isInitialized = ref(false)
|
||||
@@ -130,7 +134,7 @@ export function useCatalogProducts() {
|
||||
'public',
|
||||
'geo'
|
||||
)
|
||||
items.value = data?.productsList || []
|
||||
items.value = (data?.productsList || []).filter((p): p is ProductItem => p !== null)
|
||||
}
|
||||
|
||||
isInitialized.value = true
|
||||
|
||||
@@ -85,12 +85,12 @@ export function useCatalogSearch() {
|
||||
const quantity = computed(() => route.query.qty as string | undefined)
|
||||
|
||||
// Map bounds from URL (format: west,south,east,north)
|
||||
const urlBounds = computed(() => {
|
||||
const urlBounds = computed((): { west: number; south: number; east: number; north: number } | null => {
|
||||
const b = route.query.bounds as string | undefined
|
||||
if (!b) return null
|
||||
const parts = b.split(',').map(Number)
|
||||
if (parts.length !== 4 || parts.some(isNaN)) return null
|
||||
return { west: parts[0], south: parts[1], east: parts[2], north: parts[3] }
|
||||
return { west: parts[0]!, south: parts[1]!, east: parts[2]!, north: parts[3]! }
|
||||
})
|
||||
|
||||
// Filter by bounds checkbox state from URL
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
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<NonNullable<SuppliersListQueryResult['suppliersList']>[number]>
|
||||
type NearestSupplierItem = NonNullable<NonNullable<NearestSuppliersQueryResult['nearestSuppliers']>[number]>
|
||||
|
||||
// Shared state across list and map views
|
||||
const items = ref<any[]>([])
|
||||
const items = ref<Array<SupplierItem | NearestSupplierItem>>([])
|
||||
const total = ref(0)
|
||||
const isLoading = ref(false)
|
||||
const isLoadingMore = ref(false)
|
||||
@@ -15,7 +20,7 @@ export function useCatalogSuppliers() {
|
||||
const { execute } = useGraphQL()
|
||||
|
||||
const itemsWithCoords = computed(() =>
|
||||
items.value.filter(s => s.latitude && s.longitude)
|
||||
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)
|
||||
@@ -38,7 +43,7 @@ export function useCatalogSuppliers() {
|
||||
'public',
|
||||
'geo'
|
||||
)
|
||||
items.value = data?.nearestSuppliers || []
|
||||
items.value = (data?.nearestSuppliers || []).filter((s): s is NearestSupplierItem => s !== null)
|
||||
total.value = items.value.length
|
||||
isInitialized.value = true
|
||||
return
|
||||
@@ -60,7 +65,7 @@ export function useCatalogSuppliers() {
|
||||
'public',
|
||||
'geo'
|
||||
)
|
||||
const next = data?.suppliersList || []
|
||||
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
|
||||
|
||||
@@ -91,11 +91,27 @@ const onHoverItem = (uuid: string | null) => {
|
||||
hoveredItemId.value = uuid
|
||||
}
|
||||
|
||||
// Type for map items - must have required string uuid and number coordinates
|
||||
type MapItemWithCoords = { uuid: string; name: string; latitude: number; longitude: number; country?: string }
|
||||
|
||||
// Helper to convert items to map-compatible format (filter null values)
|
||||
const toMapItems = <T extends { uuid?: string | null; name?: string | null; latitude?: number | null; longitude?: number | null }>(
|
||||
items: T[]
|
||||
): MapItemWithCoords[] =>
|
||||
items.filter((item): item is T & { uuid: string; latitude: number; longitude: number } =>
|
||||
item.uuid != null && item.latitude != null && item.longitude != null
|
||||
).map(item => ({
|
||||
uuid: item.uuid,
|
||||
name: item.name || '',
|
||||
latitude: item.latitude,
|
||||
longitude: item.longitude
|
||||
}))
|
||||
|
||||
// Current selection items for hover highlighting on map
|
||||
const currentSelectionItems = computed(() => {
|
||||
if (selectMode.value === 'product') return filteredProducts.value
|
||||
if (selectMode.value === 'hub') return filteredHubs.value
|
||||
if (selectMode.value === 'supplier') return filteredSuppliers.value
|
||||
const currentSelectionItems = computed((): MapItemWithCoords[] => {
|
||||
if (selectMode.value === 'product') return [] // Products don't have coordinates
|
||||
if (selectMode.value === 'hub') return toMapItems(filteredHubs.value)
|
||||
if (selectMode.value === 'supplier') return toMapItems(filteredSuppliers.value)
|
||||
return []
|
||||
})
|
||||
|
||||
@@ -280,10 +296,10 @@ const relatedPoints = computed(() => {
|
||||
|
||||
// Add all hubs
|
||||
relatedHubs.value.forEach(hub => {
|
||||
if (hub.latitude && hub.longitude) {
|
||||
if (hub.uuid && hub.latitude && hub.longitude) {
|
||||
points.push({
|
||||
uuid: hub.uuid,
|
||||
name: hub.name,
|
||||
name: hub.name || '',
|
||||
latitude: hub.latitude,
|
||||
longitude: hub.longitude,
|
||||
type: 'hub'
|
||||
@@ -293,10 +309,10 @@ const relatedPoints = computed(() => {
|
||||
|
||||
// Add all suppliers
|
||||
relatedSuppliers.value.forEach(supplier => {
|
||||
if (supplier.latitude && supplier.longitude) {
|
||||
if (supplier.uuid && supplier.latitude && supplier.longitude) {
|
||||
points.push({
|
||||
uuid: supplier.uuid,
|
||||
name: supplier.name,
|
||||
name: supplier.name || '',
|
||||
latitude: supplier.latitude,
|
||||
longitude: supplier.longitude,
|
||||
type: 'supplier'
|
||||
@@ -416,10 +432,17 @@ const onInfoAddToFilter = () => {
|
||||
if (!infoId.value || !entity.value) return
|
||||
const { type, uuid } = infoId.value
|
||||
|
||||
// For offers, add the product to filter (not the offer itself)
|
||||
if (type === 'offer' && entity.value.productUuid) {
|
||||
const productName = entity.value.productName || entity.value.name || uuid.slice(0, 8) + '...'
|
||||
selectItem('product', entity.value.productUuid, productName)
|
||||
// For offers, add the product AND hub to filter
|
||||
if (type === 'offer') {
|
||||
if (entity.value.productUuid) {
|
||||
const productName = entity.value.productName || entity.value.name || uuid.slice(0, 8) + '...'
|
||||
selectItem('product', entity.value.productUuid, productName)
|
||||
}
|
||||
// Also add hub (location) to filter if available
|
||||
if (entity.value.locationUuid) {
|
||||
const hubName = entity.value.locationName || entity.value.locationUuid.slice(0, 8) + '...'
|
||||
selectItem('hub', entity.value.locationUuid, hubName)
|
||||
}
|
||||
} else {
|
||||
// For hubs and suppliers, add directly
|
||||
const name = entity.value.name || uuid.slice(0, 8) + '...'
|
||||
|
||||
Reference in New Issue
Block a user