From 86de2cc45bf52e04b775d3e23b4be99296d73875 Mon Sep 17 00:00:00 2001 From: sdarbinyan Date: Thu, 9 Jul 2026 02:57:48 +0400 Subject: [PATCH] fix(catalog): filter UX and tablet layout --- src/app/facades/platform/search.facade.ts | 71 +++++++++++++++---- .../filters-panel.component.html | 2 +- .../filters-panel/filters-panel.component.ts | 18 +++++ .../catalog-container.component.scss | 7 ++ .../containers/catalog-container.component.ts | 20 ++++-- src/app/i18n/en.ts | 2 + src/app/i18n/hy.ts | 2 + src/app/i18n/ru.ts | 2 + src/app/i18n/translations.ts | 2 + 9 files changed, 108 insertions(+), 18 deletions(-) diff --git a/src/app/facades/platform/search.facade.ts b/src/app/facades/platform/search.facade.ts index 8dce487..e495aca 100644 --- a/src/app/facades/platform/search.facade.ts +++ b/src/app/facades/platform/search.facade.ts @@ -83,6 +83,36 @@ export class SearchFacade { const categories = this.collectUnique(products.map(product => String(product.categoryID)).filter(Boolean)); const subcategories = this.collectUnique(products.map(product => product.subcategoryId ?? '').filter(Boolean)); const attributes = this.collectUnique(products.flatMap(product => (product.descriptionFields ?? []).map(field => field.key))); + const availabilityCounts = products.reduce>((acc, product) => { + const key = product.remainings === 'out' + ? 'out-of-stock' + : product.remainings === 'low' + ? 'low-stock' + : 'in-stock'; + acc[key] = (acc[key] ?? 0) + 1; + return acc; + }, {}); + const availabilityBase = [ + { id: 'in-stock', label: this.translate.t('catalog.filterInStock'), value: 'in-stock', availability: 'in-stock' as const }, + { id: 'low-stock', label: this.translate.t('catalog.filterLowStock'), value: 'low-stock', availability: 'low-stock' as const }, + { id: 'out-of-stock', label: this.translate.t('catalog.filterOutOfStock'), value: 'out-of-stock', availability: 'out-of-stock' as const }, + ]; + const availabilityOptions = availabilityBase + .filter(option => (availabilityCounts[option.value] ?? 0) > 0) + .map(option => ({ ...option, count: availabilityCounts[option.value] })); + const ratingOptions = this.collectUnique( + products + .map(product => String(Math.max(1, Math.min(5, Math.floor(product.rating || 0))))) + .filter(value => Number(value) > 0) + ) + .map(value => Number(value)) + .sort((a, b) => b - a) + .map(value => ({ + id: `rating-${value}`, + label: this.translate.t('catalog.filterStars', { count: value }), + value: String(value), + rating: value, + })); const groups: FilterGroup[] = [ { @@ -99,23 +129,14 @@ export class SearchFacade { id: 'availability', label: this.translate.t('catalog.filterAvailability'), type: 'availability', - options: [ - { id: 'in-stock', label: this.translate.t('catalog.filterInStock'), value: 'in-stock', availability: 'in-stock' }, - { id: 'low-stock', label: this.translate.t('catalog.filterLowStock'), value: 'low-stock', availability: 'low-stock' }, - { id: 'out-of-stock', label: this.translate.t('catalog.filterOutOfStock'), value: 'out-of-stock', availability: 'out-of-stock' }, - ], + options: availabilityOptions, enabled: enabled.has('availability'), }, { id: 'rating', label: this.translate.t('catalog.filterRating'), type: 'rating', - options: [5, 4, 3, 2, 1].map(value => ({ - id: `rating-${value}`, - label: this.translate.t('catalog.filterStars', { count: value }), - value: String(value), - rating: value, - })), + options: ratingOptions, enabled: enabled.has('rating'), }, { @@ -403,7 +424,12 @@ export class SearchFacade { } private toColorHex(value: string): string { - const normalized = value.toLowerCase(); + const normalized = value.trim().toLowerCase(); + + if (/^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(normalized)) { + return normalized; + } + const palette: Record = { black: '#111111', white: '#ffffff', @@ -418,8 +444,27 @@ export class SearchFacade { grey: '#6b7280', brown: '#92400e', beige: '#d6c6a8', + navy: '#1e3a8a', + cyan: '#06b6d4', + teal: '#0d9488', + lime: '#65a30d', + olive: '#4d7c0f', + maroon: '#7f1d1d', + silver: '#94a3b8', + gold: '#eab308', }; - return palette[normalized] ?? '#94a3b8'; + if (palette[normalized]) { + return palette[normalized]; + } + + const tokens = normalized.split(/[\s,\-/]+/).filter(Boolean); + for (const token of tokens) { + if (palette[token]) { + return palette[token]; + } + } + + return '#94a3b8'; } } diff --git a/src/app/features/website/catalog/components/filters-panel/filters-panel.component.html b/src/app/features/website/catalog/components/filters-panel/filters-panel.component.html index 8eaf843..3e654d1 100644 --- a/src/app/features/website/catalog/components/filters-panel/filters-panel.component.html +++ b/src/app/features/website/catalog/components/filters-panel/filters-panel.component.html @@ -4,7 +4,7 @@ @for (filter of definitions; track filter.id) { - @if (filter.enabled) { + @if (shouldRenderFilter(filter)) {