fix(search): drop unresolvable category filter options instead of faking a label
P0-4: the Catalog/Search category facet showed placeholder labels
like 'Категория 2008', 'Категория 22612' for every option — traced
to search.facade.ts buildFilterGroups() synthesizing 'Category {id}'
for every distinct product.categoryID with no attempt to resolve a
real name.
Investigated further: the mismatch isn't a missing-lookup bug, it's
a real data gap. Product categoryID values in the mock catalog
fixture don't correspond to any id in CategoryFacade's category
tree (a much smaller, separately-curated mock dataset) — there is
no real category name to show for these ids today.
Wired CategoryFacade into SearchFacade and resolve each option's
real title when the id does match; when it doesn't (the common case
with current mock data), the option is dropped rather than showing
a fabricated technical-looking label. The category filter group
simply doesn't render when nothing resolves, which is honest given
the data, instead of looking like broken/unseeded content in front
of a client.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@ import { Observable, Subject, of } from 'rxjs';
|
||||
import { debounceTime, distinctUntilChanged, map, switchMap, tap } from 'rxjs/operators';
|
||||
import { Category } from '../../../core/categories/models/category-domain.model';
|
||||
import { Product, ProductListResult } from '../../../core/products/models/product-domain.model';
|
||||
import { CategoryFacade } from '../../../facades/platform/category.facade';
|
||||
import { ProductFacade } from '../../../facades/platform/product.facade';
|
||||
import { TranslateService } from '../../../i18n/translate.service';
|
||||
import { SearchAutocompleteService } from '../services/search-autocomplete.service';
|
||||
@@ -34,6 +35,7 @@ interface LegacySearchState {
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class SearchFacade {
|
||||
private readonly productFacade = inject(ProductFacade);
|
||||
private readonly categoryFacade = inject(CategoryFacade);
|
||||
private readonly translate = inject(TranslateService);
|
||||
private readonly autocompleteService = inject(SearchAutocompleteService);
|
||||
private readonly historyService = inject(SearchHistoryService);
|
||||
@@ -278,7 +280,9 @@ export class SearchFacade {
|
||||
id: 'category',
|
||||
label: this.translate.t('catalog.filterCategory'),
|
||||
type: 'radio',
|
||||
options: categories.map(value => ({ id: `cat-${value}`, label: this.translate.t('catalog.filterCategoryValue', { value }), value })),
|
||||
options: categories
|
||||
.map(value => ({ id: `cat-${value}`, label: this.resolveCategoryLabel(value), value }))
|
||||
.filter((option): option is { id: string; label: string; value: string } => option.label !== null),
|
||||
enabled: enabled.has('category'),
|
||||
},
|
||||
{
|
||||
@@ -545,6 +549,19 @@ export class SearchFacade {
|
||||
return [...new Set(values.map(value => value.trim()).filter(Boolean))];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the real category title, or null when the id has no match in the loaded
|
||||
* category tree — callers drop unresolved options instead of showing a fabricated
|
||||
* "Category {id}" label (see RELEASE-CANDIDATE-AUDIT.md P0-4).
|
||||
*/
|
||||
private resolveCategoryLabel(categoryId: string): string | null {
|
||||
const id = Number(categoryId);
|
||||
if (!Number.isFinite(id)) {
|
||||
return null;
|
||||
}
|
||||
return this.categoryFacade.allCategories().find(category => category.id === id)?.title ?? null;
|
||||
}
|
||||
|
||||
private toColorHex(value: string): string {
|
||||
const normalized = value.trim().toLowerCase();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user