feat(catalog): filter map clusters by chips
All checks were successful
Build Docker Image / build (push) Successful in 5m1s
All checks were successful
Build Docker Image / build (push) Successful in 5m1s
This commit is contained in:
@@ -116,13 +116,6 @@
|
||||
:key="product.uuid ?? index"
|
||||
class="relative group"
|
||||
>
|
||||
<button
|
||||
v-if="product.uuid"
|
||||
class="absolute top-2 right-2 z-10 opacity-0 group-hover:opacity-100 transition-opacity bg-black/40 hover:bg-black/60 text-white rounded-full w-6 h-6 flex items-center justify-center"
|
||||
@click.stop="emit('pin', 'product', product)"
|
||||
>
|
||||
<Icon name="lucide:pin" size="12" />
|
||||
</button>
|
||||
<ProductCard
|
||||
:product="product"
|
||||
compact
|
||||
@@ -191,13 +184,6 @@
|
||||
:key="supplier.uuid ?? index"
|
||||
class="relative group"
|
||||
>
|
||||
<button
|
||||
v-if="supplier.uuid"
|
||||
class="absolute top-2 right-2 z-10 opacity-0 group-hover:opacity-100 transition-opacity bg-black/40 hover:bg-black/60 text-white rounded-full w-6 h-6 flex items-center justify-center"
|
||||
@click.stop="emit('pin', 'supplier', supplier)"
|
||||
>
|
||||
<Icon name="lucide:pin" size="12" />
|
||||
</button>
|
||||
<SupplierCard
|
||||
:supplier="supplier"
|
||||
selectable
|
||||
@@ -235,13 +221,6 @@
|
||||
:key="hub.uuid ?? index"
|
||||
class="relative group"
|
||||
>
|
||||
<button
|
||||
v-if="hub.uuid"
|
||||
class="absolute top-2 right-2 z-10 opacity-0 group-hover:opacity-100 transition-opacity bg-black/40 hover:bg-black/60 text-white rounded-full w-6 h-6 flex items-center justify-center"
|
||||
@click.stop="emit('pin', 'hub', hub)"
|
||||
>
|
||||
<Icon name="lucide:pin" size="12" />
|
||||
</button>
|
||||
<HubCard
|
||||
:hub="hub"
|
||||
:origin="originCoords"
|
||||
@@ -267,13 +246,6 @@
|
||||
:key="hub.uuid ?? index"
|
||||
class="relative group"
|
||||
>
|
||||
<button
|
||||
v-if="hub.uuid"
|
||||
class="absolute top-2 right-2 z-10 opacity-0 group-hover:opacity-100 transition-opacity bg-black/40 hover:bg-black/60 text-white rounded-full w-6 h-6 flex items-center justify-center"
|
||||
@click.stop="emit('pin', 'hub', hub)"
|
||||
>
|
||||
<Icon name="lucide:pin" size="12" />
|
||||
</button>
|
||||
<HubCard
|
||||
:hub="hub"
|
||||
:origin="originCoords"
|
||||
@@ -286,11 +258,6 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Add to filter button -->
|
||||
<button class="btn btn-primary btn-sm mt-2" @click="emit('add-to-filter')">
|
||||
<Icon name="lucide:filter-plus" size="16" />
|
||||
{{ $t('catalog.info.addToFilter') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -326,8 +293,6 @@ const props = defineProps<{
|
||||
|
||||
const emit = defineEmits<{
|
||||
'close': []
|
||||
'add-to-filter': []
|
||||
'pin': [type: 'product' | 'supplier' | 'hub', item: { uuid?: string | null; name?: string | null }]
|
||||
'open-info': [type: InfoEntityType, uuid: string]
|
||||
'select-product': [uuid: string | null]
|
||||
'select-offer': [offer: { uuid: string; productUuid?: string | null }]
|
||||
|
||||
@@ -31,13 +31,6 @@
|
||||
@mouseenter="emit('hover', item.uuid ?? null)"
|
||||
@mouseleave="emit('hover', null)"
|
||||
>
|
||||
<button
|
||||
v-if="item.uuid"
|
||||
class="absolute top-2 right-2 z-10 opacity-0 group-hover:opacity-100 transition-opacity bg-black/40 hover:bg-black/60 text-white rounded-full w-6 h-6 flex items-center justify-center"
|
||||
@click.stop="onPin(item)"
|
||||
>
|
||||
<Icon name="lucide:pin" size="12" />
|
||||
</button>
|
||||
<ProductCard
|
||||
:product="item"
|
||||
selectable
|
||||
@@ -56,13 +49,6 @@
|
||||
@mouseenter="emit('hover', item.uuid ?? null)"
|
||||
@mouseleave="emit('hover', null)"
|
||||
>
|
||||
<button
|
||||
v-if="item.uuid"
|
||||
class="absolute top-2 right-2 z-10 opacity-0 group-hover:opacity-100 transition-opacity bg-black/40 hover:bg-black/60 text-white rounded-full w-6 h-6 flex items-center justify-center"
|
||||
@click.stop="onPin(item)"
|
||||
>
|
||||
<Icon name="lucide:pin" size="12" />
|
||||
</button>
|
||||
<HubCard
|
||||
:hub="item"
|
||||
selectable
|
||||
@@ -80,13 +66,6 @@
|
||||
@mouseenter="emit('hover', item.uuid ?? null)"
|
||||
@mouseleave="emit('hover', null)"
|
||||
>
|
||||
<button
|
||||
v-if="item.uuid"
|
||||
class="absolute top-2 right-2 z-10 opacity-0 group-hover:opacity-100 transition-opacity bg-black/40 hover:bg-black/60 text-white rounded-full w-6 h-6 flex items-center justify-center"
|
||||
@click.stop="onPin(item)"
|
||||
>
|
||||
<Icon name="lucide:pin" size="12" />
|
||||
</button>
|
||||
<SupplierCard
|
||||
:supplier="item"
|
||||
selectable
|
||||
@@ -129,7 +108,6 @@ const props = defineProps<{
|
||||
|
||||
const emit = defineEmits<{
|
||||
'select': [type: string, item: Item]
|
||||
'pin': [type: string, item: Item]
|
||||
'close': []
|
||||
'load-more': []
|
||||
'hover': [uuid: string | null]
|
||||
@@ -191,10 +169,4 @@ const onSelect = (item: Item) => {
|
||||
emit('select', props.selectMode, item)
|
||||
}
|
||||
}
|
||||
|
||||
const onPin = (item: Item) => {
|
||||
if (props.selectMode && item.uuid) {
|
||||
emit('pin', props.selectMode, item)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -71,6 +71,7 @@
|
||||
<!-- View mode toggle -->
|
||||
<div class="flex gap-1 glass-bright rounded-full p-1">
|
||||
<button
|
||||
v-if="showOffersToggle"
|
||||
class="flex items-center gap-2 px-3 py-1.5 rounded-md text-sm font-medium transition-colors"
|
||||
:class="mapViewMode === 'offers' ? 'bg-white/20 text-white' : 'text-white/70 hover:text-white hover:bg-white/10'"
|
||||
@click="setMapViewMode('offers')"
|
||||
@@ -81,6 +82,7 @@
|
||||
{{ $t('catalog.views.offers') }}
|
||||
</button>
|
||||
<button
|
||||
v-if="showHubsToggle"
|
||||
class="flex items-center gap-2 px-3 py-1.5 rounded-md text-sm font-medium transition-colors"
|
||||
:class="mapViewMode === 'hubs' ? 'bg-white/20 text-white' : 'text-white/70 hover:text-white hover:bg-white/10'"
|
||||
@click="setMapViewMode('hubs')"
|
||||
@@ -91,6 +93,7 @@
|
||||
{{ $t('catalog.views.hubs') }}
|
||||
</button>
|
||||
<button
|
||||
v-if="showSuppliersToggle"
|
||||
class="flex items-center gap-2 px-3 py-1.5 rounded-md text-sm font-medium transition-colors"
|
||||
:class="mapViewMode === 'suppliers' ? 'bg-white/20 text-white' : 'text-white/70 hover:text-white hover:bg-white/10'"
|
||||
@click="setMapViewMode('suppliers')"
|
||||
@@ -132,6 +135,7 @@
|
||||
<!-- Mobile view toggle - hide in info mode or when hideViewToggle -->
|
||||
<div v-if="!isInfoMode && !hideViewToggle" class="flex gap-1 glass-bright rounded-full p-1">
|
||||
<button
|
||||
v-if="showOffersToggle"
|
||||
class="flex items-center justify-center w-8 h-8 rounded-md transition-colors"
|
||||
:class="mapViewMode === 'offers' ? 'bg-white/20' : 'hover:bg-white/10'"
|
||||
@click="setMapViewMode('offers')"
|
||||
@@ -141,6 +145,7 @@
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
v-if="showHubsToggle"
|
||||
class="flex items-center justify-center w-8 h-8 rounded-md transition-colors"
|
||||
:class="mapViewMode === 'hubs' ? 'bg-white/20' : 'hover:bg-white/10'"
|
||||
@click="setMapViewMode('hubs')"
|
||||
@@ -150,6 +155,7 @@
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
v-if="showSuppliersToggle"
|
||||
class="flex items-center justify-center w-8 h-8 rounded-md transition-colors"
|
||||
:class="mapViewMode === 'suppliers' ? 'bg-white/20' : 'hover:bg-white/10'"
|
||||
@click="setMapViewMode('suppliers')"
|
||||
@@ -258,6 +264,12 @@ const props = withDefaults(defineProps<{
|
||||
forceInfoMode?: boolean
|
||||
panelWidth?: string
|
||||
hideViewToggle?: boolean
|
||||
showOffersToggle?: boolean
|
||||
showHubsToggle?: boolean
|
||||
showSuppliersToggle?: boolean
|
||||
clusterProductUuid?: string
|
||||
clusterHubUuid?: string
|
||||
clusterSupplierUuid?: string
|
||||
relatedPoints?: Array<{
|
||||
uuid: string
|
||||
name: string
|
||||
@@ -279,6 +291,12 @@ const props = withDefaults(defineProps<{
|
||||
forceInfoMode: false,
|
||||
panelWidth: 'w-96',
|
||||
hideViewToggle: false,
|
||||
showOffersToggle: true,
|
||||
showHubsToggle: true,
|
||||
showSuppliersToggle: true,
|
||||
clusterProductUuid: undefined,
|
||||
clusterHubUuid: undefined,
|
||||
clusterSupplierUuid: undefined,
|
||||
relatedPoints: () => []
|
||||
})
|
||||
|
||||
@@ -291,13 +309,23 @@ const emit = defineEmits<{
|
||||
|
||||
const useTypedClusters = computed(() => props.useTypedClusters && props.useServerClustering)
|
||||
|
||||
const clusterProductUuid = computed(() => props.clusterProductUuid ?? undefined)
|
||||
const clusterHubUuid = computed(() => props.clusterHubUuid ?? undefined)
|
||||
const clusterSupplierUuid = computed(() => props.clusterSupplierUuid ?? undefined)
|
||||
|
||||
// Server-side clustering (single-type mode)
|
||||
const { clusteredNodes, fetchClusters, loading: singleClusterLoading, clearNodes } = useClusteredNodes(undefined, activeClusterNodeType)
|
||||
const { clusteredNodes, fetchClusters, loading: singleClusterLoading, clearNodes } = useClusteredNodes(
|
||||
undefined,
|
||||
activeClusterNodeType,
|
||||
clusterProductUuid,
|
||||
clusterHubUuid,
|
||||
clusterSupplierUuid
|
||||
)
|
||||
|
||||
// Server-side clustering (typed mode)
|
||||
const offerClusters = useClusteredNodes(undefined, ref('offer'))
|
||||
const hubClusters = useClusteredNodes(undefined, ref('logistics'))
|
||||
const supplierClusters = useClusteredNodes(undefined, ref('supplier'))
|
||||
const offerClusters = useClusteredNodes(undefined, ref('offer'), clusterProductUuid, clusterHubUuid, clusterSupplierUuid)
|
||||
const hubClusters = useClusteredNodes(undefined, ref('logistics'), clusterProductUuid, clusterHubUuid, clusterSupplierUuid)
|
||||
const supplierClusters = useClusteredNodes(undefined, ref('supplier'), clusterProductUuid, clusterHubUuid, clusterSupplierUuid)
|
||||
|
||||
const clusteredPointsByType = computed(() => ({
|
||||
offer: offerClusters.clusteredNodes.value,
|
||||
@@ -341,6 +369,7 @@ const fetchActiveClusters = async () => {
|
||||
// Refetch clusters when view mode changes
|
||||
watch(mapViewMode, async () => {
|
||||
if (!props.useServerClustering) return
|
||||
if (isInfoMode.value) return
|
||||
if (useTypedClusters.value) {
|
||||
clearInactiveClusters(activeClusterType.value)
|
||||
if (currentBounds.value) {
|
||||
@@ -356,6 +385,17 @@ watch(mapViewMode, async () => {
|
||||
}
|
||||
})
|
||||
|
||||
watch([clusterProductUuid, clusterHubUuid, clusterSupplierUuid], async () => {
|
||||
if (!props.useServerClustering) return
|
||||
if (isInfoMode.value) return
|
||||
if (!currentBounds.value) return
|
||||
if (useTypedClusters.value) {
|
||||
await fetchActiveClusters()
|
||||
return
|
||||
}
|
||||
await fetchClusters(currentBounds.value)
|
||||
})
|
||||
|
||||
// Map refs
|
||||
const mapRef = ref<{ flyTo: (lat: number, lng: number, zoom?: number) => void } | null>(null)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user