Skip to content

Commit

Permalink
feat: Show total in front of each section header in search results
Browse files Browse the repository at this point in the history
When using search, show not only a total number of results, but also total number for each section separately.

Resolves: #7745
  • Loading branch information
bzhn committed Nov 6, 2024
1 parent 5a715cb commit 810f526
Show file tree
Hide file tree
Showing 6 changed files with 52 additions and 63 deletions.
15 changes: 2 additions & 13 deletions src/app/main/model/search/search.model.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,6 @@
import { NewsSearchModel } from './newsSearch.model';
import { TipsSearchModel } from './tipsSearch.model';
import { EventsSearchModel } from './eventsSearch.model';

export interface SearchModel {
countOfResults: number;
ecoNews: Array<NewsSearchModel>;
events: Array<EventsSearchModel>;
tipsAndTricks: Array<TipsSearchModel>;
}

export interface SearchDataModel {
export interface SearchDataModel<T = any> {
currentPage: number;
page: Array<TipsSearchModel>;
page: Array<T>;
totalElements: number;
totalPages: number;
}
7 changes: 4 additions & 3 deletions src/app/main/service/search/search.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { environment } from '@environment/environment';
import { Observable, Subject } from 'rxjs';
import { SearchDataModel, SearchModel } from '../../model/search/search.model';
import { SearchDataModel } from '../../model/search/search.model';
import { SearchDto } from 'src/app/main/component/layout/components/models/search-dto';
import { SearchCategory } from 'src/app/shared/search-popup/search-consts';

@Injectable({
providedIn: 'root'
Expand All @@ -15,8 +16,8 @@ export class SearchService {
allSearchSubject = new Subject<boolean>();
allElements: SearchDto;

getAllResults(searchQuery: string, category, lang: string): Observable<SearchModel> {
return this.http.get<SearchModel>(`${this.backEndLink}search/${category}?lang=${lang}&searchQuery=${encodeURI(searchQuery)}`);
getAllResults(searchQuery: string, category: SearchCategory, lang: string): Observable<SearchDataModel> {
return this.http.get<SearchDataModel>(`${this.backEndLink}search/${category}?lang=${lang}&searchQuery=${encodeURI(searchQuery)}`);
}

getAllResultsByCat(
Expand Down
18 changes: 13 additions & 5 deletions src/app/shared/search-popup/search-popup.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@
<div class="search_body-wrapper">
<div class="search-content-wrapper container">
<div class="search_remaining-items" *ngIf="!isLoading && searchInput.value">
{{ itemsFound }} {{ 'search.search-popup.items-found' | translate }}
{{ resultsCounters.total }} {{ 'search.search-popup.items-found' | translate }}
</div>
<div *ngIf="newsElements && newsElements.length > 0">
<a [routerLink]="['/news']" (click)="closeSearch()">
<h3 class="search-title">{{ 'search.search-popup.news' | translate }}</h3>
<h3 class="search-title">
{{ 'search.search-popup.news' | translate }} <span class="counter">({{ resultsCounters.news }})</span>
</h3>
</a>
<div class="list-search-items">
<app-search-item
Expand All @@ -42,7 +44,9 @@ <h3 class="search-title">{{ 'search.search-popup.news' | translate }}</h3>
</div>
<div *ngIf="eventsElements && eventsElements.length > 0">
<a [routerLink]="['/events']" (click)="closeSearch()">
<h3 class="search-title">{{ 'search.search-popup.events' | translate }}</h3>
<h3 class="search-title">
{{ 'search.search-popup.events' | translate }} <span class="counter">({{ resultsCounters.events }})</span>
</h3>
</a>
<div class="list-search-items">
<app-search-item
Expand All @@ -55,7 +59,7 @@ <h3 class="search-title">{{ 'search.search-popup.events' | translate }}</h3>
</app-search-item>
</div>
</div>
<div class="search_see-all" *ngIf="itemsFound">
<div class="search_see-all" *ngIf="resultsCounters.total">
<a
[routerLink]="['/search']"
[queryParams]="{ query: searchInput.value, category: newsElements && newsElements.length > 0 ? 'econews' : 'events' }"
Expand All @@ -65,7 +69,11 @@ <h3 class="search-title">{{ 'search.search-popup.events' | translate }}</h3>
</a>
</div>

<app-search-not-found *ngIf="itemsFound === 0" [inputValue]="searchInput.value" (closeUnsuccessfulSearchResults)="closeSearch()">
<app-search-not-found
*ngIf="resultsCounters.total === 0"
[inputValue]="searchInput.value"
(closeUnsuccessfulSearchResults)="closeSearch()"
>
</app-search-not-found>
</div>
<app-spinner class="search_spinner" *ngIf="isLoading"></app-spinner>
Expand Down
4 changes: 4 additions & 0 deletions src/app/shared/search-popup/search-popup.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,10 @@ div.search-content-wrapper {
display: block;
}

span.counter {
opacity: 0.5;
}

@media (min-width: 576px) {
.list-search-items {
grid-template-columns: repeat(2, 254px);
Expand Down
41 changes: 11 additions & 30 deletions src/app/shared/search-popup/search-popup.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ import { LocalStorageService } from '@global-service/localstorage/local-storage.
import { SharedModule } from 'src/app/shared/shared.module';
import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { EventsSearchModel } from '@global-models/search/eventsSearch.model';
import { SearchDataModel } from '@global-models/search/search.model';
import { SearchCategory } from './search-consts';

describe('SearchPopupComponent', () => {
let component: SearchPopupComponent;
Expand All @@ -26,45 +29,23 @@ describe('SearchPopupComponent', () => {
const localStorageServiceMock: LocalStorageService = jasmine.createSpyObj('LocalStorageService', ['getCurrentLanguage']);
localStorageServiceMock.getCurrentLanguage = () => 'ua' as Language;

const mockTipData = {
id: 1,
title: 'test',
author: {
id: 1,
name: 'test'
},
creationDate: '0101',
tags: ['test']
};

const mockNewsData = {
id: 1,
title: 'test',
author: {
id: 1,
name: 'test'
},
creationDate: '0101',
tags: ['test']
};

const mockEventsData = {
id: 1,
title: 'test',
creationDate: '0101',
tags: ['test']
};

const searchModelMock = {
countOfResults: 2,
ecoNews: [mockNewsData],
events: [mockEventsData],
tipsAndTricks: [mockTipData]
const eventsSearchModelMock: SearchDataModel<EventsSearchModel> = {
currentPage: 1,
page: [mockEventsData],
totalElements: 1,
totalPages: 1
};

const searchMock: SearchService = jasmine.createSpyObj('SearchService', ['getAllResults']);
searchMock.searchSubject = new Subject();
searchMock.getAllResults = () => of(searchModelMock);
searchMock.getAllResults = () => of(eventsSearchModelMock);
searchMock.closeSearchSignal = () => true;

beforeEach(waitForAsync(() => {
Expand Down Expand Up @@ -113,12 +94,12 @@ describe('SearchPopupComponent', () => {

describe('Testing services:', () => {
it('should handle search value changes', fakeAsync(() => {
const getSearchSpy = spyOn(component.searchService, 'getAllResults').and.returnValue(of(searchModelMock));
const getSearchSpy = spyOn(component.searchService, 'getAllResults').and.returnValue(of(eventsSearchModelMock));
component.ngOnInit();

component.searchInput.setValue('test');
tick(300);
expect(getSearchSpy).toHaveBeenCalledWith('test', 'econews', 'ua');
expect(getSearchSpy).toHaveBeenCalledWith('test', SearchCategory.EVENTS, 'ua');
}));

it('closeSearch should open SearchService/closeSearchSignal', () => {
Expand Down
30 changes: 18 additions & 12 deletions src/app/shared/search-popup/search-popup.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,14 @@ import { SearchCategory } from './search-consts';
styleUrls: ['./search-popup.component.scss']
})
export class SearchPopupComponent implements OnInit, OnDestroy {
resultsCounters: {
news: number;
events: number;
total: number;
} = { events: null, news: null, total: null };
newsElements: NewsSearchModel[] = [];
eventsElements: EventsSearchModel[] = [];
isSearchClicked = false;
itemsFound: number = null;
searchModalSubscription: Subscription;
searchInput = new FormControl('');
isLoading = false;
Expand Down Expand Up @@ -53,15 +57,15 @@ export class SearchPopupComponent implements OnInit, OnDestroy {
this.resetData();
this.isLoading = true;
}),
switchMap((val: string) => {
switchMap((query: string) => {
this.currentLanguage = this.localStorageService.getCurrentLanguage();
return forkJoin([
this.searchService.getAllResults(val, SearchCategory.NEWS, this.currentLanguage),
this.searchService.getAllResults(val, SearchCategory.EVENTS, this.currentLanguage)
]);
return forkJoin({
news: this.searchService.getAllResults(query, SearchCategory.NEWS, this.currentLanguage),
events: this.searchService.getAllResults(query, SearchCategory.EVENTS, this.currentLanguage)
});
})
)
.subscribe((data: SearchDataModel[]) => {
.subscribe((data: { news: SearchDataModel<NewsSearchModel>; events: SearchDataModel<EventsSearchModel> }) => {
this.setData(data);
});

Expand All @@ -80,12 +84,14 @@ export class SearchPopupComponent implements OnInit, OnDestroy {
this.snackBar.openSnackBar('error');
}

private setData(data: SearchDataModel[]): void {
private setData(data: { news: SearchDataModel<NewsSearchModel>; events: SearchDataModel<EventsSearchModel> }): void {
this.isLoading = false;

this.newsElements = data[0].page;
this.eventsElements = data[1].page;
this.itemsFound = data[0].totalElements + data[1].totalElements;
this.newsElements = data.news.page;
this.eventsElements = data.events.page;
this.resultsCounters.events = data.events.totalElements;
this.resultsCounters.news = data.news.totalElements;
this.resultsCounters.total = data.events.totalElements + data.news.totalElements;
}

private subscribeToSignal(signal: boolean): void {
Expand All @@ -104,7 +110,7 @@ export class SearchPopupComponent implements OnInit, OnDestroy {
private resetData(): void {
this.newsElements = [];
this.eventsElements = [];
this.itemsFound = null;
this.resultsCounters = { events: null, news: null, total: null };
}

ngOnDestroy() {
Expand Down

0 comments on commit 810f526

Please sign in to comment.