All checks were successful
Build Docker Image / build (push) Successful in 3m10s
- MainNavigation: fixed min-height to prevent jumping on mode switch - CatalogMap: default pointColor changed from green to orange (#f97316) - CatalogPage: panel scroll on entire container, not inner - SelectionPanel: sticky header and search, removed inner scroll
144 lines
3.9 KiB
Vue
144 lines
3.9 KiB
Vue
<template>
|
|
<div class="flex flex-col gap-3">
|
|
<!-- Header (sticky at top) -->
|
|
<div class="flex items-center justify-between sticky top-0 bg-base-300/95 -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-300/95 -mx-4 px-4 pb-2 z-10">
|
|
<input
|
|
v-model="searchQuery"
|
|
type="text"
|
|
:placeholder="searchPlaceholder"
|
|
class="input input-bordered input-sm w-full"
|
|
/>
|
|
</div>
|
|
|
|
<!-- List -->
|
|
<div class="-mx-1 px-1">
|
|
<div v-if="loading" class="flex items-center justify-center py-8">
|
|
<span class="loading loading-spinner loading-md" />
|
|
</div>
|
|
|
|
<div v-else-if="filteredItems.length === 0" class="text-center py-8 text-base-content/60">
|
|
<Icon name="lucide:search-x" size="32" class="mb-2" />
|
|
<p>{{ $t('catalog.empty.noResults') }}</p>
|
|
</div>
|
|
|
|
<div v-else class="flex flex-col gap-2">
|
|
<!-- Products -->
|
|
<template v-if="selectMode === 'product'">
|
|
<ProductCard
|
|
v-for="item in filteredItems"
|
|
:key="item.uuid"
|
|
:product="item"
|
|
selectable
|
|
compact
|
|
:is-selected="selectedId === item.uuid"
|
|
@select="onSelect(item)"
|
|
/>
|
|
</template>
|
|
|
|
<!-- Hubs -->
|
|
<template v-else-if="selectMode === 'hub'">
|
|
<HubCard
|
|
v-for="item in filteredItems"
|
|
:key="item.uuid"
|
|
:hub="item"
|
|
selectable
|
|
:is-selected="selectedId === item.uuid"
|
|
@select="onSelect(item)"
|
|
/>
|
|
</template>
|
|
|
|
<!-- Suppliers -->
|
|
<template v-else-if="selectMode === 'supplier'">
|
|
<SupplierCard
|
|
v-for="item in filteredItems"
|
|
:key="item.uuid"
|
|
:supplier="item"
|
|
selectable
|
|
:is-selected="selectedId === item.uuid"
|
|
@select="onSelect(item)"
|
|
/>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import type { SelectMode } from '~/composables/useCatalogSearch'
|
|
|
|
interface Item {
|
|
uuid?: string | null
|
|
name?: string | null
|
|
[key: string]: any
|
|
}
|
|
|
|
const props = defineProps<{
|
|
selectMode: SelectMode
|
|
products?: Item[]
|
|
hubs?: Item[]
|
|
suppliers?: Item[]
|
|
selectedId?: string
|
|
loading?: boolean
|
|
}>()
|
|
|
|
const emit = defineEmits<{
|
|
'select': [type: string, item: Item]
|
|
'close': []
|
|
}>()
|
|
|
|
const { t } = useI18n()
|
|
|
|
const searchQuery = ref('')
|
|
|
|
const title = computed(() => {
|
|
switch (props.selectMode) {
|
|
case 'product': return t('catalog.headers.selectProduct')
|
|
case 'hub': return t('catalog.headers.selectHub')
|
|
case 'supplier': return t('catalog.headers.selectSupplier')
|
|
default: return ''
|
|
}
|
|
})
|
|
|
|
const searchPlaceholder = computed(() => {
|
|
switch (props.selectMode) {
|
|
case 'product': return t('catalog.search.searchProducts')
|
|
case 'hub': return t('catalog.search.searchHubs')
|
|
case 'supplier': return t('catalog.search.searchSuppliers')
|
|
default: return t('catalog.search.placeholder')
|
|
}
|
|
})
|
|
|
|
const items = computed(() => {
|
|
switch (props.selectMode) {
|
|
case 'product': return props.products || []
|
|
case 'hub': return props.hubs || []
|
|
case 'supplier': return props.suppliers || []
|
|
default: return []
|
|
}
|
|
})
|
|
|
|
const filteredItems = computed(() => {
|
|
if (!searchQuery.value.trim()) return items.value
|
|
|
|
const query = searchQuery.value.toLowerCase()
|
|
return items.value.filter(item =>
|
|
item.name?.toLowerCase().includes(query) ||
|
|
item.country?.toLowerCase().includes(query)
|
|
)
|
|
})
|
|
|
|
const onSelect = (item: Item) => {
|
|
if (props.selectMode && item.uuid) {
|
|
emit('select', props.selectMode, item)
|
|
}
|
|
}
|
|
</script>
|