mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
[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:
@@ -3,4 +3,5 @@ import { Filter } from './filter.model';
|
||||
export interface ProcessSelectedFilters {
|
||||
processId: number;
|
||||
selectedFilters: Filter[];
|
||||
dropdownFilters: Filter[];
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import { RadioButtonGroup } from '@libs/ui/lib/radio-button-group';
|
||||
|
||||
export interface ProductSerachFilters {
|
||||
radioButtonGroup: RadioButtonGroup;
|
||||
archiveFilters: boolean;
|
||||
}
|
||||
@@ -8,4 +8,5 @@ export interface Search {
|
||||
firstLoad: boolean;
|
||||
type?: string;
|
||||
hits?: boolean;
|
||||
archiveFilter: boolean;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,6 +154,7 @@ export class SharedSelectors {
|
||||
filter.push(f);
|
||||
}
|
||||
});
|
||||
|
||||
return filter;
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -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],
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 }));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
}
|
||||
|
||||
.content {
|
||||
height: calc(100% - 110px);
|
||||
height: calc(100% - 104px);
|
||||
position: relative;
|
||||
bottom: 1px;
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
.radio-button-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
justify-content: space-between;
|
||||
max-height: 40px;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user