Fix catalog UI issues
All checks were successful
Build Docker Image / build (push) Successful in 3m31s

1. Fix navbar height - prevent tag wrapping with overflow-hidden
2. Fix translation keys for mode labels and search form labels
3. Fix SelectionPanel - white glass header/search, no top gap
4. Map click fills active selector - emit full properties from map
This commit is contained in:
Ruslan Bakiev
2026-01-24 09:47:41 +07:00
parent 3140226bc3
commit 2a607d0d2d
5 changed files with 68 additions and 29 deletions

View File

@@ -62,7 +62,7 @@ const props = withDefaults(defineProps<{
})
const emit = defineEmits<{
'select-item': [uuid: string]
'select-item': [uuid: string, properties?: Record<string, any>]
'bounds-change': [bounds: MapBounds]
}>()
@@ -428,11 +428,12 @@ const initServerClusteringLayers = async (map: MapboxMapType) => {
})
})
// Click on individual point
// Click on individual point - emit full properties
map.on('click', 'server-points', (e) => {
const features = map.queryRenderedFeatures(e.point, { layers: ['server-points'] })
if (!features.length) return
emit('select-item', features[0].properties?.id)
const props = features[0].properties || {}
emit('select-item', props.id, props)
})
map.on('mouseenter', 'server-clusters', () => { map.getCanvas().style.cursor = 'pointer' })

View File

@@ -1,25 +1,23 @@
<template>
<div class="flex flex-col gap-3">
<!-- Header (sticky at top) -->
<div class="flex items-center justify-between sticky top-0 bg-base-100/80 backdrop-blur-sm -mx-4 px-4 py-2 -mt-4 z-10">
<h3 class="font-semibold text-lg">{{ title }}</h3>
<button class="btn btn-ghost btn-sm btn-circle" @click="emit('close')">
<Icon name="lucide:x" size="18" />
</button>
</div>
<!-- Search input (sticky below header) -->
<div class="sticky top-10 bg-base-100/80 backdrop-blur-sm -mx-4 px-4 pb-2 z-10">
<div class="flex flex-col h-full">
<!-- Header + Search (white glass, sticky) -->
<div class="sticky top-0 z-10 -m-4 mb-0 p-3 rounded-t-xl bg-white/90 backdrop-blur-md border-b border-white/20">
<div class="flex items-center justify-between mb-2">
<h3 class="font-semibold text-base text-base-content">{{ title }}</h3>
<button class="btn btn-ghost btn-xs btn-circle text-base-content/60 hover:text-base-content" @click="emit('close')">
<Icon name="lucide:x" size="16" />
</button>
</div>
<input
v-model="searchQuery"
type="text"
:placeholder="searchPlaceholder"
class="input input-bordered input-sm w-full"
class="input input-sm w-full bg-white/50 border-base-300/50 text-base-content placeholder:text-base-content/50"
/>
</div>
<!-- List -->
<div class="-mx-1 px-1">
<div class="flex-1 pt-3">
<div v-if="loading" class="flex items-center justify-center py-8">
<span class="loading loading-spinner loading-md" />
</div>

View File

@@ -20,7 +20,7 @@
: (glassStyle ? 'text-white/70 hover:text-white hover:bg-white/10' : 'text-base-content/70 hover:text-base-content hover:bg-base-200')"
@click="$emit('set-catalog-mode', 'explore')"
>
TradeScanner
{{ $t('catalog.modes.explore') }}
</button>
<button
class="px-3 py-1 text-sm font-medium rounded-lg transition-colors"
@@ -29,7 +29,7 @@
: (glassStyle ? 'text-white/70 hover:text-white hover:bg-white/10' : 'text-base-content/70 hover:text-base-content hover:bg-base-200')"
@click="$emit('set-catalog-mode', 'quote')"
>
Search
{{ $t('catalog.modes.quote') }}
</button>
</nav>
</div>
@@ -44,7 +44,7 @@
class="flex-1 px-4 py-2 text-left hover:bg-base-200/50 rounded-l-full transition-colors min-w-0"
@click="$emit('edit-token', 'product')"
>
<div class="text-xs text-base-content/60">{{ $t('catalog.quote.product') }}</div>
<div class="text-xs text-base-content/60">{{ $t('catalog.filters.product') }}</div>
<div class="font-medium truncate text-base-content">{{ productLabel || $t('catalog.quote.selectProduct') }}</div>
</button>
<!-- Hub segment -->
@@ -52,7 +52,7 @@
class="flex-1 px-4 py-2 text-left hover:bg-base-200/50 transition-colors min-w-0"
@click="$emit('edit-token', 'hub')"
>
<div class="text-xs text-base-content/60">{{ $t('catalog.quote.hub') }}</div>
<div class="text-xs text-base-content/60">{{ $t('catalog.filters.hub') }}</div>
<div class="font-medium truncate text-base-content">{{ hubLabel || $t('catalog.quote.selectHub') }}</div>
</button>
<!-- Quantity segment -->
@@ -60,7 +60,7 @@
class="flex-1 px-4 py-2 text-left hover:bg-base-200/50 transition-colors min-w-0"
@click="$emit('edit-token', 'quantity')"
>
<div class="text-xs text-base-content/60">{{ $t('catalog.quote.quantity') }}</div>
<div class="text-xs text-base-content/60">{{ $t('catalog.filters.quantity') }}</div>
<div class="font-medium text-base-content">{{ quantity ? `${quantity} ${$t('units.t')}` : '—' }}</div>
</button>
<!-- Search button inside -->
@@ -83,8 +83,8 @@
>
<Icon name="lucide:search" size="22" class="text-primary flex-shrink-0" />
<!-- Tokens + input inline -->
<div class="flex items-center gap-2 flex-wrap flex-1 min-w-0">
<!-- Tokens + input inline (no wrap to prevent height change) -->
<div class="flex items-center gap-2 flex-1 min-w-0 overflow-hidden">
<!-- Active filter tokens (outline style with icon in circle) -->
<div
v-for="token in activeTokens"

View File

@@ -265,16 +265,20 @@ const onBoundsChange = (bounds: MapBounds) => {
}
// Handle selection from map
const onMapSelect = (uuid: string) => {
const onMapSelect = (uuid: string, properties?: Record<string, any>) => {
const item = props.items.find(i => i.uuid === uuid)
if (item) {
selectedMapItem.value = item
emit('select', item)
} else if (props.useServerClustering) {
// For server clustering, item might not be in props.items
// Create minimal item with just uuid
selectedMapItem.value = { uuid }
emit('select', { uuid })
// For server clustering, include properties from cluster data
const mapItem: MapItem = {
uuid,
name: properties?.name,
...properties
}
selectedMapItem.value = mapItem
emit('select', mapItem)
}
}

View File

@@ -129,7 +129,43 @@ const mapPointColor = computed(() => {
// Handle map item selection
const onMapSelect = (item: any) => {
// Navigate to offer detail page if in quote mode with results
// If in selection mode, use map click to fill the selector
if (selectMode.value && item.uuid) {
const itemName = item.name || item.uuid.slice(0, 8) + '...'
// For hubs selection - click on hub fills hub selector
if (selectMode.value === 'hub' && mapViewMode.value === 'hubs') {
selectItem('hub', item.uuid, itemName)
showQuoteResults.value = false
offers.value = []
return
}
// For supplier selection - click on supplier fills supplier selector
if (selectMode.value === 'supplier' && mapViewMode.value === 'suppliers') {
selectItem('supplier', item.uuid, itemName)
showQuoteResults.value = false
offers.value = []
return
}
// For product selection viewing offers - try to find product in loaded list
if (selectMode.value === 'product' && mapViewMode.value === 'offers') {
// The offer has product info - we'll look it up from the products list if loaded
// or just use the offer's product_uuid if available
const product = products.value.find((p: any) => p.uuid === item.productUuid)
if (product) {
selectItem('product', product.uuid, product.name || itemName)
} else if (item.productUuid) {
selectItem('product', item.productUuid, item.productName || itemName)
}
showQuoteResults.value = false
offers.value = []
return
}
}
// Default behavior for quote mode etc
if (catalogMode.value === 'quote' && item.uuid) {
console.log('Selected from map:', item)
}