Make pins explicit and selection open info
All checks were successful
Build Docker Image / build (push) Successful in 5m24s

This commit is contained in:
Ruslan Bakiev
2026-02-07 13:56:36 +07:00
parent 2d54dc3283
commit a73a801a1d
4 changed files with 72 additions and 25 deletions

View File

@@ -12,9 +12,20 @@
</div> </div>
<h3 class="font-semibold text-base text-white">{{ entityName }}</h3> <h3 class="font-semibold text-base text-white">{{ entityName }}</h3>
</div> </div>
<button class="btn btn-ghost btn-xs btn-circle text-white/60 hover:text-white" @click="emit('close')"> <div class="flex items-center gap-2">
<Icon name="lucide:x" size="16" /> <button
</button> v-if="(entityType === 'hub' || entityType === 'supplier') && entity?.uuid"
class="rounded-full glass-bright border border-white/30 shadow-lg p-1.5 transition-transform hover:scale-105"
@click="emit('pin', entityType, { uuid: entity?.uuid, name: entity?.name })"
aria-label="Pin"
title="Pin"
>
<Icon name="lucide:pin" size="16" class="text-white" />
</button>
<button class="btn btn-ghost btn-xs btn-circle text-white/60 hover:text-white" @click="emit('close')">
<Icon name="lucide:x" size="16" />
</button>
</div>
</div> </div>
</div> </div>
@@ -123,11 +134,12 @@
@select="onProductSelect(product)" @select="onProductSelect(product)"
/> />
<button <button
class="absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity rounded-full bg-white/10 hover:bg-white/20 p-1" class="absolute -top-2 -right-2 rounded-full glass-bright border border-white/30 shadow-lg p-1.5 transition-transform hover:scale-105"
@click.stop="emit('pin', 'product', product)" @click.stop="emit('pin', 'product', product)"
aria-label="Pin product" aria-label="Pin product"
title="Pin"
> >
<Icon name="lucide:pin" size="14" class="text-white/80" /> <Icon name="lucide:pin" size="16" class="text-white" />
</button> </button>
</div> </div>
</div> </div>
@@ -197,11 +209,12 @@
@select="onSupplierSelect(supplier)" @select="onSupplierSelect(supplier)"
/> />
<button <button
class="absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity rounded-full bg-white/10 hover:bg-white/20 p-1" class="absolute -top-2 -right-2 rounded-full glass-bright border border-white/30 shadow-lg p-1.5 transition-transform hover:scale-105"
@click.stop="emit('pin', 'supplier', supplier)" @click.stop="emit('pin', 'supplier', supplier)"
aria-label="Pin supplier" aria-label="Pin supplier"
title="Pin"
> >
<Icon name="lucide:pin" size="14" class="text-white/80" /> <Icon name="lucide:pin" size="16" class="text-white" />
</button> </button>
</div> </div>
</div> </div>
@@ -242,11 +255,12 @@
@select="onHubSelect(hub)" @select="onHubSelect(hub)"
/> />
<button <button
class="absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity rounded-full bg-white/10 hover:bg-white/20 p-1" class="absolute -top-2 -right-2 rounded-full glass-bright border border-white/30 shadow-lg p-1.5 transition-transform hover:scale-105"
@click.stop="emit('pin', 'hub', hub)" @click.stop="emit('pin', 'hub', hub)"
aria-label="Pin hub" aria-label="Pin hub"
title="Pin"
> >
<Icon name="lucide:pin" size="14" class="text-white/80" /> <Icon name="lucide:pin" size="16" class="text-white" />
</button> </button>
</div> </div>
</div> </div>
@@ -274,11 +288,12 @@
@select="onHubSelect(hub)" @select="onHubSelect(hub)"
/> />
<button <button
class="absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity rounded-full bg-white/10 hover:bg-white/20 p-1" class="absolute -top-2 -right-2 rounded-full glass-bright border border-white/30 shadow-lg p-1.5 transition-transform hover:scale-105"
@click.stop="emit('pin', 'hub', hub)" @click.stop="emit('pin', 'hub', hub)"
aria-label="Pin hub" aria-label="Pin hub"
title="Pin"
> >
<Icon name="lucide:pin" size="14" class="text-white/80" /> <Icon name="lucide:pin" size="16" class="text-white" />
</button> </button>
</div> </div>
</div> </div>

View File

@@ -38,11 +38,12 @@
@select="onSelect(item)" @select="onSelect(item)"
/> />
<button <button
class="absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity rounded-full bg-white/10 hover:bg-white/20 p-1" class="absolute -top-2 -right-2 rounded-full glass-bright border border-white/30 shadow-lg p-1.5 transition-transform hover:scale-105"
@click.stop="emit('pin', 'product', item)" @click.stop="emit('pin', 'product', item)"
aria-label="Pin product" aria-label="Pin product"
title="Pin"
> >
<Icon name="lucide:pin" size="14" class="text-white/80" /> <Icon name="lucide:pin" size="16" class="text-white" />
</button> </button>
</div> </div>
</template> </template>
@@ -62,11 +63,12 @@
@select="onSelect(item)" @select="onSelect(item)"
/> />
<button <button
class="absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity rounded-full bg-white/10 hover:bg-white/20 p-1" class="absolute -top-2 -right-2 rounded-full glass-bright border border-white/30 shadow-lg p-1.5 transition-transform hover:scale-105"
@click.stop="emit('pin', 'hub', item)" @click.stop="emit('pin', 'hub', item)"
aria-label="Pin hub" aria-label="Pin hub"
title="Pin"
> >
<Icon name="lucide:pin" size="14" class="text-white/80" /> <Icon name="lucide:pin" size="16" class="text-white" />
</button> </button>
</div> </div>
</template> </template>
@@ -86,11 +88,12 @@
@select="onSelect(item)" @select="onSelect(item)"
/> />
<button <button
class="absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity rounded-full bg-white/10 hover:bg-white/20 p-1" class="absolute -top-2 -right-2 rounded-full glass-bright border border-white/30 shadow-lg p-1.5 transition-transform hover:scale-105"
@click.stop="emit('pin', 'supplier', item)" @click.stop="emit('pin', 'supplier', item)"
aria-label="Pin supplier" aria-label="Pin supplier"
title="Pin"
> >
<Icon name="lucide:pin" size="14" class="text-white/80" /> <Icon name="lucide:pin" size="16" class="text-white" />
</button> </button>
</div> </div>
</template> </template>

View File

@@ -94,7 +94,8 @@ export function useCatalogSearch() {
}) })
// Filter by bounds checkbox state from URL // Filter by bounds checkbox state from URL
const filterByBounds = computed(() => route.query.bounds !== undefined) // Use explicit flag so bounds don't auto-enable filtering.
const filterByBounds = computed(() => route.query.boundsFilter === '1')
// Get label for a filter (from cache or fallback to ID) // Get label for a filter (from cache or fallback to ID)
const getLabel = (type: string, id: string | undefined): string | null => { const getLabel = (type: string, id: string | undefined): string | null => {
@@ -261,7 +262,7 @@ export function useCatalogSearch() {
const setBoundsInUrl = (bounds: { west: number; south: number; east: number; north: number } | null) => { const setBoundsInUrl = (bounds: { west: number; south: number; east: number; north: number } | null) => {
if (bounds) { if (bounds) {
const boundsStr = `${bounds.west.toFixed(4)},${bounds.south.toFixed(4)},${bounds.east.toFixed(4)},${bounds.north.toFixed(4)}` const boundsStr = `${bounds.west.toFixed(4)},${bounds.south.toFixed(4)},${bounds.east.toFixed(4)},${bounds.north.toFixed(4)}`
updateQuery({ bounds: boundsStr }) updateQuery({ bounds: boundsStr, boundsFilter: '1' })
} else { } else {
updateQuery({ bounds: null }) updateQuery({ bounds: null })
} }
@@ -269,7 +270,12 @@ export function useCatalogSearch() {
// Clear bounds from URL // Clear bounds from URL
const clearBoundsFromUrl = () => { const clearBoundsFromUrl = () => {
updateQuery({ bounds: null }) updateQuery({ bounds: null, boundsFilter: null })
}
// Explicitly enable/disable bounds filter flag in URL
const setBoundsFilterEnabled = (enabled: boolean) => {
updateQuery({ boundsFilter: enabled ? '1' : null })
} }
const openInfo = (type: InfoEntityType, uuid: string) => { const openInfo = (type: InfoEntityType, uuid: string) => {
@@ -427,6 +433,7 @@ export function useCatalogSearch() {
setQuantity, setQuantity,
setBoundsInUrl, setBoundsInUrl,
clearBoundsFromUrl, clearBoundsFromUrl,
setBoundsFilterEnabled,
openInfo, openInfo,
closeInfo, closeInfo,
setInfoTab, setInfoTab,

View File

@@ -25,7 +25,7 @@
:cluster-supplier-uuid="clusterSupplierUuid" :cluster-supplier-uuid="clusterSupplierUuid"
@select="onMapSelect" @select="onMapSelect"
@bounds-change="onBoundsChange" @bounds-change="onBoundsChange"
@update:filter-by-bounds="$event ? setBoundsInUrl(currentMapBounds) : clearBoundsFromUrl()" @update:filter-by-bounds="onToggleBoundsFilter"
> >
<!-- Panel slot - shows selection list OR info OR quote results --> <!-- Panel slot - shows selection list OR info OR quote results -->
<template #panel> <template #panel>
@@ -175,7 +175,8 @@ const {
urlBounds, urlBounds,
filterByBounds, filterByBounds,
setBoundsInUrl, setBoundsInUrl,
clearBoundsFromUrl clearBoundsFromUrl,
setBoundsFilterEnabled
} = useCatalogSearch() } = useCatalogSearch()
// Info panel composable // Info panel composable
@@ -255,7 +256,20 @@ const getSelectionBounds = () => {
return { west: bounds.west, south: bounds.south, east: bounds.east, north: bounds.north } return { west: bounds.west, south: bounds.south, east: bounds.east, north: bounds.north }
} }
const onToggleBoundsFilter = (enabled: boolean) => {
if (enabled) {
setBoundsFilterEnabled(true)
const bounds = getSelectionBounds()
if (bounds) {
setBoundsInUrl(bounds)
}
} else {
clearBoundsFromUrl()
}
}
const applySelectionBounds = () => { const applySelectionBounds = () => {
if (!filterByBounds.value) return
if (!selectionBoundsBackup.value) { if (!selectionBoundsBackup.value) {
selectionBoundsBackup.value = { selectionBoundsBackup.value = {
hadBounds: !!urlBounds.value, hadBounds: !!urlBounds.value,
@@ -651,10 +665,18 @@ const onMapSelect = async (item: MapSelectItem) => {
setLabel(infoType, itemId, itemName) setLabel(infoType, itemId, itemName)
} }
// Handle selection from SelectionPanel - add to filter (show badge in search) // Handle selection from SelectionPanel - open info card (pin only via pin button)
const onSelectItem = (type: string, item: { uuid?: string | null; name?: string | null }) => { const onSelectItem = (type: string, item: { uuid?: string | null; name?: string | null }) => {
if (item.uuid && item.name) { if (!item.uuid) return
selectItem(type, item.uuid, item.name) if (type === 'hub' || type === 'supplier') {
if (item.name) {
setLabel(type, item.uuid, item.name)
}
openInfo(type, item.uuid)
return
}
if (type === 'product') {
router.push(localePath(`/catalog/products/${item.uuid}`))
} }
} }