Implement unified catalog search with token-based filtering
All checks were successful
Build Docker Image / build (push) Successful in 3m23s
All checks were successful
Build Docker Image / build (push) Successful in 3m23s
- Add useCatalogSearch composable for managing unified search state - Add UnifiedSearchBar component with token chips for filters - Add CatalogHero component for empty/landing state - Create grid components for each display mode: - CatalogGridProducts, CatalogGridSuppliers, CatalogGridHubs - CatalogGridHubsForProduct, CatalogGridProductsFromSupplier - CatalogGridProductsInHub, CatalogGridOffers - Add unified catalog page at /catalog with query params - Remove SubNavigation from catalog section (kept for other sections) - Update all links to use new unified catalog paths - Delete old nested catalog pages (offers/suppliers/hubs flows) - Add i18n translations for catalog section
This commit is contained in:
145
app/pages/catalog/index.vue
Normal file
145
app/pages/catalog/index.vue
Normal file
@@ -0,0 +1,145 @@
|
||||
<template>
|
||||
<div class="flex flex-col h-full">
|
||||
<!-- Unified Search Bar -->
|
||||
<div class="sticky top-0 z-20 px-3 lg:px-6 py-3 bg-base-300">
|
||||
<UnifiedSearchBar
|
||||
:active-tokens="activeTokens"
|
||||
:available-chips="availableChips"
|
||||
:select-mode="selectMode"
|
||||
:search-query="searchQuery"
|
||||
@start-select="startSelect"
|
||||
@cancel-select="cancelSelect"
|
||||
@edit-token="editFilter"
|
||||
@remove-token="removeFilter"
|
||||
@update:search-query="searchQuery = $event"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Dynamic Content -->
|
||||
<div class="flex-1 px-3 lg:px-6 py-4">
|
||||
<!-- Hero (empty state) -->
|
||||
<template v-if="displayMode === 'hero'">
|
||||
<CatalogHero @start-select="startSelect" />
|
||||
</template>
|
||||
|
||||
<!-- Products Grid -->
|
||||
<template v-else-if="displayMode === 'grid-products'">
|
||||
<CatalogGridProducts
|
||||
:search-query="searchQuery"
|
||||
@select="onSelectProduct"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- Suppliers Grid -->
|
||||
<template v-else-if="displayMode === 'grid-suppliers'">
|
||||
<CatalogGridSuppliers
|
||||
:search-query="searchQuery"
|
||||
@select="onSelectSupplier"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- Hubs Grid -->
|
||||
<template v-else-if="displayMode === 'grid-hubs'">
|
||||
<CatalogGridHubs
|
||||
:search-query="searchQuery"
|
||||
@select="onSelectHub"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- Hubs for selected product -->
|
||||
<template v-else-if="displayMode === 'grid-hubs-for-product'">
|
||||
<CatalogGridHubsForProduct
|
||||
:product-id="productId!"
|
||||
:search-query="searchQuery"
|
||||
@select="onSelectHub"
|
||||
@product-loaded="setLabel('product', productId!, $event)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- Products from supplier -->
|
||||
<template v-else-if="displayMode === 'grid-products-from-supplier'">
|
||||
<CatalogGridProductsFromSupplier
|
||||
:supplier-id="supplierId!"
|
||||
:search-query="searchQuery"
|
||||
@select="onSelectProduct"
|
||||
@supplier-loaded="setLabel('supplier', supplierId!, $event)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- Products in hub -->
|
||||
<template v-else-if="displayMode === 'grid-products-in-hub'">
|
||||
<CatalogGridProductsInHub
|
||||
:hub-id="hubId!"
|
||||
:search-query="searchQuery"
|
||||
@select="onSelectProduct"
|
||||
@hub-loaded="setLabel('hub', hubId!, $event)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- Offers -->
|
||||
<template v-else-if="displayMode === 'grid-offers'">
|
||||
<CatalogGridOffers
|
||||
:product-id="productId"
|
||||
:supplier-id="supplierId"
|
||||
:hub-id="hubId"
|
||||
:search-query="searchQuery"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
layout: 'topnav'
|
||||
})
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const {
|
||||
selectMode,
|
||||
displayMode,
|
||||
productId,
|
||||
supplierId,
|
||||
hubId,
|
||||
searchQuery,
|
||||
activeTokens,
|
||||
availableChips,
|
||||
startSelect,
|
||||
cancelSelect,
|
||||
selectItem,
|
||||
removeFilter,
|
||||
editFilter,
|
||||
setLabel
|
||||
} = useCatalogSearch()
|
||||
|
||||
// Selection handlers
|
||||
const onSelectProduct = (product: { uuid: string; name: string }) => {
|
||||
selectItem('product', product.uuid, product.name)
|
||||
}
|
||||
|
||||
const onSelectSupplier = (supplier: { uuid: string; name: string }) => {
|
||||
selectItem('supplier', supplier.uuid, supplier.name)
|
||||
}
|
||||
|
||||
const onSelectHub = (hub: { uuid: string; name: string }) => {
|
||||
selectItem('hub', hub.uuid, hub.name)
|
||||
}
|
||||
|
||||
// SEO
|
||||
useHead(() => {
|
||||
let title = t('catalog.hero.title')
|
||||
|
||||
if (displayMode.value === 'grid-products') {
|
||||
title = t('catalog.headers.selectProduct')
|
||||
} else if (displayMode.value === 'grid-suppliers') {
|
||||
title = t('catalog.headers.selectSupplier')
|
||||
} else if (displayMode.value === 'grid-hubs') {
|
||||
title = t('catalog.headers.selectHub')
|
||||
} else if (displayMode.value === 'grid-offers') {
|
||||
title = t('catalog.headers.offers')
|
||||
}
|
||||
|
||||
return { title }
|
||||
})
|
||||
</script>
|
||||
Reference in New Issue
Block a user