diff --git a/BACKEND-API-REFERENCE.md b/BACKEND-API-REFERENCE.md index 79459a5..696b9ab 100644 --- a/BACKEND-API-REFERENCE.md +++ b/BACKEND-API-REFERENCE.md @@ -522,3 +522,13 @@ Separately, `createCartPayment()` (payment-gateway charge creation) still sends { "telegramUserId": "8823771" } ``` `telegramUserId` may be `null` for a non-Telegram web session - decide whether to also accept an email address as an alternative identifier (the frontend has no email capture on this flow today, so that would need a small frontend addition too). Once this ships, the frontend's localStorage fallback becomes purely a resilience path rather than the common case, and could optionally sync any locally-queued subscriptions on next successful call. + +### 12.6 Trending search terms + +**Gap:** `SearchTrendingService.loadTrending()` is a stub returning `of(null)` - no trending-searches endpoint exists. It already degrades gracefully (UI hides the trending section rather than showing an error), so this is purely a missing-feature gap, not a bug. + +**Ask:** an endpoint returning the top N search queries over some recent window, e.g.: +```json +{ "trending": [{ "query": "wireless earbuds", "count": 214 }, { "query": "winter jacket", "count": 187 }] } +``` +Once it exists, wire `loadTrending()` to it and map `query` -> `SearchSuggestion.title/text`. diff --git a/src/app/features/search/facade/search.facade.ts b/src/app/features/search/facade/search.facade.ts index 3a57c2f..a7d59e9 100644 --- a/src/app/features/search/facade/search.facade.ts +++ b/src/app/features/search/facade/search.facade.ts @@ -55,40 +55,47 @@ export class SearchFacade { readonly state = this.store.state; - readonly popularSearches: SearchSuggestion[] = [ - { - id: 'popular-smartphones', - type: 'collection', - title: 'Smartphones', - text: 'Smartphones', - icon: 'trendingUp', - target: { route: '/search', query: { q: 'Smartphones' } } - }, - { - id: 'popular-sneakers', - type: 'collection', - title: 'Sneakers', - text: 'Sneakers', - icon: 'trendingUp', - target: { route: '/search', query: { q: 'Sneakers' } } - }, - { - id: 'popular-headphones', - type: 'collection', - title: 'Headphones', - text: 'Headphones', - icon: 'trendingUp', - target: { route: '/search', query: { q: 'Headphones' } } - }, - { - id: 'popular-laptops', - type: 'collection', - title: 'Laptops', - text: 'Laptops', - icon: 'trendingUp', - target: { route: '/search', query: { q: 'Laptops' } } - }, - ]; + /** + * Search query text stays the stable English canonical term (what the + * backend search index matches against); only the displayed title/text + * are translated. + */ + get popularSearches(): SearchSuggestion[] { + return [ + { + id: 'popular-smartphones', + type: 'collection', + title: this.translate.t('search.popularSmartphones'), + text: this.translate.t('search.popularSmartphones'), + icon: 'trendingUp', + target: { route: '/search', query: { q: 'Smartphones' } } + }, + { + id: 'popular-sneakers', + type: 'collection', + title: this.translate.t('search.popularSneakers'), + text: this.translate.t('search.popularSneakers'), + icon: 'trendingUp', + target: { route: '/search', query: { q: 'Sneakers' } } + }, + { + id: 'popular-headphones', + type: 'collection', + title: this.translate.t('search.popularHeadphones'), + text: this.translate.t('search.popularHeadphones'), + icon: 'trendingUp', + target: { route: '/search', query: { q: 'Headphones' } } + }, + { + id: 'popular-laptops', + type: 'collection', + title: this.translate.t('search.popularLaptops'), + text: this.translate.t('search.popularLaptops'), + icon: 'trendingUp', + target: { route: '/search', query: { q: 'Laptops' } } + }, + ]; + } constructor() { const history = this.historyService.getSnapshot(); diff --git a/src/app/features/website/user-experience/compare/components/compare-table.component.html b/src/app/features/website/user-experience/compare/components/compare-table.component.html index 487790d..d898b19 100644 --- a/src/app/features/website/user-experience/compare/components/compare-table.component.html +++ b/src/app/features/website/user-experience/compare/components/compare-table.component.html @@ -6,7 +6,7 @@ {{ 'ux.compareAttribute' | translate }} @for (product of products; track product.itemID) { -
{{ product.name }}
+
{{ productTitle(product) }}
} diff --git a/src/app/features/website/user-experience/compare/components/compare-table.component.ts b/src/app/features/website/user-experience/compare/components/compare-table.component.ts index 29b45ea..d1d925e 100644 --- a/src/app/features/website/user-experience/compare/components/compare-table.component.ts +++ b/src/app/features/website/user-experience/compare/components/compare-table.component.ts @@ -2,6 +2,8 @@ import { ChangeDetectionStrategy, Component, Input, computed, inject } from '@an import { Product } from '../../../../../core/products/models/product-domain.model'; import { TranslateService } from '../../../../../i18n/translate.service'; import { TranslatePipe } from '../../../../../i18n/translate.pipe'; +import { LanguageService } from '../../../../../services/language.service'; +import { getTranslatedField } from '../../../../../utils/item.utils'; interface CompareRow { key: string; @@ -27,6 +29,7 @@ const STOCK_LABEL_KEYS: Record = { }) export class CompareTableComponent { private readonly i18n = inject(TranslateService); + private readonly languageService = inject(LanguageService); @Input() products: Product[] = []; @Input() hideIdentical = false; @@ -61,6 +64,10 @@ export class CompareTableComponent { return this.hideIdentical ? baseRows.filter(row => !row.identical) : baseRows; }); + productTitle(product: Product): string { + return getTranslatedField(product, 'name', this.languageService.currentLanguage()); + } + isDifferentRow(row: CompareRow): boolean { return this.highlightDifferences && !row.identical; } diff --git a/src/app/features/website/user-experience/compare/containers/compare-page.component.html b/src/app/features/website/user-experience/compare/containers/compare-page.component.html index 8af8411..e417590 100644 --- a/src/app/features/website/user-experience/compare/containers/compare-page.component.html +++ b/src/app/features/website/user-experience/compare/containers/compare-page.component.html @@ -26,7 +26,7 @@
@for (product of products(); track product.itemID) { } diff --git a/src/app/features/website/user-experience/compare/containers/compare-page.component.ts b/src/app/features/website/user-experience/compare/containers/compare-page.component.ts index f21f025..09adb29 100644 --- a/src/app/features/website/user-experience/compare/containers/compare-page.component.ts +++ b/src/app/features/website/user-experience/compare/containers/compare-page.component.ts @@ -5,6 +5,8 @@ import { Product } from '../../../../../core/products/models/product-domain.mode import { UserExperienceFacade } from '../../../../../facades/platform/user-experience.facade'; import { TranslatePipe } from '../../../../../i18n/translate.pipe'; import { LangRoutePipe } from '../../../../../pipes/lang-route.pipe'; +import { LanguageService } from '../../../../../services/language.service'; +import { getTranslatedField } from '../../../../../utils/item.utils'; import { DEFAULT_USER_EXPERIENCE_CONFIG } from '../../../../../shared/models/config'; import { CompareTableComponent } from '../components/compare-table.component'; import { EmptyStateComponent } from '../../../../../shared/ui/empty-state/empty-state.component'; @@ -21,6 +23,7 @@ import { ButtonComponent } from '../../../../../shared/ui/button/button.componen export class ComparePageComponent { private readonly uxFacade = inject(UserExperienceFacade); private readonly configService = inject(ConfigService); + private readonly languageService = inject(LanguageService); private readonly compareConfig = this.resolveCompareConfig(); @@ -39,6 +42,10 @@ export class ComparePageComponent { this.uxFacade.clearCompare(); } + productTitle(product: Product): string { + return getTranslatedField(product, 'name', this.languageService.currentLanguage()); + } + private resolveCompareConfig() { const raw = (this.configService.getBootstrapSnapshot() as any)?.userExperience?.compare ?? {}; return { diff --git a/src/app/i18n/en.ts b/src/app/i18n/en.ts index ad5cc77..38f4758 100644 --- a/src/app/i18n/en.ts +++ b/src/app/i18n/en.ts @@ -144,6 +144,10 @@ export const en: Translations = { noResultsHint: 'Try changing your query or using different keywords', emptyResultsAria: 'Empty search results', popularCategories: 'Popular categories', + popularSmartphones: 'Smartphones', + popularSneakers: 'Sneakers', + popularHeadphones: 'Headphones', + popularLaptops: 'Laptops', recommendedProducts: 'Recommended products', aiSuggestionHint: 'AI suggestion (future)', suggestionType: { diff --git a/src/app/i18n/hy.ts b/src/app/i18n/hy.ts index c3f4963..cd580e6 100644 --- a/src/app/i18n/hy.ts +++ b/src/app/i18n/hy.ts @@ -144,6 +144,10 @@ export const hy: Translations = { noResultsHint: 'Փորձեք փոխել հարցումը կամ օգտագործել այլ բանալի բառեր', emptyResultsAria: 'Դատարկ որոնման արդյունքներ', popularCategories: 'Հանրաճանաչ կատեգորիաներ', + popularSmartphones: 'Սմարթֆոններ', + popularSneakers: 'Կեդեր', + popularHeadphones: 'Ականջակալներ', + popularLaptops: 'Նոութբուքեր', recommendedProducts: 'Առաջարկվող ապրանքներ', aiSuggestionHint: 'AI առաջարկ (ապագայում)', suggestionType: { diff --git a/src/app/i18n/ru.ts b/src/app/i18n/ru.ts index 7503dc2..25f701f 100644 --- a/src/app/i18n/ru.ts +++ b/src/app/i18n/ru.ts @@ -144,6 +144,10 @@ export const ru: Translations = { noResultsHint: 'Попробуйте изменить запрос или используйте другие ключевые слова', emptyResultsAria: 'Пустые результаты поиска', popularCategories: 'Популярные категории', + popularSmartphones: 'Смартфоны', + popularSneakers: 'Кроссовки', + popularHeadphones: 'Наушники', + popularLaptops: 'Ноутбуки', recommendedProducts: 'Рекомендуемые товары', aiSuggestionHint: 'AI-подсказка (в будущем)', suggestionType: { diff --git a/src/app/i18n/translations.ts b/src/app/i18n/translations.ts index 9304c16..f41addb 100644 --- a/src/app/i18n/translations.ts +++ b/src/app/i18n/translations.ts @@ -142,6 +142,10 @@ export interface Translations { noResultsHint: string; emptyResultsAria: string; popularCategories: string; + popularSmartphones: string; + popularSneakers: string; + popularHeadphones: string; + popularLaptops: string; recommendedProducts: string; aiSuggestionHint: string; suggestionType: {