search engien

This commit is contained in:
sdarbinyan
2026-07-10 13:15:46 +04:00
parent aed0a47388
commit 494451bb96
15 changed files with 1060 additions and 649 deletions

View File

@@ -1,27 +1 @@
import { CatalogLayoutMode } from '../../products/models/catalog-experience.model';
import { ProductSort } from '../../products/models/product-domain.model';
import { SearchFilterState } from './search.model';
export interface SearchState {
text: string;
sort: ProductSort | 'discount';
layout: CatalogLayoutMode;
page: number;
pageSize: number;
filters: SearchFilterState;
}
export function createInitialSearchState(pageSize = 24): SearchState {
return {
text: '',
sort: 'relevance',
layout: 'grid',
page: 1,
pageSize,
filters: {
values: {},
ranges: {},
toggles: {},
},
};
}
export * from '../../../features/search/models/search-state.model';

View File

@@ -1,76 +1 @@
import { Product, ProductSort } from '../../products/models/product-domain.model';
export type SearchFilterType =
| 'checkbox'
| 'radio'
| 'toggle'
| 'range'
| 'slider'
| 'color'
| 'size'
| 'rating'
| 'availability';
export interface SearchQuery {
text: string;
categoryIds: number[];
subcategoryIds: string[];
filters: SearchFilterState;
sort: ProductSort | 'discount';
page: number;
pageSize: number;
}
export interface SearchResult<TItem = Product> {
query: SearchQuery;
items: TItem[];
total: number;
page: number;
pageSize: number;
summary: string;
}
export interface FilterOption {
id: string;
label: string;
value: string;
count?: number;
colorHex?: string;
availability?: 'in-stock' | 'low-stock' | 'out-of-stock';
rating?: number;
}
export interface FilterGroup {
id: string;
label: string;
type: SearchFilterType;
options: FilterOption[];
min?: number;
max?: number;
step?: number;
enabled: boolean;
}
export interface SortOption {
id: ProductSort | 'discount';
label: string;
enabled: boolean;
}
export interface SearchSuggestion {
id: string;
text: string;
kind: 'live' | 'recent' | 'popular';
}
export interface SearchHistory {
items: string[];
recent: string[];
popular: string[];
}
export interface SearchFilterState {
values: Record<string, string[]>;
ranges: Record<string, { min?: number; max?: number }>;
toggles: Record<string, boolean>;
}
export * from '../../../features/search/models/search.model';

View File

@@ -1,76 +1 @@
import { Injectable } from '@angular/core';
import { SearchHistory } from '../models/search.model';
const STORAGE_KEY = 'marketplace.catalog.search.history';
@Injectable({ providedIn: 'root' })
export class SearchHistoryService {
private readonly maxSize = 10;
getHistory(): string[] {
if (typeof window === 'undefined') {
return [];
}
try {
const raw = localStorage.getItem(STORAGE_KEY);
if (!raw) {
return [];
}
const parsed = JSON.parse(raw);
return Array.isArray(parsed)
? parsed.filter((entry): entry is string => typeof entry === 'string' && entry.trim().length > 0)
: [];
} catch {
return [];
}
}
getSnapshot(popular: string[]): SearchHistory {
const items = this.getHistory();
return {
items,
recent: items.slice(0, 5),
popular,
};
}
push(term: string, popular: string[]): SearchHistory {
const normalized = term.trim();
if (!normalized.length) {
return this.getSnapshot(popular);
}
const history = [normalized, ...this.getHistory().filter(entry => entry.toLowerCase() !== normalized.toLowerCase())]
.slice(0, this.maxSize);
this.save(history);
return {
items: history,
recent: history.slice(0, 5),
popular,
};
}
clear(popular: string[]): SearchHistory {
if (typeof window !== 'undefined') {
localStorage.removeItem(STORAGE_KEY);
}
return {
items: [],
recent: [],
popular,
};
}
private save(history: string[]): void {
if (typeof window === 'undefined') {
return;
}
localStorage.setItem(STORAGE_KEY, JSON.stringify(history));
}
}
export { SearchHistoryService } from '../../../features/search/services/search-history.service';