Polish marketplace search states
This commit is contained in:
@@ -131,6 +131,7 @@ export const en: Translations = {
|
|||||||
loadingMore: 'Loading...',
|
loadingMore: 'Loading...',
|
||||||
allLoaded: 'All results loaded',
|
allLoaded: 'All results loaded',
|
||||||
emptyState: 'Enter a query to search for products',
|
emptyState: 'Enter a query to search for products',
|
||||||
|
minLength: 'Enter at least {{count}} characters to search',
|
||||||
of: 'of',
|
of: 'of',
|
||||||
},
|
},
|
||||||
category: {
|
category: {
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ export const hy: Translations = {
|
|||||||
loadingMore: 'Բեռնում...',
|
loadingMore: 'Բեռնում...',
|
||||||
allLoaded: 'Բոլոր արդյունքները բեռնված են',
|
allLoaded: 'Բոլոր արդյունքները բեռնված են',
|
||||||
emptyState: 'Մուտքագրեք հարցում որոնման համար',
|
emptyState: 'Մուտքագրեք հարցում որոնման համար',
|
||||||
|
minLength: 'Որոնման համար մուտքագրեք առնվազն {{count}} նիշ',
|
||||||
of: '-ից',
|
of: '-ից',
|
||||||
},
|
},
|
||||||
category: {
|
category: {
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ export const ru: Translations = {
|
|||||||
loadingMore: 'Загрузка...',
|
loadingMore: 'Загрузка...',
|
||||||
allLoaded: 'Все результаты загружены',
|
allLoaded: 'Все результаты загружены',
|
||||||
emptyState: 'Введите запрос для поиска товаров',
|
emptyState: 'Введите запрос для поиска товаров',
|
||||||
|
minLength: 'Введите минимум {{count}} символа для поиска',
|
||||||
of: 'из',
|
of: 'из',
|
||||||
},
|
},
|
||||||
category: {
|
category: {
|
||||||
|
|||||||
@@ -129,6 +129,7 @@ export interface Translations {
|
|||||||
loadingMore: string;
|
loadingMore: string;
|
||||||
allLoaded: string;
|
allLoaded: string;
|
||||||
emptyState: string;
|
emptyState: string;
|
||||||
|
minLength: string;
|
||||||
of: string;
|
of: string;
|
||||||
};
|
};
|
||||||
category: {
|
category: {
|
||||||
|
|||||||
@@ -39,11 +39,23 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (!loading() && searchQuery && items().length === 0 && !error()) {
|
@if (isQueryTooShort()) {
|
||||||
|
<div class="empty-state">
|
||||||
|
<div class="empty-state-icon">
|
||||||
|
<svg width="56" height="56" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 4C7.58172 4 4 7.58172 4 12C4 16.4183 7.58172 20 12 20C16.4183 20 20 16.4183 20 12C20 7.58172 16.4183 4 12 4ZM2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12Z" fill="#d3dad9" />
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M18.2929 18.2929C18.6834 17.9024 19.3166 17.9024 19.7071 18.2929L25.7071 24.2929C26.0976 24.6834 26.0976 25.3166 25.7071 25.7071C25.3166 26.0976 24.6834 26.0976 24.2929 25.7071L18.2929 19.7071C17.9024 19.3166 17.9024 18.6834 18.2929 18.2929Z" fill="#d3dad9" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<p>{{ 'search.minLength' | translate:{ count: minSearchLength } }}</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (!loading() && searchQuery && !isQueryTooShort() && items().length === 0 && !error()) {
|
||||||
<div class="no-results">
|
<div class="no-results">
|
||||||
<div class="no-results-icon">
|
<div class="no-results-icon">
|
||||||
<svg width="48" height="48" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="48" height="48" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 4C7.58172 4 4 7.58172 4 12C4 16.4183 7.58172 20 12 20C16.4183 20 20 16.4183 20 12C20 7.58172 16.4183 4 12 4ZZ2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12Z" fill="#a1b4b5" />
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 4C7.58172 4 4 7.58172 4 12C4 16.4183 7.58172 20 12 20C16.4183 20 20 16.4183 20 12C20 7.58172 16.4183 4 12 4ZM2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12Z" fill="#a1b4b5" />
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M18.2929 18.2929C18.6834 17.9024 19.3166 17.9024 19.7071 18.2929L25.7071 24.2929C26.0976 24.6834 26.0976 25.3166 25.7071 25.7071C25.3166 26.0976 24.6834 26.0976 24.2929 25.7071L18.2929 19.7071C17.9024 19.3166 17.9024 18.6834 18.2929 18.2929Z" fill="#a1b4b5" />
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M18.2929 18.2929C18.6834 17.9024 19.3166 17.9024 19.7071 18.2929L25.7071 24.2929C26.0976 24.6834 26.0976 25.3166 25.7071 25.7071C25.3166 26.0976 24.6834 26.0976 24.2929 25.7071L18.2929 19.7071C17.9024 19.3166 17.9024 18.6834 18.2929 18.2929Z" fill="#a1b4b5" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import { ProductCardComponent } from '../../components/product-card/product-card
|
|||||||
changeDetection: ChangeDetectionStrategy.OnPush
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
})
|
})
|
||||||
export class SearchComponent implements OnDestroy {
|
export class SearchComponent implements OnDestroy {
|
||||||
|
readonly minSearchLength = 3;
|
||||||
searchQuery = '';
|
searchQuery = '';
|
||||||
items = signal<Item[]>([]);
|
items = signal<Item[]>([]);
|
||||||
loading = signal(false);
|
loading = signal(false);
|
||||||
@@ -60,17 +61,23 @@ export class SearchComponent implements OnDestroy {
|
|||||||
|
|
||||||
onSearchInput(query: string): void {
|
onSearchInput(query: string): void {
|
||||||
this.searchQuery = query;
|
this.searchQuery = query;
|
||||||
|
this.error.set(null);
|
||||||
|
|
||||||
|
if (this.isQueryTooShort()) {
|
||||||
|
this.resetSearchState();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.searchSubject.next(query);
|
this.searchSubject.next(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
performSearch(query: string): void {
|
performSearch(query: string): void {
|
||||||
if (!query.trim()) {
|
if (!query.trim()) {
|
||||||
this.items.set([]);
|
this.resetSearchState();
|
||||||
this.hasMore.set(false);
|
|
||||||
this.totalResults.set(0);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.error.set(null);
|
||||||
this.items.set([]);
|
this.items.set([]);
|
||||||
this.skip = 0;
|
this.skip = 0;
|
||||||
this.hasMore.set(true);
|
this.hasMore.set(true);
|
||||||
@@ -84,7 +91,7 @@ export class SearchComponent implements OnDestroy {
|
|||||||
this.loading.set(true);
|
this.loading.set(true);
|
||||||
this.isLoadingMore = true;
|
this.isLoadingMore = true;
|
||||||
|
|
||||||
this.productFacade.searchProducts({ search: this.searchQuery, count: this.count, skip: this.skip }).subscribe({
|
this.productFacade.searchProducts({ search: this.searchQuery.trim(), count: this.count, skip: this.skip }).subscribe({
|
||||||
next: (response) => {
|
next: (response) => {
|
||||||
// Update total results (only on first load)
|
// Update total results (only on first load)
|
||||||
if (this.skip === 0) {
|
if (this.skip === 0) {
|
||||||
@@ -114,6 +121,20 @@ export class SearchComponent implements OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private resetSearchState(): void {
|
||||||
|
this.items.set([]);
|
||||||
|
this.loading.set(false);
|
||||||
|
this.hasMore.set(false);
|
||||||
|
this.totalResults.set(0);
|
||||||
|
this.skip = 0;
|
||||||
|
this.isLoadingMore = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
isQueryTooShort(): boolean {
|
||||||
|
const length = this.searchQuery.trim().length;
|
||||||
|
return length > 0 && length < this.minSearchLength;
|
||||||
|
}
|
||||||
|
|
||||||
private scrollTimeout?: ReturnType<typeof setTimeout>;
|
private scrollTimeout?: ReturnType<typeof setTimeout>;
|
||||||
|
|
||||||
@HostListener('window:scroll')
|
@HostListener('window:scroll')
|
||||||
|
|||||||
Reference in New Issue
Block a user