From 08d7e0ade99e40ef62633972d1db3e4483fe1875 Mon Sep 17 00:00:00 2001 From: Ruslan Bakiev <572431+veikab@users.noreply.github.com> Date: Thu, 22 Jan 2026 10:57:30 +0700 Subject: [PATCH] Implement unified catalog search with token-based filtering - 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 --- app/components/GoodsContent.vue | 4 +- app/components/Sidebar.vue | 36 +- app/components/catalog/CatalogBreadcrumbs.vue | 2 +- app/components/catalog/CatalogGridHubs.vue | 58 +++ .../catalog/CatalogGridHubsForProduct.vue | 99 ++++ app/components/catalog/CatalogGridOffers.vue | 124 +++++ .../catalog/CatalogGridProducts.vue | 46 ++ .../CatalogGridProductsFromSupplier.vue | 95 ++++ .../catalog/CatalogGridProductsInHub.vue | 93 ++++ .../catalog/CatalogGridSuppliers.vue | 57 +++ app/components/catalog/CatalogHero.vue | 42 ++ app/components/catalog/CatalogHubsSection.vue | 2 +- .../catalog/CatalogOffersSection.vue | 2 +- .../catalog/CatalogSuppliersSection.vue | 2 +- app/components/catalog/OffersBreadcrumbs.vue | 6 +- app/components/catalog/SupplierCard.vue | 2 +- .../catalog/SuppliersBreadcrumbs.vue | 6 +- app/components/navigation/MainNavigation.vue | 2 +- app/components/navigation/SubNavigation.vue | 6 +- app/components/search/GlobalSearchBar.vue | 12 +- app/components/search/UnifiedSearchBar.vue | 133 ++++++ app/composables/useCatalogSearch.ts | 233 +++++++++ app/layouts/topnav.vue | 24 +- app/pages/catalog/hubs/[id]/[productId].vue | 309 ------------ app/pages/catalog/hubs/[id]/index.vue | 6 +- app/pages/catalog/hubs/index.vue | 184 ------- app/pages/catalog/index.vue | 145 ++++++ .../catalog/offers/[productId]/[hubId].vue | 356 -------------- .../catalog/offers/[productId]/index.vue | 158 ------ app/pages/catalog/offers/index.vue | 85 ---- app/pages/catalog/products/[id].vue | 2 +- .../[supplierId]/[productId]/[hubId].vue | 450 ------------------ .../[supplierId]/[productId]/index.vue | 257 ---------- .../catalog/suppliers/[supplierId]/index.vue | 215 --------- app/pages/catalog/suppliers/index.vue | 124 ----- app/pages/select-location/index.vue | 7 +- app/pages/select-location/map.vue | 7 +- i18n/locales/en/catalog.json | 36 ++ i18n/locales/ru/catalog.json | 36 ++ 39 files changed, 1278 insertions(+), 2185 deletions(-) create mode 100644 app/components/catalog/CatalogGridHubs.vue create mode 100644 app/components/catalog/CatalogGridHubsForProduct.vue create mode 100644 app/components/catalog/CatalogGridOffers.vue create mode 100644 app/components/catalog/CatalogGridProducts.vue create mode 100644 app/components/catalog/CatalogGridProductsFromSupplier.vue create mode 100644 app/components/catalog/CatalogGridProductsInHub.vue create mode 100644 app/components/catalog/CatalogGridSuppliers.vue create mode 100644 app/components/catalog/CatalogHero.vue create mode 100644 app/components/search/UnifiedSearchBar.vue create mode 100644 app/composables/useCatalogSearch.ts delete mode 100644 app/pages/catalog/hubs/[id]/[productId].vue delete mode 100644 app/pages/catalog/hubs/index.vue create mode 100644 app/pages/catalog/index.vue delete mode 100644 app/pages/catalog/offers/[productId]/[hubId].vue delete mode 100644 app/pages/catalog/offers/[productId]/index.vue delete mode 100644 app/pages/catalog/offers/index.vue delete mode 100644 app/pages/catalog/suppliers/[supplierId]/[productId]/[hubId].vue delete mode 100644 app/pages/catalog/suppliers/[supplierId]/[productId]/index.vue delete mode 100644 app/pages/catalog/suppliers/[supplierId]/index.vue delete mode 100644 app/pages/catalog/suppliers/index.vue create mode 100644 i18n/locales/en/catalog.json create mode 100644 i18n/locales/ru/catalog.json diff --git a/app/components/GoodsContent.vue b/app/components/GoodsContent.vue index 7abeeba..a770e05 100644 --- a/app/components/GoodsContent.vue +++ b/app/components/GoodsContent.vue @@ -54,8 +54,8 @@ const selectProduct = (product: any) => { if (locationUuid) { // Both product and hub selected -> show offers navigateTo({ - path: `/catalog/offers/${product.uuid}/${locationUuid}`, - query + path: `/catalog`, + query: { product: product.uuid, hub: locationUuid, ...query } }) return } diff --git a/app/components/Sidebar.vue b/app/components/Sidebar.vue index 5990a07..0506e62 100644 --- a/app/components/Sidebar.vue +++ b/app/components/Sidebar.vue @@ -38,8 +38,8 @@
  • @@ -48,8 +48,8 @@
  • @@ -58,8 +58,8 @@
  • @@ -169,19 +169,19 @@
  • - + {{ t('nav.offers') }}
  • - + {{ t('nav.suppliers') }}
  • - + {{ t('nav.hubs') }} @@ -379,6 +379,24 @@ const isActive = (path: string) => { return current.startsWith(localePath(path) + '/') } +// Check if catalog section is active based on query params +const isCatalogActive = (type: 'product' | 'supplier' | 'hub') => { + const catalogPath = localePath('/catalog') + if (!route.path.startsWith(catalogPath)) return false + + const { select, product, supplier, hub } = route.query + + // If we're in selection mode for this type + if (select === type) return true + + // If this type has been selected (in the flow) + if (type === 'product' && (product || select === 'product')) return true + if (type === 'supplier' && supplier) return true + if (type === 'hub' && hub) return true + + return false +} + const isExactActive = (path: string) => { return route.path === localePath(path) } diff --git a/app/components/catalog/CatalogBreadcrumbs.vue b/app/components/catalog/CatalogBreadcrumbs.vue index 3cc2459..b8f5a19 100644 --- a/app/components/catalog/CatalogBreadcrumbs.vue +++ b/app/components/catalog/CatalogBreadcrumbs.vue @@ -28,7 +28,7 @@ const breadcrumbs = computed(() => { // Hubs list crumbs.push({ label: t('breadcrumbs.hubs', 'Hubs'), - to: localePath('/catalog/hubs') + to: localePath('/catalog?select=hub') }) // Hub diff --git a/app/components/catalog/CatalogGridHubs.vue b/app/components/catalog/CatalogGridHubs.vue new file mode 100644 index 0000000..a8d214f --- /dev/null +++ b/app/components/catalog/CatalogGridHubs.vue @@ -0,0 +1,58 @@ + + + diff --git a/app/components/catalog/CatalogGridHubsForProduct.vue b/app/components/catalog/CatalogGridHubsForProduct.vue new file mode 100644 index 0000000..948c024 --- /dev/null +++ b/app/components/catalog/CatalogGridHubsForProduct.vue @@ -0,0 +1,99 @@ + + + diff --git a/app/components/catalog/CatalogGridOffers.vue b/app/components/catalog/CatalogGridOffers.vue new file mode 100644 index 0000000..fff654c --- /dev/null +++ b/app/components/catalog/CatalogGridOffers.vue @@ -0,0 +1,124 @@ + + + diff --git a/app/components/catalog/CatalogGridProducts.vue b/app/components/catalog/CatalogGridProducts.vue new file mode 100644 index 0000000..2e4f29f --- /dev/null +++ b/app/components/catalog/CatalogGridProducts.vue @@ -0,0 +1,46 @@ + + + diff --git a/app/components/catalog/CatalogGridProductsFromSupplier.vue b/app/components/catalog/CatalogGridProductsFromSupplier.vue new file mode 100644 index 0000000..0b89d1e --- /dev/null +++ b/app/components/catalog/CatalogGridProductsFromSupplier.vue @@ -0,0 +1,95 @@ + + + diff --git a/app/components/catalog/CatalogGridProductsInHub.vue b/app/components/catalog/CatalogGridProductsInHub.vue new file mode 100644 index 0000000..e55924e --- /dev/null +++ b/app/components/catalog/CatalogGridProductsInHub.vue @@ -0,0 +1,93 @@ + + + diff --git a/app/components/catalog/CatalogGridSuppliers.vue b/app/components/catalog/CatalogGridSuppliers.vue new file mode 100644 index 0000000..0402949 --- /dev/null +++ b/app/components/catalog/CatalogGridSuppliers.vue @@ -0,0 +1,57 @@ + + + diff --git a/app/components/catalog/CatalogHero.vue b/app/components/catalog/CatalogHero.vue new file mode 100644 index 0000000..1d2ada2 --- /dev/null +++ b/app/components/catalog/CatalogHero.vue @@ -0,0 +1,42 @@ + + + diff --git a/app/components/catalog/CatalogHubsSection.vue b/app/components/catalog/CatalogHubsSection.vue index 76cb76f..b48ebc0 100644 --- a/app/components/catalog/CatalogHubsSection.vue +++ b/app/components/catalog/CatalogHubsSection.vue @@ -4,7 +4,7 @@ {{ t('catalogHubsSection.header.title') }} {{ t('catalogHubsSection.actions.view_all') }} diff --git a/app/components/catalog/CatalogOffersSection.vue b/app/components/catalog/CatalogOffersSection.vue index 4b43f22..5dfc5d2 100644 --- a/app/components/catalog/CatalogOffersSection.vue +++ b/app/components/catalog/CatalogOffersSection.vue @@ -4,7 +4,7 @@ {{ t('catalogOffersSection.header.title') }} {{ t('catalogOffersSection.actions.view_all') }} diff --git a/app/components/catalog/CatalogSuppliersSection.vue b/app/components/catalog/CatalogSuppliersSection.vue index c8c012c..a0b40d8 100644 --- a/app/components/catalog/CatalogSuppliersSection.vue +++ b/app/components/catalog/CatalogSuppliersSection.vue @@ -4,7 +4,7 @@ {{ t('catalogSuppliersSection.header.title') }} {{ t('catalogSuppliersSection.actions.view_all') }} diff --git a/app/components/catalog/OffersBreadcrumbs.vue b/app/components/catalog/OffersBreadcrumbs.vue index 73f373c..6517945 100644 --- a/app/components/catalog/OffersBreadcrumbs.vue +++ b/app/components/catalog/OffersBreadcrumbs.vue @@ -25,17 +25,17 @@ const { t } = useI18n() const breadcrumbs = computed(() => { const crumbs: Array<{ label: string; to?: string }> = [] - // Products list + // Catalog root crumbs.push({ label: t('breadcrumbs.products', 'Products'), - to: localePath('/catalog/offers') + to: localePath('/catalog?select=product') }) // Product if (props.productId) { crumbs.push({ label: props.productName || `#${props.productId.slice(0, 8)}...`, - to: props.hubId ? localePath(`/catalog/offers/${props.productId}`) : undefined + to: props.hubId ? localePath(`/catalog?product=${props.productId}`) : undefined }) } diff --git a/app/components/catalog/SupplierCard.vue b/app/components/catalog/SupplierCard.vue index 7d6c6d0..d0462d3 100644 --- a/app/components/catalog/SupplierCard.vue +++ b/app/components/catalog/SupplierCard.vue @@ -1,7 +1,7 @@