[HIMA-598] Update of filters product search

- Added new filters to product search page
- Added new filters to product result search page
- Updated logic for filters
This commit is contained in:
Milos Jovanov
2019-06-11 12:24:41 +02:00
parent bbc4a5cae8
commit 05efaf9246
29 changed files with 443 additions and 87 deletions

View File

@@ -3,4 +3,5 @@ import { Filter } from './filter.model';
export interface ProcessSelectedFilters {
processId: number;
selectedFilters: Filter[];
dropdownFilters: Filter[];
}

View File

@@ -2,6 +2,7 @@ import { Search } from './search.model';
import { ItemDTO } from 'swagger';
import { CustomerSearch } from './customer-search.model';
import { DisplayOrderDTO } from 'libs/swagger/src/lib/oms/models/display-order-dto';
import { ProductSerachFilters } from './product-search-filters.model';
export interface Process {
id: number;
@@ -25,6 +26,7 @@ export interface Process {
ordersOpenend: boolean;
closeDirectlyTab: boolean;
articleSearchErrorStatus: boolean;
productSearchFilters: ProductSerachFilters;
}
export class CustomerFormState {

View File

@@ -0,0 +1,6 @@
import { RadioButtonGroup } from '@libs/ui/lib/radio-button-group';
export interface ProductSerachFilters {
radioButtonGroup: RadioButtonGroup;
archiveFilters: boolean;
}

View File

@@ -8,4 +8,5 @@ export interface Search {
firstLoad: boolean;
type?: string;
hits?: boolean;
archiveFilter: boolean;
}

View File

@@ -67,7 +67,7 @@ export class ProductService {
// service method for calling product search API
searchItemsCheckForresults(search: Search): Observable<number> {
this.persistLastSearchToLocalStorage(search.query);
const queryToken = <QueryTokenDTO>{
let queryToken = <QueryTokenDTO>{
input: this.createSearchInput(search),
skip: search.skip,
take: search.take,
@@ -113,6 +113,7 @@ export class ProductService {
skip: skip,
take: size,
};
const queryWithFilters = this.filterMapping.toQueryTokenDto(queryToken, filters);
return this.searchService.SearchSearch(queryWithFilters).pipe(
map(response => {
@@ -187,6 +188,7 @@ export class ProductService {
} else if (search.type === 'publisher') {
input = { publisher: search.query };
}
return input;
}
}

View File

@@ -3,6 +3,7 @@ import { Search } from '../../models/search.model';
import { CustomerSearch } from '../../models/customer-search.model';
import { DisplayOrderDTO } from 'libs/swagger/src/lib/oms/models/display-order-dto';
import { RecentArticleSearch } from '../../models/recent-article-search.model';
import { ProductSerachFilters } from '../../models/product-search-filters.model';
export const ADD_PROCESS = '[PROCESS] Add';
export const DELETE_PROCESS = '[PROCESS] Delete';
@@ -29,6 +30,7 @@ export const ORDERS_PAGE_OPENED = '[PROCESS] Orders page visited for customer';
export const EMPTY_FINISHED_ORDERS = '[PROCESS] Empty finished orders';
export const SET_ARTICLE_SEARCH_ERROR_STATUS = '[PROCESS] Set article search error status';
export const SET_PROCESS_NEW_STATUS_TO_FALSE = '[PROCESS] Set process new status to false';
export const SET_PROCESS_PRODUCT_FILTERS = '[PROCESS] Set process filters for products';
export class AddProcess {
static readonly type = ADD_PROCESS;
@@ -161,3 +163,9 @@ export class SetProcessNewStatusToFalse {
constructor() {}
}
export class SetProcessProductFilters {
static readonly type = SET_PROCESS_PRODUCT_FILTERS;
constructor(public filters: ProductSerachFilters) {}
}

View File

@@ -93,4 +93,9 @@ export class ProcessSelectors {
static getPreviousRoute(state: AppStateModel, processState: ProcessStateModel) {
return processState.processes[state.currentProcesssId].previousRoute;
}
@Selector([AppState, ProcessState])
static getProductFilters(state: AppStateModel, processState: ProcessStateModel) {
return processState.processes[state.currentProcesssId].productSearchFilters;
}
}

View File

@@ -154,6 +154,7 @@ export class SharedSelectors {
filter.push(f);
}
});
return filter;
}
return null;

View File

@@ -5,15 +5,15 @@ import { FilterService } from '../../services/filter.service';
import { ProcessSelectedFilters } from '../../models/process-selected-filters.model';
import { FilterItem } from '../../models/filter-item.model';
import { removeItem } from '@ngxs/store/operators';
import { tap, filter } from 'rxjs/operators';
import { tap } from 'rxjs/operators';
import { AppState } from './app.state';
import { AddSearch } from '../actions/process.actions';
import { ProcessSelectors } from '../selectors/process.selectors';
import { ItemSizeAverager } from '@angular/cdk-experimental/scrolling';
export class FilterStateModel {
filters: Filter[];
processesSelectedFilters: ProcessSelectedFilters[];
dropdownFilters: Filter[];
}
@State<FilterStateModel>({
@@ -21,6 +21,7 @@ export class FilterStateModel {
defaults: {
filters: [],
processesSelectedFilters: [],
dropdownFilters: [],
},
})
export class FilterState {
@@ -33,7 +34,7 @@ export class FilterState {
@Selector()
static getFilterCount(state: FilterStateModel) {
return state.filters.length;
return state.dropdownFilters.length;
}
static getFilterIndex(index: number) {
@@ -41,21 +42,11 @@ export class FilterState {
return createSelector(
[FilterState],
(state: FilterStateModel) => {
return state.filters[val];
return state.dropdownFilters[val];
}
);
}
@Selector()
static getFiltersJSON(state: FilterStateModel) {
return JSON.stringify(state.filters.reduce((prev, curr) => [...curr.items, ...prev], []));
}
@Selector()
static getSelectedFilters(state: FilterStateModel) {
return state.filters.filter(f => f.items.find(i => i.selected === true));
}
@Action(actions.LoadFilters)
load(ctx: StateContext<FilterStateModel>) {
return this.filterService.getFilters().pipe(
@@ -82,6 +73,7 @@ export class FilterState {
}
ctx.patchState({
filters: [...newFilterState],
dropdownFilters: [...newFilterState.filter((fil: Filter) => fil.id !== 'cattype')],
});
})
);
@@ -113,6 +105,7 @@ export class FilterState {
}
ctx.patchState({
filters: [...newFilterState],
dropdownFilters: [...newFilterState.filter((fil: Filter) => fil.id !== 'cattype')],
});
})
);
@@ -120,11 +113,11 @@ export class FilterState {
@Action(actions.SelectFilterById)
selectFilterById(ctx: StateContext<FilterStateModel>, { id }: actions.SelectFilterById) {
const filters = ctx.getState().filters;
const filters = ctx.getState().dropdownFilters;
return this.filterService.selectFilterById(filters, id).pipe(
tap((filter: Filter[]) => {
ctx.patchState({
filters: filter,
dropdownFilters: filter,
});
})
);
@@ -132,11 +125,11 @@ export class FilterState {
@Action(actions.UnselectFilterById)
unselectFilterById(ctx: StateContext<FilterStateModel>, { id }: actions.UnselectFilterById) {
const filters = ctx.getState().filters;
const filters = ctx.getState().dropdownFilters;
return this.filterService.unselectFilterById(filters, id).pipe(
tap((filter: Filter[]) => {
ctx.patchState({
filters: [...filter],
dropdownFilters: filter,
});
})
);
@@ -144,11 +137,11 @@ export class FilterState {
@Action(actions.ToggleFilterItemById)
toggleItemById(ctx: StateContext<FilterStateModel>, { id }: actions.ToggleFilterItemById) {
const filters = ctx.getState().filters;
const filters = ctx.getState().dropdownFilters;
return this.filterService.toggleFilterItemsById(filters, id).pipe(
tap((filter: Filter[]) => {
ctx.patchState({
filters: [...filter],
dropdownFilters: [...filter],
});
})
);
@@ -156,7 +149,7 @@ export class FilterState {
@Action(actions.ToggleFilterItemByName)
toggleItemByName(ctx: StateContext<FilterStateModel>, { name }: actions.ToggleFilterItemByName) {
const filters = ctx.getState().filters;
const filters = ctx.getState().dropdownFilters;
return this.filterService.toggleFilterItemsByName(filters, name).pipe(
tap((filter: Filter[]) => {
let search = this.store.selectSnapshot(ProcessSelectors.getSearch);
@@ -165,7 +158,7 @@ export class FilterState {
this.store.dispatch(new AddSearch(search));
}
ctx.patchState({
filters: [...filter],
dropdownFilters: [...filter],
});
})
);

View File

@@ -13,6 +13,8 @@ import { AppSetCurrentProcess, AppDeleteProcess, AppAddProcess, AppUserDataSync
import { patch, append } from '@ngxs/store/operators';
import { CurrentCustomerPageLoaded } from '../actions/customer.actions';
import { UserStateSyncData } from '../../models/user-state-sync.model';
import { RadioButtonGroup } from '@libs/ui/lib/radio-button-group';
import { RadioButton } from '@libs/ui';
export interface ProcessStateModel {
processes: { [id: number]: Process };
@@ -47,6 +49,31 @@ export class ProcessState {
ordersOpenend: false,
closeDirectlyTab: false,
articleSearchErrorStatus: false,
productSearchFilters: {
archiveFilters: false,
radioButtonGroup: <RadioButtonGroup>{
radioButtons: [
<RadioButton>{
id: 1,
name: 'Autor',
key: 'author',
selected: false,
},
<RadioButton>{
id: 2,
name: 'Titel',
key: 'title',
selected: false,
},
<RadioButton>{
id: 3,
name: 'Verlag',
key: 'publisher',
selected: false,
},
],
},
},
},
};
ctx.patchState({ processes });
@@ -388,6 +415,20 @@ export class ProcessState {
this.syncApiState(currentProcessId, processes, recentArticles);
}
@Action(actions.SetProcessProductFilters)
updateProductFilters(ctx: StateContext<ProcessStateModel>, { filters }: actions.SetProcessProductFilters) {
const state = ctx.getState();
const currentProcesses = state.processes;
const recentArticles = state.recentArticles;
const currentProcessId = this.store.selectSnapshot(AppState.getCurrentProcessId);
const process = { ...currentProcesses[currentProcessId], productSearchFilters: filters };
ctx.setState(this.updateProcess(process));
let processes = { ...currentProcesses };
processes[process.id] = process;
this.syncApiState(currentProcessId, processes, recentArticles);
}
updateProcess<T extends Process>(process: T) {
if (process.id === undefined) {
return;

View File

@@ -0,0 +1,10 @@
<div class="radio-button-group-wrapper align-center" *ngIf="radioButtonGroup">
<div class="toggle">
<ui-switch (change)="archiveFilter($event)" color="#172062" [checked]="acrhiveFilter"></ui-switch>
<span [ngClass]="{ active: acrhiveFilter }">Archiv</span>
</div>
<div class="radio-button-group">
<lib-radio-button-group (clickRadio)="filterUpdate($event)" [radioButtonGroup]="radioButtonGroup"></lib-radio-button-group>
</div>
</div>

View File

@@ -0,0 +1,66 @@
.radio-button-group-wrapper {
display: flex;
align-items: center;
justify-content: center;
width: 500px;
margin: 0 auto;
.radio-button-group {
// width: 430px;
width: 350px;
margin-left: 20px;
}
.toggle {
display: flex;
justify-content: space-between;
align-items: center;
width: 110px;
ui-switch {
position: relative;
top: 3px;
}
span {
font-size: 16px;
color: #cccccc;
text-align: left;
line-height: 21px;
}
span.active {
color: #172062;
}
}
}
// Our stlyes for swith components
:host ::ng-deep ui-switch span {
background-color: transparent !important;
border: 2px solid #cccccc !important;
}
:host ::ng-deep ui-switch span.checked {
border: 2px solid #172062 !important;
background-color: #172062 !important;
}
:host ::ng-deep ui-switch .switch-medium {
height: 28px !important;
}
:host ::ng-deep ui-switch .switch-medium small {
width: 28px !important;
height: 28px !important;
top: -2px;
left: -2px;
box-shadow: none !important;
border: 2px solid #cccccc !important;
background-color: transparent !important;
}
:host ::ng-deep ui-switch span.checked small {
border: 2px solid #172062 !important;
background-color: #fff !important;
}

View File

@@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { InteractiveFiltersComponent } from './interactive-filters.component';
describe('InteractiveFiltersComponent', () => {
let component: InteractiveFiltersComponent;
let fixture: ComponentFixture<InteractiveFiltersComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ InteractiveFiltersComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(InteractiveFiltersComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,63 @@
import { Component, OnInit, ChangeDetectionStrategy, OnDestroy, ChangeDetectorRef, EventEmitter, Output } from '@angular/core';
import { RadioButtonGroup } from '@libs/ui/lib/radio-button-group';
import { Store, Select } from '@ngxs/store';
import { ProcessSelectors } from 'apps/sales/src/app/core/store/selectors/process.selectors';
import { Observable, Subject } from 'rxjs';
import { ProductSerachFilters } from 'apps/sales/src/app/core/models/product-search-filters.model';
import { filter, takeUntil } from 'rxjs/operators';
import { isNullOrUndefined } from 'util';
import { SetProcessProductFilters } from 'apps/sales/src/app/core/store/actions/process.actions';
@Component({
selector: 'app-interactive-filters',
templateUrl: './interactive-filters.component.html',
styleUrls: ['./interactive-filters.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class InteractiveFiltersComponent implements OnInit, OnDestroy {
@Select(ProcessSelectors.getProductFilters) filters$: Observable<ProductSerachFilters>;
destroy$ = new Subject();
radioButtonGroup: RadioButtonGroup;
acrhiveFilter: boolean;
@Output() filtersUpdated = new EventEmitter<ProductSerachFilters>();
constructor(private store: Store, private cdr: ChangeDetectorRef) {}
ngOnInit() {
this.filters$
.pipe(
filter(data => !isNullOrUndefined(data)),
takeUntil(this.destroy$)
)
.subscribe((filters: ProductSerachFilters) => {
this.radioButtonGroup = filters.radioButtonGroup;
this.acrhiveFilter = filters.archiveFilters;
this.cdr.detectChanges();
});
}
filterUpdate(_radioButtonGroup: RadioButtonGroup) {
this.radioButtonGroup = _radioButtonGroup;
const data = this.updateFiltersObject();
this.store.dispatch(new SetProcessProductFilters(data));
this.filtersUpdated.emit(data);
}
archiveFilter(active: boolean) {
this.acrhiveFilter = active;
const data = this.updateFiltersObject();
this.store.dispatch(new SetProcessProductFilters(data));
this.filtersUpdated.emit(data);
}
private updateFiltersObject(): ProductSerachFilters {
return {
radioButtonGroup: this.radioButtonGroup,
archiveFilters: this.acrhiveFilter,
};
}
ngOnDestroy(): void {
this.destroy$.next();
}
}

View File

@@ -1,4 +1,5 @@
<div class="result-container" *ngIf="!loading">
<app-interactive-filters (filtersUpdated)="filtersClicked($event)"></app-interactive-filters>
<app-filter (filtersChanged)="updateSearch()"></app-filter>
<div *ngIf="!ds || (ds.loading && !ds.results)">
<div [@stagger]="'yes'">
@@ -7,6 +8,7 @@
</div>
</div>
</div>
<div *ngIf="ds && !ds.loading && !loading && ds.cachedData.length === 0" class="no-results-text">Keine Suchergebnisse gefunden</div>
<cdk-virtual-scroll-viewport itemSize="190" class="viewport" #scroller>
<div *cdkVirtualFor="let product of ds; let i = index" class="product-item">
<app-product-card

View File

@@ -21,3 +21,13 @@ app-filter {
min-height: 130px;
overflow: hidden;
}
.no-results-text {
font-weight: 600;
font-size: 16px;
color: rgba(90, 114, 138, 1);
position: absolute;
top: 450px;
width: 100%;
text-align: center;
}

View File

@@ -10,11 +10,16 @@ import { Observable, Subject } from 'rxjs';
import { staggerAnimation } from './stagger.animation';
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
import { SearchDataSource } from './product-data.datasource';
import { takeUntil, filter } from 'rxjs/operators';
import { takeUntil, filter, take } from 'rxjs/operators';
import { SharedSelectors } from '../../../../core/store/selectors/shared.selectors';
import { ProcessSelectors } from 'apps/sales/src/app/core/store/selectors/process.selectors';
import { ProcessSelectors } from '../../../../core/store/selectors/process.selectors';
import { isNullOrUndefined } from 'util';
import { PRODUCT_SCROLL_INDEX } from 'apps/sales/src/app/core/utils/app.constants';
import { PRODUCT_SCROLL_INDEX } from '../../../../core/utils/app.constants';
import { FilterItem } from '../../../../core/models/filter-item.model';
import { Filter } from '../../../../core/models/filter.model';
import { ProductSerachFilters } from '../../../../core/models/product-search-filters.model';
import { RadioButtonGroup } from '@libs/ui/lib/radio-button-group';
import { SetProcessProductFilters } from '../../../../core/store/actions/process.actions';
@Component({
selector: 'app-product-results',
@@ -27,6 +32,7 @@ export class ProductResultsComponent implements OnInit, OnDestroy {
@Select(ProcessSelectors.getProcesses) processes$: Observable<Process[]>;
currentProcess: Process;
@Select(SharedSelectors.getSearchedProducts) products$: Observable<{ [key: number]: ItemDTO }>;
@Select(ProcessSelectors.getProductFilters) filters$: Observable<ProductSerachFilters>;
id: number;
firstload = 'false';
currentSearch: Search;
@@ -40,6 +46,9 @@ export class ProductResultsComponent implements OnInit, OnDestroy {
ds: SearchDataSource;
destroy$ = new Subject();
radioButtonGroup: RadioButtonGroup;
acrhiveFilter: boolean;
constructor(private store: Store, private productService: ProductService, private route: ActivatedRoute, private cdr: ChangeDetectorRef) {
this.route.queryParams.pipe(takeUntil(this.destroy$)).subscribe(params => (this.firstload = params['firstload']));
}
@@ -60,15 +69,27 @@ export class ProductResultsComponent implements OnInit, OnDestroy {
this.loadDataSource();
}
});
this.filters$
.pipe(
filter(data => !isNullOrUndefined(data)),
take(1)
)
.subscribe((filters: ProductSerachFilters) => {
this.radioButtonGroup = filters.radioButtonGroup;
this.acrhiveFilter = filters.archiveFilters;
});
}
processFilters() {}
loadDataSource() {
this.ds = new SearchDataSource(
this.id,
this.productService,
this.currentSearch,
this.store,
this.currentSearch && this.currentSearch.fitlers ? this.currentSearch.fitlers : []
this.currentSearch.fitlers ? this.currentSearch.fitlers : []
);
this.loading = false;
@@ -110,13 +131,17 @@ export class ProductResultsComponent implements OnInit, OnDestroy {
// // Needed for virtual scroller
this.loading = true;
// Select author filter in UI
this.selectAuthorFilter();
this.currentSearch = <Search>{
query: author,
fitlers: [],
fitlers: this.currentSearch.fitlers ? this.currentSearch.fitlers : [],
take: 5,
skip: 0,
firstLoad: true,
type: 'author',
archiveFilter: this.currentSearch.archiveFilter,
};
this.ds = new SearchDataSource(this.id, this.productService, this.currentSearch, this.store, []);
this.loading = false;
@@ -126,4 +151,67 @@ export class ProductResultsComponent implements OnInit, OnDestroy {
ngOnDestroy() {
this.destroy$.next();
}
filtersClicked(filtersData: ProductSerachFilters) {
this.loading = true;
this.radioButtonGroup = filtersData.radioButtonGroup;
this.acrhiveFilter = filtersData.archiveFilters;
const filters = this.activeFilters();
const exists: Search = {
query: this.currentSearch.query,
skip: 0,
take: 5,
fitlers: filters,
firstLoad: true,
type: this.radioButtonGroup.radioButtons.find(t => t.selected)
? this.radioButtonGroup.radioButtons.find(t => t.selected).key
: undefined,
archiveFilter: this.acrhiveFilter,
};
this.ds = new SearchDataSource(this.id, this.productService, exists, this.store, filters);
this.loading = false;
this.cdr.detectChanges();
}
private activeFilters(): Filter[] {
let filters: Filter[] = [];
if (this.currentSearch.fitlers) {
filters = this.currentSearch.fitlers.map((filter: Filter) => {
if (filter.id === 'cattype') {
let fil = {
...filter,
items: filter.items.map((filterItm: FilterItem) => {
if (filterItm.id === '2') {
let itm = { ...filterItm, selected: this.acrhiveFilter };
return itm;
}
return { ...filterItm, selected: false };
}),
};
return fil;
}
return filter;
});
}
return filters;
}
private selectAuthorFilter() {
let radioButtons = this.radioButtonGroup.radioButtons;
radioButtons = radioButtons.map(radio => {
if (radio.key === 'author') {
return { ...radio, selected: true };
}
return { ...radio, selected: false };
});
const updatedRadios: RadioButtonGroup = { radioButtons };
this.radioButtonGroup = updatedRadios;
this.store.dispatch(new SetProcessProductFilters({ radioButtonGroup: updatedRadios, archiveFilters: this.acrhiveFilter }));
}
}

View File

@@ -20,8 +20,7 @@ import { takeUntil } from 'rxjs/operators';
animations: [fadeInAnimation],
})
export class FilterItemComponent implements OnInit, OnDestroy {
@Input()
index: number;
@Input() index: number;
destroy$ = new Subject();
filter: Filter;

View File

@@ -6,11 +6,7 @@
<div class="align-center">
<span class="article-search-description">Welchen Artikel suchen Sie?</span>
</div>
<div class="radio-button-group-wrapper align-center">
<div class="radio-button-group">
<lib-radio-button-group (clickRadio)="filterUpdate($event)" [radioButtonGroup]="radioButtonGroup"></lib-radio-button-group>
</div>
</div>
<app-interactive-filters></app-interactive-filters>
<div class="align-center search-container">
<app-search
#searchInput

View File

@@ -121,15 +121,8 @@
}
}
.radio-button-group-wrapper {
app-interactive-filters {
padding-top: 35px;
display: flex;
align-items: center;
justify-content: center;
.radio-button-group {
width: 430px;
}
}
/*

View File

@@ -1,4 +1,4 @@
import { SearchInputComponent, RadioButton } from '@libs/ui';
import { SearchInputComponent } from '@libs/ui';
import { Observable, Subject } from 'rxjs';
import { Breadcrumb } from '../../../../../../core/models/breadcrumb.model';
import { Process } from '../../../../../../core/models/process.model';
@@ -19,17 +19,19 @@ import { Select, Store } from '@ngxs/store';
import { AutocompleteState } from '../../../../../../core/store/state/autocomplete.state';
import { LoadAutocomplete } from '../../../../../../core/store/actions/autocomplete.actions';
import { ProductService } from '../../../../../../core/services/product.service';
import { debounceTime, distinctUntilChanged, map, takeUntil } from 'rxjs/operators';
import { ItemDTO } from 'cat-service';
import { debounceTime, distinctUntilChanged, takeUntil, filter } from 'rxjs/operators';
import { fadeInAnimation } from './fadeIn.animation';
import { AddBreadcrumb } from '../../../../../../core/store/actions/breadcrumb.actions';
import { ProcessSelectors } from 'apps/sales/src/app/core/store/selectors/process.selectors';
import { ProcessSelectors } from '../../../../../../core/store/selectors/process.selectors';
import { RadioButtonGroup } from '@libs/ui/lib/radio-button-group';
import { AutocompleteResults } from 'apps/sales/src/app/core/models/autocomplete-results.model';
import { PRODUCT_SCROLL_INDEX } from 'apps/sales/src/app/core/utils/app.constants';
import { SharedSelectors } from 'apps/sales/src/app/core/store/selectors/shared.selectors';
import { AppState } from 'apps/sales/src/app/core/store/state/app.state';
import { AutocompleteResults } from '../../../../../../core/models/autocomplete-results.model';
import { PRODUCT_SCROLL_INDEX } from '../../../../../../core/utils/app.constants';
import { AppState } from '../../../../../../core/store/state/app.state';
import { isNullOrUndefined } from 'util';
import { ProductSerachFilters } from '../../../../../../core/models/product-search-filters.model';
import { FilterState } from '../../../../../../core/store/state/filter.state';
import { Filter } from '../../../../../../core/models/filter.model';
import { FilterItem } from '../../../../../../core/models/filter-item.model';
@Component({
selector: 'app-text-search',
@@ -39,6 +41,8 @@ import { isNullOrUndefined } from 'util';
})
export class TextSearchComponent implements OnInit, AfterViewInit, OnDestroy {
@Select(ProcessSelectors.getRecentProducts) recentArticles$: Observable<RecentArticleSearch[]>;
@Select(ProcessSelectors.getProductFilters) filters$: Observable<ProductSerachFilters>;
@Select(FilterState.getFilters) allFilters$: Observable<Filter[]>;
recentArticles: RecentArticleSearch[];
products$: Observable<Product[]>;
products: Product[];
@@ -55,28 +59,9 @@ export class TextSearchComponent implements OnInit, AfterViewInit, OnDestroy {
inputForProcess: { [id: number]: string } = [];
@Input()
searchParams = '';
radioButtonGroup = <RadioButtonGroup>{
radioButtons: [
<RadioButton>{
id: 1,
name: 'Autor',
key: 'author',
selected: false,
},
<RadioButton>{
id: 2,
name: 'Titel',
key: 'title',
selected: false,
},
<RadioButton>{
id: 3,
name: 'Verlag',
key: 'publisher',
selected: false,
},
],
};
radioButtonGroup: RadioButtonGroup;
acrhiveFilter: boolean;
allFilters: Filter[] = [];
get filters() {
const selectedFilter = this.radioButtonGroup.radioButtons.filter(t => t.selected === true);
@@ -94,6 +79,16 @@ export class TextSearchComponent implements OnInit, AfterViewInit, OnDestroy {
ngOnInit() {
this.loadRecentArticles();
this.filters$
.pipe(
filter(data => !isNullOrUndefined(data)),
takeUntil(this.destroy$)
)
.subscribe((filters: ProductSerachFilters) => {
this.radioButtonGroup = filters.radioButtonGroup;
this.acrhiveFilter = filters.archiveFilters;
});
this.searchInput.inputChange.subscribe(data => {
if (!data && this.error) {
this.updateInputForProcess(data);
@@ -101,6 +96,15 @@ export class TextSearchComponent implements OnInit, AfterViewInit, OnDestroy {
}
});
this.allFilters$
.pipe(
filter(data => !isNullOrUndefined(data)),
takeUntil(this.destroy$)
)
.subscribe((allFilters: Filter[]) => {
this.allFilters = allFilters;
});
this.store
.select(AppState.getCurrentProcessId)
.pipe(takeUntil(this.destroy$))
@@ -146,16 +150,19 @@ export class TextSearchComponent implements OnInit, AfterViewInit, OnDestroy {
}
private checkIfExistAndSumbit(searchParams: string) {
let filters = this.activeFilters();
const exists: Search = {
query: searchParams,
skip: 0,
take: 1,
fitlers: [],
fitlers: filters.length > 0 ? filters : [],
firstLoad: true,
type: this.radioButtonGroup.radioButtons.find(t => t.selected)
? this.radioButtonGroup.radioButtons.find(t => t.selected).key
: undefined,
hits: true,
archiveFilter: this.acrhiveFilter,
};
this.productService
@@ -180,15 +187,18 @@ export class TextSearchComponent implements OnInit, AfterViewInit, OnDestroy {
}
private submitSearch() {
let filters = this.activeFilters();
const search = <Search>{
query: this.searchParams,
fitlers: [],
fitlers: filters.length > 0 ? filters : [],
take: 5,
skip: 0,
firstLoad: true,
type: this.radioButtonGroup.radioButtons.find(t => t.selected)
? this.radioButtonGroup.radioButtons.find(t => t.selected).key
: undefined,
archiveFilter: this.acrhiveFilter,
};
this.store.dispatch(new AllowProductLoad());
@@ -284,7 +294,29 @@ export class TextSearchComponent implements OnInit, AfterViewInit, OnDestroy {
this.showAutocomplete = false;
}
filterUpdate(_radioButtonGroup: RadioButtonGroup) {
this.radioButtonGroup = _radioButtonGroup;
private activeFilters(): Filter[] {
let filters = [];
if (this.allFilters) {
filters = this.allFilters.map((filter: Filter) => {
if (filter.id === 'cattype') {
let fil = {
...filter,
items: filter.items.map((filterItm: FilterItem) => {
if (filterItm.id === '2') {
let itm = { ...filterItm, selected: this.acrhiveFilter };
return itm;
}
return { ...filterItm, selected: false };
}),
};
return fil;
}
return filter;
});
}
return filters;
}
}

View File

@@ -4,7 +4,7 @@
}
.content {
height: calc(100% - 110px);
height: calc(100% - 104px);
position: relative;
bottom: 1px;
}

View File

@@ -7,6 +7,7 @@ import { ActivatedRoute } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { Location } from '@angular/common';
import { LoadFullFilters } from '../../../../core/store/actions/filter.actions';
@Component({
selector: 'app-product-search',
@@ -25,6 +26,7 @@ export class ProductSearchComponent implements OnInit, OnDestroy {
constructor(private store: Store, private route: ActivatedRoute, private location: Location) {}
ngOnInit() {
this.store.dispatch(new LoadFullFilters());
this.route.queryParams.pipe(takeUntil(this.destroy$)).subscribe(params => {
if (params && params['path'] === 'scan') {
this.changeCards('scan');

View File

@@ -31,6 +31,8 @@ import { BookThumbnailPipe } from '../../pipes/book-thumbnail.pipe';
import { AvailabilityLoadingComponent } from './components/availability-loading/availability-loading.component';
import { ProductDetailsLoadingComponent } from './components/product-details-loading/product-details-loading.component';
import { ProductOtherFormatsComponent } from './components/product-other-formats/product-other-formats.component';
import { UiSwitchModule } from 'ngx-toggle-switch';
import { InteractiveFiltersComponent } from './components/interactive-filters/interactive-filters.component';
@NgModule({
imports: [
@@ -55,6 +57,7 @@ import { ProductOtherFormatsComponent } from './components/product-other-formats
RadioButtonGroupModule,
SearchDropdownModule,
PrinterSelectionModule,
UiSwitchModule,
],
declarations: [
ProductResultsComponent,
@@ -75,6 +78,7 @@ import { ProductOtherFormatsComponent } from './components/product-other-formats
AvailabilityLoadingComponent,
ProductDetailsLoadingComponent,
ProductOtherFormatsComponent,
InteractiveFiltersComponent,
],
exports: [],
providers: [BookImagePipe, BookThumbnailPipe, DescriptionTextPipe],

View File

@@ -3,14 +3,16 @@ import { PriceDTO } from './price-dto';
export interface AvailabilityRequestDTO {
price?: PriceDTO;
itemId?: string;
ean?: string;
qty: number;
orderCode?: string;
supplier?: string;
preBook?: boolean;
ean?: string;
supplierProductNumber?: string;
ssc?: string;
estimatedShipping?: string;
shopId?: number;
branchNumber?: string;
availabilityReference?: string;
name?: string;
}

View File

@@ -1,5 +1,6 @@
.radio-button-group {
display: flex;
align-items: center;
justify-content: space-evenly;
justify-content: space-between;
max-height: 40px;
}

View File

@@ -1,6 +1,6 @@
<div class="wrapper" *ngIf="radioButton" (click)="clickRadioButton()" [ngClass]="{ selected: radioButton.selected === true }">
<div class="img" [ngClass]="{ selected: radioButton.selected === true }">
<lib-icon name="Check_white"></lib-icon>
<lib-icon name="Check_white" *ngIf="radioButton.selected"></lib-icon>
</div>
<div class="text" [ngClass]="{ selected: radioButton.selected === true }">
<span>

View File

@@ -3,26 +3,28 @@
flex-direction: row;
padding: 6px 15px 6px 8px;
align-content: center;
border: 2px solid #dce2e9;
border: 2px solid #cccccc;
border-radius: 30px;
cursor: pointer;
.img {
border: 2px solid #dce2e9;
border-radius: 100px;
width: 23px;
border: 2px solid #cccccc;
border-radius: 100%;
width: 22px;
}
.text {
font-size: 16px;
font-weight: bold;
color: #dce2e9;
color: #cccccc;
padding-top: 3px;
padding-left: 10px;
position: relative;
bottom: 1px;
}
}
.selected {
background-color: #172062;
border-color: #172062;
color: #ffffff !important;
}

View File

@@ -39,6 +39,7 @@
"ng-circle-progress": "^1.4.0",
"ng-connection-service": "^1.0.4",
"ngx-infinite-scroll": "^7.0.1",
"ngx-toggle-switch": "^2.0.5",
"rxjs": "~6.4.0",
"scandit-sdk": "^4.1.1",
"scandit-sdk-angular": "^2.0.0",