fix(icons): replace broken Material-icon-ligature text with app-icon in search suggestions

Search suggestions/popular-searches (search-autocomplete.service.ts,
search.facade.ts) set icon values like 'inventory_2', 'category', 'sell',
'auto_awesome', 'trending_up' - Material Symbols ligature names rendered as
raw {{ item.icon }} text in search-bar.component.html. No Material Icons
font is loaded anywhere in this Lucide/app-icon-based app, so these
rendered as literal garbled text ("inventory_2", etc.) instead of icons.

- SearchSuggestion.icon retyped from string to AppIconName (search.model.ts)
- Suggestion icon values mapped to registered app-icon names: product->package,
  category->folder, brand->tag, ai->zap, popular/trending->trendingUp (new
  registry entry, LucideTrendingUp)
- search-bar.component now renders <app-icon [name]="item.icon" /> instead of
  the raw ligature string, and its icon-only clear ("x") button now renders
  app-icon name="x" instead of a bare literal "x" character glyph

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
sdarbinyan
2026-07-25 20:15:36 +04:00
parent 5b3f969dfd
commit 6d663eb238
7 changed files with 24 additions and 16 deletions

View File

@@ -26,7 +26,9 @@
<span class="loading-dot" [attr.aria-label]="'search.searching' | translate"></span> <span class="loading-dot" [attr.aria-label]="'search.searching' | translate"></span>
} }
<button type="button" class="search-clear-btn" [disabled]="!query" (click)="clearQuery()" [attr.aria-label]="'catalog.clearSearch' | translate">×</button> <button type="button" class="search-clear-btn" [disabled]="!query" (click)="clearQuery()" [attr.aria-label]="'catalog.clearSearch' | translate">
<app-icon name="x" [size]="16" />
</button>
<button type="submit" [disabled]="loading">{{ 'catalog.searchSubmit' | translate }}</button> <button type="submit" [disabled]="loading">{{ 'catalog.searchSubmit' | translate }}</button>
</form> </form>
@@ -44,7 +46,7 @@
[attr.aria-selected]="$index === activeSuggestionIndex()" [attr.aria-selected]="$index === activeSuggestionIndex()"
[attr.aria-label]="suggestionLabel(item)" [attr.aria-label]="suggestionLabel(item)"
(click)="selectSuggestion(item)"> (click)="selectSuggestion(item)">
<span class="suggestion-icon" aria-hidden="true">{{ item.icon }}</span> <span class="suggestion-icon" aria-hidden="true"><app-icon [name]="item.icon" [size]="16" /></span>
<span class="suggestion-copy"> <span class="suggestion-copy">
<span class="suggestion-title">{{ item.title }}</span> <span class="suggestion-title">{{ item.title }}</span>
@if (item.subtitle) { @if (item.subtitle) {

View File

@@ -59,11 +59,13 @@
.search-clear-btn { .search-clear-btn {
min-width: 44px; min-width: 44px;
min-height: 44px; min-height: 44px;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid var(--border-color) !important; border: 1px solid var(--border-color) !important;
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
background: var(--bg-primary) !important; background: var(--bg-primary) !important;
color: var(--text-secondary) !important; color: var(--text-secondary) !important;
font-size: var(--font-size-2xl, 1.25rem);
line-height: 1; line-height: 1;
} }
@@ -98,8 +100,8 @@
} }
.suggestion-icon { .suggestion-icon {
font-size: var(--font-size-xs, 0.75rem); display: flex;
text-transform: uppercase; align-items: center;
color: var(--text-secondary); color: var(--text-secondary);
} }

View File

@@ -2,12 +2,13 @@ import { ChangeDetectionStrategy, Component, EventEmitter, HostListener, Input,
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { SearchSuggestion } from '../../models/search.model'; import { SearchSuggestion } from '../../models/search.model';
import { TranslatePipe } from '../../../../i18n/translate.pipe'; import { TranslatePipe } from '../../../../i18n/translate.pipe';
import { IconComponent } from '../../../../shared/ui/icon/icon.component';
import { TrendingSearchesComponent } from '../trending-searches/trending-searches.component'; import { TrendingSearchesComponent } from '../trending-searches/trending-searches.component';
@Component({ @Component({
selector: 'app-search-bar', selector: 'app-search-bar',
standalone: true, standalone: true,
imports: [FormsModule, TranslatePipe, TrendingSearchesComponent], imports: [FormsModule, TranslatePipe, IconComponent, TrendingSearchesComponent],
templateUrl: './search-bar.component.html', templateUrl: './search-bar.component.html',
styleUrls: ['./search-bar.component.scss'], styleUrls: ['./search-bar.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush

View File

@@ -59,7 +59,7 @@ export class SearchFacade {
type: 'collection', type: 'collection',
title: 'Smartphones', title: 'Smartphones',
text: 'Smartphones', text: 'Smartphones',
icon: 'trending_up', icon: 'trendingUp',
target: { route: '/search', query: { q: 'Smartphones' } } target: { route: '/search', query: { q: 'Smartphones' } }
}, },
{ {
@@ -67,7 +67,7 @@ export class SearchFacade {
type: 'collection', type: 'collection',
title: 'Sneakers', title: 'Sneakers',
text: 'Sneakers', text: 'Sneakers',
icon: 'trending_up', icon: 'trendingUp',
target: { route: '/search', query: { q: 'Sneakers' } } target: { route: '/search', query: { q: 'Sneakers' } }
}, },
{ {
@@ -75,7 +75,7 @@ export class SearchFacade {
type: 'collection', type: 'collection',
title: 'Headphones', title: 'Headphones',
text: 'Headphones', text: 'Headphones',
icon: 'trending_up', icon: 'trendingUp',
target: { route: '/search', query: { q: 'Headphones' } } target: { route: '/search', query: { q: 'Headphones' } }
}, },
{ {
@@ -83,7 +83,7 @@ export class SearchFacade {
type: 'collection', type: 'collection',
title: 'Laptops', title: 'Laptops',
text: 'Laptops', text: 'Laptops',
icon: 'trending_up', icon: 'trendingUp',
target: { route: '/search', query: { q: 'Laptops' } } target: { route: '/search', query: { q: 'Laptops' } }
}, },
]; ];

View File

@@ -1,4 +1,5 @@
import { Product, ProductSort } from '../../../core/products/models/product-domain.model'; import { Product, ProductSort } from '../../../core/products/models/product-domain.model';
import { AppIconName } from '../../../shared/ui/icon/icon-registry';
export type SearchFilterType = export type SearchFilterType =
| 'checkbox' | 'checkbox'
@@ -33,7 +34,7 @@ export interface SearchSuggestion {
text: string; text: string;
kind?: 'live' | 'recent' | 'popular'; kind?: 'live' | 'recent' | 'popular';
subtitle?: string; subtitle?: string;
icon: string; icon: AppIconName;
target: SearchNavigationTarget; target: SearchNavigationTarget;
score?: number; score?: number;
} }

View File

@@ -22,7 +22,7 @@ export class SearchAutocompleteService {
text: item.name, text: item.name,
kind: 'live' as const, kind: 'live' as const,
subtitle: item.simpleDescription ?? '', subtitle: item.simpleDescription ?? '',
icon: 'inventory_2', icon: 'package',
target: { route: '/product', params: { id: item.itemID } }, target: { route: '/product', params: { id: item.itemID } },
score: this.score(item.name, normalized), score: this.score(item.name, normalized),
})) }))
@@ -39,7 +39,7 @@ export class SearchAutocompleteService {
text: item.title, text: item.title,
kind: 'live' as const, kind: 'live' as const,
subtitle: '', subtitle: '',
icon: 'category', icon: 'folder',
target: { route: '/catalog', params: { id: item.id } }, target: { route: '/catalog', params: { id: item.id } },
score: this.score(item.title, normalized), score: this.score(item.title, normalized),
})) }))
@@ -57,7 +57,7 @@ export class SearchAutocompleteService {
text: tag, text: tag,
kind: 'live' as const, kind: 'live' as const,
subtitle: '', subtitle: '',
icon: 'sell', icon: 'tag',
target: { route: '/search', query: { q: tag } }, target: { route: '/search', query: { q: tag } },
score: this.score(tag, normalized), score: this.score(tag, normalized),
})) }))
@@ -71,7 +71,7 @@ export class SearchAutocompleteService {
text: query, text: query,
kind: 'live', kind: 'live',
subtitle: 'search.aiSuggestionHint', subtitle: 'search.aiSuggestionHint',
icon: 'auto_awesome', icon: 'zap',
target: { route: '/search', query: { q: query } }, target: { route: '/search', query: { q: query } },
score: 0, score: 0,
} }

View File

@@ -73,6 +73,7 @@ import {
LucideThumbsDown as ThumbsDown, LucideThumbsDown as ThumbsDown,
LucideThumbsUp as ThumbsUp, LucideThumbsUp as ThumbsUp,
LucideTrash2 as Trash2, LucideTrash2 as Trash2,
LucideTrendingUp as TrendingUp,
LucideTriangleAlert as TriangleAlert, LucideTriangleAlert as TriangleAlert,
LucideUpload as Upload, LucideUpload as Upload,
LucideUser as User, LucideUser as User,
@@ -171,7 +172,8 @@ export const APP_ICONS = {
verified: BadgeCheck, verified: BadgeCheck,
video: Video, video: Video,
circle: Circle, circle: Circle,
zap: Zap zap: Zap,
trendingUp: TrendingUp
} as const; } as const;
export type AppIconName = keyof typeof APP_ICONS; export type AppIconName = keyof typeof APP_ICONS;