feat(catalog): add search to all catalog pages
All checks were successful
Build Docker Image / build (push) Successful in 4m55s

Add CatalogSearchBar component with filtering to:
- /catalog/offers - search by product name
- /catalog/offers/[productId] - search by hub name/country
- /catalog/hubs/[id] - search by product name
- /catalog/suppliers/[supplierId] - search by product name
- /catalog/suppliers/[supplierId]/[productId] - search by hub name/country
This commit is contained in:
Ruslan Bakiev
2026-01-16 02:19:41 +07:00
parent 45ec9923e3
commit d9d05a4c21
5 changed files with 107 additions and 5 deletions

View File

@@ -1,12 +1,21 @@
<template>
<CatalogPage
:items="products"
:items="filteredProducts"
:map-items="mapItems"
:loading="isLoading"
:total-count="products.length"
with-map
map-id="hub-products-map"
point-color="#10b981"
>
<template #searchBar="{ displayedCount, totalCount }">
<CatalogSearchBar
v-model:search-query="searchQuery"
:displayed-count="displayedCount"
:total-count="totalCount"
/>
</template>
<template #header>
<!-- Not Found -->
<Card v-if="!isLoading && !hub" padding="lg">
@@ -70,6 +79,17 @@ const products = ref<Array<{ uuid: string; name: string }>>([])
const hubId = computed(() => route.params.id as string)
// Search
const searchQuery = ref('')
const filteredProducts = computed(() => {
if (!searchQuery.value.trim()) return products.value
const q = searchQuery.value.toLowerCase()
return products.value.filter(item =>
item.name?.toLowerCase().includes(q)
)
})
// Map items - show the hub itself
const mapItems = computed(() => {
if (!hub.value?.latitude || !hub.value?.longitude) return []

View File

@@ -1,7 +1,8 @@
<template>
<CatalogPage
:items="hubs"
:items="filteredHubs"
:loading="isLoading"
:total-count="hubs.length"
with-map
map-id="offers-product-hubs-map"
point-color="#10b981"
@@ -9,6 +10,14 @@
@update:hovered-id="hoveredHubId = $event"
@select="onSelectHub"
>
<template #searchBar="{ displayedCount, totalCount }">
<CatalogSearchBar
v-model:search-query="searchQuery"
:displayed-count="displayedCount"
:total-count="totalCount"
/>
</template>
<template #header>
<!-- Product Not Found -->
<Card v-if="!isLoading && !product" padding="lg">
@@ -86,6 +95,18 @@ const hoveredHubId = ref<string>()
const productId = computed(() => route.params.productId as string)
// Search
const searchQuery = ref('')
const filteredHubs = computed(() => {
if (!searchQuery.value.trim()) return hubs.value
const q = searchQuery.value.toLowerCase()
return hubs.value.filter(hub =>
hub.name?.toLowerCase().includes(q) ||
hub.country?.toLowerCase().includes(q)
)
})
// Handle hub selection
const onSelectHub = (hub: any) => {
navigateTo(localePath(`/catalog/offers/${productId.value}/${hub.uuid}`))

View File

@@ -1,11 +1,20 @@
<template>
<CatalogPage
:items="products"
:items="filteredProducts"
:loading="isLoading"
:total-count="products.length"
with-map
map-id="offers-products-map"
point-color="#3b82f6"
>
<template #searchBar="{ displayedCount, totalCount }">
<CatalogSearchBar
v-model:search-query="searchQuery"
:displayed-count="displayedCount"
:total-count="totalCount"
/>
</template>
<template #header>
<!-- Empty -->
<Card v-if="!isLoading && products.length === 0" padding="lg">
@@ -49,6 +58,17 @@ const { t } = useI18n()
const { items: products, isLoading, init } = useCatalogProducts()
// Search
const searchQuery = ref('')
const filteredProducts = computed(() => {
if (!searchQuery.value.trim()) return products.value
const q = searchQuery.value.toLowerCase()
return products.value.filter(item =>
item.name?.toLowerCase().includes(q)
)
})
// Navigate to product detail
const goToProduct = (productId: string) => {
navigateTo(localePath(`/catalog/offers/${productId}`))

View File

@@ -1,7 +1,8 @@
<template>
<CatalogPage
:items="hubs"
:items="filteredHubs"
:loading="isLoading"
:total-count="hubs.length"
with-map
map-id="supplier-product-hubs-map"
point-color="#10b981"
@@ -9,6 +10,14 @@
@update:hovered-id="hoveredHubId = $event"
@select="onSelectHub"
>
<template #searchBar="{ displayedCount, totalCount }">
<CatalogSearchBar
v-model:search-query="searchQuery"
:displayed-count="displayedCount"
:total-count="totalCount"
/>
</template>
<template #header>
<!-- Not Found -->
<Card v-if="!isLoading && (!supplier || !product)" padding="lg">
@@ -109,6 +118,18 @@ const hoveredHubId = ref<string>()
const supplierId = computed(() => routeRef.params.supplierId as string)
const productId = computed(() => routeRef.params.productId as string)
// Search
const searchQuery = ref('')
const filteredHubs = computed(() => {
if (!searchQuery.value.trim()) return hubs.value
const q = searchQuery.value.toLowerCase()
return hubs.value.filter(hub =>
hub.name?.toLowerCase().includes(q) ||
hub.country?.toLowerCase().includes(q)
)
})
// Handle hub selection
const onSelectHub = (hub: any) => {
navigateTo(localePath(`/catalog/suppliers/${supplierId.value}/${productId.value}/${hub.uuid}`))

View File

@@ -1,12 +1,21 @@
<template>
<CatalogPage
:items="products"
:items="filteredProducts"
:map-items="mapItems"
:loading="isLoading"
:total-count="products.length"
with-map
map-id="supplier-products-map"
point-color="#3b82f6"
>
<template #searchBar="{ displayedCount, totalCount }">
<CatalogSearchBar
v-model:search-query="searchQuery"
:displayed-count="displayedCount"
:total-count="totalCount"
/>
</template>
<template #header>
<!-- Supplier Not Found -->
<Card v-if="!isLoading && !supplier" padding="lg">
@@ -91,6 +100,17 @@ const offers = ref<any[]>([])
const supplierId = computed(() => route.params.supplierId as string)
// Search
const searchQuery = ref('')
const filteredProducts = computed(() => {
if (!searchQuery.value.trim()) return products.value
const q = searchQuery.value.toLowerCase()
return products.value.filter(item =>
item.name?.toLowerCase().includes(q)
)
})
// Map items - show supplier location
const mapItems = computed(() => {
if (!supplier.value?.latitude || !supplier.value?.longitude) return []