Use offer result cards in catalog and compute routes for supplier offers
All checks were successful
Build Docker Image / build (push) Successful in 5m50s
All checks were successful
Build Docker Image / build (push) Successful in 5m50s
This commit is contained in:
@@ -37,13 +37,14 @@
|
|||||||
|
|
||||||
<!-- Offers Tab -->
|
<!-- Offers Tab -->
|
||||||
<template v-if="activeTab === 'offers'">
|
<template v-if="activeTab === 'offers'">
|
||||||
<OfferCard
|
<OfferResultCard
|
||||||
v-for="(offer, index) in offers"
|
v-for="(offer, index) in offers"
|
||||||
:key="offer.uuid ?? index"
|
:key="offer.uuid ?? index"
|
||||||
:offer="offer"
|
:location-name="offer.locationName"
|
||||||
selectable
|
:product-name="offer.title || undefined"
|
||||||
compact
|
:stages="[]"
|
||||||
:is-selected="selectedId === offer.uuid"
|
:start-name="offer.locationName || undefined"
|
||||||
|
:end-name="undefined"
|
||||||
@select="selectOffer(offer)"
|
@select="selectOffer(offer)"
|
||||||
/>
|
/>
|
||||||
<Text v-if="offers.length === 0" tone="muted" size="sm" class="text-center py-4">
|
<Text v-if="offers.length === 0" tone="muted" size="sm" class="text-center py-4">
|
||||||
|
|||||||
@@ -13,10 +13,12 @@
|
|||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
<Grid :cols="1" :md="2" :lg="3" :gap="4">
|
<Grid :cols="1" :md="2" :lg="3" :gap="4">
|
||||||
<OfferCard
|
<OfferResultCard
|
||||||
v-for="(offer, index) in offers"
|
v-for="(offer, index) in offers"
|
||||||
:key="offer.uuid ?? index"
|
:key="offer.uuid ?? index"
|
||||||
:offer="offer"
|
:location-name="offer.locationName"
|
||||||
|
:product-name="offer.title || undefined"
|
||||||
|
:stages="[]"
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
|||||||
@@ -141,12 +141,15 @@
|
|||||||
{{ $t('catalog.empty.noOffers') }}
|
{{ $t('catalog.empty.noOffers') }}
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="!loadingOffers" class="flex flex-col gap-2">
|
<div v-else-if="!loadingOffers" class="flex flex-col gap-2">
|
||||||
<OfferCard
|
<OfferResultCard
|
||||||
v-for="(offer, index) in relatedOffers"
|
v-for="(offer, index) in relatedOffers"
|
||||||
:key="offer.uuid ?? index"
|
:key="offer.uuid ?? index"
|
||||||
:offer="offer"
|
:location-name="offer.locationName || offer.locationCountry || offer.locationName"
|
||||||
compact
|
:product-name="offer.productName"
|
||||||
selectable
|
:price-per-unit="offer.pricePerUnit ? Number(offer.pricePerUnit) : null"
|
||||||
|
:currency="offer.currency"
|
||||||
|
:unit="offer.unit"
|
||||||
|
:stages="getOfferStages(offer)"
|
||||||
@select="onOfferSelect(offer)"
|
@select="onOfferSelect(offer)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -217,6 +220,7 @@ import type {
|
|||||||
InfoSupplierItem,
|
InfoSupplierItem,
|
||||||
InfoOfferItem
|
InfoOfferItem
|
||||||
} from '~/composables/useCatalogInfo'
|
} from '~/composables/useCatalogInfo'
|
||||||
|
import type { RouteStageType } from '~/composables/graphql/public/geo-generated'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
entityType: InfoEntityType
|
entityType: InfoEntityType
|
||||||
@@ -344,4 +348,15 @@ const onSupplierSelect = (supplier: InfoSupplierItem) => {
|
|||||||
emit('open-info', 'supplier', supplier.uuid)
|
emit('open-info', 'supplier', supplier.uuid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getOfferStages = (offer: InfoOfferItem) => {
|
||||||
|
const route = offer.routes?.[0]
|
||||||
|
if (!route?.stages) return []
|
||||||
|
return route.stages
|
||||||
|
.filter((stage): stage is NonNullable<RouteStageType> => stage !== null)
|
||||||
|
.map(stage => ({
|
||||||
|
transportType: stage.transportType,
|
||||||
|
distanceKm: stage.distanceKm
|
||||||
|
}))
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -26,7 +26,14 @@
|
|||||||
class="cursor-pointer"
|
class="cursor-pointer"
|
||||||
@click="emit('select-offer', offer)"
|
@click="emit('select-offer', offer)"
|
||||||
>
|
>
|
||||||
<OfferCard :offer="offer" compact selectable />
|
<OfferResultCard
|
||||||
|
:location-name="offer.locationName || offer.locationCountry"
|
||||||
|
:product-name="offer.productName"
|
||||||
|
:price-per-unit="offer.pricePerUnit ? Number(offer.pricePerUnit) : null"
|
||||||
|
:currency="offer.currency"
|
||||||
|
:unit="offer.unit"
|
||||||
|
:stages="[]"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -431,6 +431,29 @@ export function useCatalogInfo() {
|
|||||||
isLoadingHubs.value = true
|
isLoadingHubs.value = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
let hubUuid: string | null = relatedHubs.value?.[0]?.uuid ?? null
|
||||||
|
if (!hubUuid && supplier.uuid) {
|
||||||
|
const hubsData = await execute(
|
||||||
|
NearestHubsDocument,
|
||||||
|
{
|
||||||
|
lat: supplier.latitude,
|
||||||
|
lon: supplier.longitude,
|
||||||
|
radius: 1000,
|
||||||
|
sourceUuid: supplier.uuid,
|
||||||
|
limit: 1
|
||||||
|
},
|
||||||
|
'public',
|
||||||
|
'geo'
|
||||||
|
)
|
||||||
|
const hub = (hubsData?.nearestHubs || []).find((h): h is HubItem => h !== null)
|
||||||
|
if (hub?.uuid) {
|
||||||
|
hubUuid = hub.uuid
|
||||||
|
if (!relatedHubs.value.length) {
|
||||||
|
relatedHubs.value = [hub]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Find offers near supplier for this product
|
// Find offers near supplier for this product
|
||||||
const offersData = await execute(
|
const offersData = await execute(
|
||||||
NearestOffersDocument,
|
NearestOffersDocument,
|
||||||
@@ -438,6 +461,7 @@ export function useCatalogInfo() {
|
|||||||
lat: supplier.latitude,
|
lat: supplier.latitude,
|
||||||
lon: supplier.longitude,
|
lon: supplier.longitude,
|
||||||
productUuid,
|
productUuid,
|
||||||
|
...(hubUuid ? { hubUuid } : {}),
|
||||||
radius: 500,
|
radius: 500,
|
||||||
limit: 12
|
limit: 12
|
||||||
},
|
},
|
||||||
@@ -447,37 +471,6 @@ export function useCatalogInfo() {
|
|||||||
|
|
||||||
relatedOffers.value = (offersData?.nearestOffers || []).filter((o): o is OfferItem => o !== null)
|
relatedOffers.value = (offersData?.nearestOffers || []).filter((o): o is OfferItem => o !== null)
|
||||||
isLoadingOffers.value = false
|
isLoadingOffers.value = false
|
||||||
|
|
||||||
// Load hubs near each offer and aggregate (limit to 12)
|
|
||||||
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
|
|
||||||
|
|
||||||
try {
|
|
||||||
const hubsData = await execute(
|
|
||||||
NearestHubsDocument,
|
|
||||||
{
|
|
||||||
lat: offer.latitude,
|
|
||||||
lon: offer.longitude,
|
|
||||||
radius: 1000,
|
|
||||||
limit: 5
|
|
||||||
},
|
|
||||||
'public',
|
|
||||||
'geo'
|
|
||||||
)
|
|
||||||
hubsData?.nearestHubs?.forEach(hub => {
|
|
||||||
if (hub && hub.uuid && !allHubs.has(hub.uuid)) {
|
|
||||||
allHubs.set(hub.uuid, hub)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} catch (e) {
|
|
||||||
console.warn('Error loading hubs for offer:', offer.uuid, e)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (allHubs.size >= 12) break
|
|
||||||
}
|
|
||||||
relatedHubs.value = Array.from(allHubs.values()).slice(0, 12)
|
|
||||||
} finally {
|
} finally {
|
||||||
isLoadingOffers.value = false
|
isLoadingOffers.value = false
|
||||||
isLoadingHubs.value = false
|
isLoadingHubs.value = false
|
||||||
|
|||||||
Reference in New Issue
Block a user