Merge Store Changes from Main Branch

This commit is contained in:
Sebastian
2020-06-26 11:46:21 +02:00
11 changed files with 288 additions and 66 deletions

View File

@@ -13,7 +13,7 @@ export class BranchService {
constructor(private checkoutService: StoreCheckoutService, private orderService: OrderService, private ssoService: SsoService) {}
getCurrentBranchNumber() {
this.ssoService.getClaimByKey('branch_no');
return this.ssoService.getClaimByKey('branch_no');
}
searchBranches(input: string, skip: number, take: number): Observable<BranchDTO[]> {

View File

@@ -1,22 +1,15 @@
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import {
map,
switchMap,
filter,
startWith,
shareReplay,
withLatestFrom,
share,
} from 'rxjs/operators';
import { map, switchMap, filter, startWith } from 'rxjs/operators';
import { LocationService } from './location-service';
import { RemissionOverlayService } from '../../modules/remission/services';
import { Select } from '@ngxs/store';
import { RemissionSelectors } from '../store/selectors/remission.selectors';
import { SearchStateFacade } from '../../store/customer';
import { isNullOrUndefined } from 'util';
import { SharedSelectors } from '../store/selectors/shared.selectors';
import { ShelfOverlayService } from '../../modules/shelf/services';
import { RemissionSelectors } from '../store/selectors/remission.selectors';
import { SharedSelectors } from '../store/selectors/shared.selectors';
import { SearchStateFacade } from 'apps/sales/src/app/store/customer';
@Injectable({ providedIn: 'root' })
export class ContentHeaderService {

View File

@@ -0,0 +1,5 @@
export interface GroupedByCustomer<T> {
buyerNumber?: string;
fullName?: string;
items?: T[];
}

View File

@@ -1 +1,2 @@
export * from './search-process';
export * from './grouped-by-customer';

View File

@@ -1,4 +1,5 @@
import { ShelfPrimaryFilterOptions } from 'apps/sales/src/app/modules/shelf/defs';
import { OrderItemListItemDTO } from '@swagger/oms';
export interface SearchProcess {
id: number; // Prozess ID;
@@ -7,4 +8,7 @@ export interface SearchProcess {
selectedFilters?: { [key: string]: string[] };
primaryFilters?: ShelfPrimaryFilterOptions;
};
result: OrderItemListItemDTO[];
hits?: number;
fetching: boolean;
}

View File

@@ -1,5 +1,10 @@
import { createAction, props } from '@ngrx/store';
import { ShelfPrimaryFilterOptions } from 'apps/sales/src/app/modules/shelf/defs';
import {
OrderItemListItemDTO,
StrictHttpResponse,
ListResponseArgsOfOrderItemListItemDTO,
} from '@swagger/oms';
const prefix = '[CUSTOMER] [SHELF] [SEARCH]';
@@ -13,11 +18,6 @@ export const removeSearchProcess = createAction(
props<{ id: number }>()
);
export const setInput = createAction(
`${prefix} Set Input`,
props<{ id: number; input: string }>()
);
export const setSelectedFilters = createAction(
`${prefix} Set Selected Filters`,
props<{ id: number; filters: { [key: string]: string[] } }>()
@@ -32,3 +32,41 @@ export const setPrimaryFilters = createAction(
`${prefix} Set Primary Filters`,
props<{ id: number; filters: ShelfPrimaryFilterOptions }>()
);
export const setInput = createAction(
`${prefix} Search Input`,
props<{ id: number; input: string }>()
);
export const clearResults = createAction(
`${prefix} Clear Results`,
props<{ id: number }>()
);
export const addResult = createAction(
`${prefix} Add Result`,
props<{ id: number; result: OrderItemListItemDTO[] }>()
);
export const clearHits = createAction(
`${prefix} Clear Hits`,
props<{ id: number }>()
);
export const setHits = createAction(
`${prefix} Set Hits`,
props<{ id: number; hits: number }>()
);
export const fetchResult = createAction(
`${prefix} Fetch Result`,
props<{ id: number }>()
);
export const fetchResultDone = createAction(
`${prefix} Fetch Result Done`,
props<{
id: number;
response: StrictHttpResponse<ListResponseArgsOfOrderItemListItemDTO>;
}>()
);

View File

@@ -1,16 +1,66 @@
import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { Actions as NgxsActions, ofActionDispatched } from '@ngxs/store';
import { AddProcess, DeleteProcess } from 'apps/sales/src/app/core/store/actions/process.actions';
import * as actions from './search.actions';
import { switchMap, withLatestFrom, catchError, map, flatMap } from 'rxjs/operators';
import { OrderService, StrictHttpResponse, ListResponseArgsOfOrderItemListItemDTO } from '@swagger/oms';
import { BranchService } from '@sales/core-services';
import { SearchStateFacade } from './search.facade';
import { of, NEVER } from 'rxjs';
@Injectable()
export class SearchEffects {
constructor(private ngxsActions$: NgxsActions, private store: Store<any>) {
constructor(
private actions$: Actions,
private ngxsActions$: NgxsActions,
private store: Store<any>,
private searchStateFacade: SearchStateFacade,
private orderService: OrderService,
private branchService: BranchService
) {
this.initAddProcess();
this.initRemoveProcess();
}
fetchResults$ = createEffect(() =>
this.actions$.pipe(
ofType(actions.fetchResult),
switchMap((a) =>
of(a).pipe(
withLatestFrom(this.searchStateFacade.getProcess$(a.id)),
flatMap(([_, process]) =>
this.orderService
.OrderWarenausgabeResponse({
branchNumber: this.branchService.getCurrentBranchNumber(),
input: process.input,
skip: process.result.length || 0,
take: 20,
})
.pipe(
catchError((err) => of<StrictHttpResponse<ListResponseArgsOfOrderItemListItemDTO>>(err)),
map((response) => actions.fetchResultDone({ id: a.id, response }))
)
)
)
)
)
);
fetchResultDone$ = createEffect(() =>
this.actions$.pipe(
ofType(actions.fetchResultDone),
flatMap((action) => {
if (action.response.ok) {
const result = action.response.body;
return [actions.setHits({ id: action.id, hits: result.hits }), actions.addResult({ id: action.id, result: result.result })];
}
return NEVER;
})
)
);
initAddProcess() {
this.ngxsActions$.pipe(ofActionDispatched(AddProcess)).subscribe((action) => {
if (action instanceof AddProcess) {

View File

@@ -2,37 +2,109 @@ import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { Store as NgxsStore } from '@ngxs/store';
import * as actions from './search.actions';
import * as selectors from './search.selectors';
import { SharedSelectors } from 'apps/sales/src/app/core/store/selectors/shared.selectors';
import { map, first, take, switchMap, filter } from 'rxjs/operators';
import { from, Observable } from 'rxjs';
import {
selectSearchProcessById,
selectProcessPrimaryFiltersById,
} from './search.selectors';
import { SearchProcess } from './defs';
import { isNullOrUndefined } from 'util';
import { ShelfPrimaryFilterOptions } from 'apps/sales/src/app/modules/shelf/defs';
import { OrderItemListItemDTO } from '@swagger/oms';
import { GroupedByCustomer } from './defs';
@Injectable({ providedIn: 'root' })
export class SearchStateFacade {
get process$() {
return this.getProcessId$().pipe(switchMap((id) => this.getProcess$(id)));
}
get input$() {
return this.getProcessId$().pipe(switchMap((id) => this.getInput$(id)));
}
get result$() {
return this.getProcessId$().pipe(switchMap((id) => this.getResult$(id)));
}
get resultGroupedByCustomer$() {
return this.getProcessId$().pipe(
switchMap((id) => this.getResultGroupedByCustomer$(id))
);
}
get hits$() {
return this.getProcessId$().pipe(switchMap((id) => this.getHits$(id)));
}
get fetching$() {
return this.getProcessId$().pipe(switchMap((id) => this.getFetching$(id)));
}
constructor(private store: Store<any>, private ngxsStore: NgxsStore) {}
getProcessId$() {
return this.ngxsStore.select(SharedSelectors.getCurrentProcess).pipe(
filter((process) => !!process && !!process.id),
map((currentProcess) => currentProcess.id)
);
}
getProcessId() {
return this.ngxsStore
.select(SharedSelectors.getCurrentProcess)
.pipe(
filter((currentProcess) => !isNullOrUndefined(currentProcess)),
map((currentProcess) => currentProcess.id),
first()
return this.getProcessId$().pipe(first()).toPromise();
}
getProcess$(id: number) {
return this.store.select(selectors.selectProcess, id);
}
getInput$(id: number) {
return this.store.select(selectors.selectInput, id);
}
getResult$(id: number) {
return this.store.select(selectors.selectResult, id);
}
getResultGroupedByCustomer$(
id: number
): Observable<GroupedByCustomer<OrderItemListItemDTO>[]> {
return this.getResult$(id).pipe(
map((result) =>
result.reduce((acc, item) => {
const existing = acc.findIndex(
(ex) => ex.buyerNumber === item.buyerNumber
);
if (existing === -1) {
return [
...acc,
{
buyerNumber: item.buyerNumber,
fullName: `${item.firstName} ${item.lastName}`,
items: [item],
},
];
} else {
acc[existing].items.push(item);
}
return acc;
}, [] as GroupedByCustomer<OrderItemListItemDTO>[])
)
.toPromise();
);
}
getHits$(id: number) {
return this.store.select(selectors.selectHits, id);
}
getFetching$(id: number) {
return this.store.select(selectors.selectFetching, id);
}
get currentSearchProcess$(): Observable<SearchProcess> {
return from(this.getProcessId()).pipe(
take(1),
switchMap((processId) =>
this.store.select(selectSearchProcessById, processId)
this.store.select(selectors.selectProcess, processId)
)
);
}
@@ -105,6 +177,22 @@ export class SearchStateFacade {
private getCurrentPrimaryFilters(
processId: number
): Observable<ShelfPrimaryFilterOptions> {
return this.store.select(selectProcessPrimaryFiltersById, processId);
return this.store.select(selectors.selectPrimaryFilters, processId);
}
async fetchResult(id?: number) {
let processId = id;
if (typeof processId !== 'number') {
processId = await this.getProcessId();
}
this.store.dispatch(actions.fetchResult({ id: processId }));
}
async clearResult(id?: number) {
let processId = id;
if (typeof processId !== 'number') {
processId = await this.getProcessId();
}
this.store.dispatch(actions.clearResults({ id: processId }));
}
}

View File

@@ -3,14 +3,14 @@ import {
INITIAL_SEARCH_STATE,
SearchState,
searchStateAdapter,
INITIAL_FILTERS,
INITIAL_SEARCH_PROCESS,
} from './search.state';
import * as actions from './search.actions';
const _searchReducer = createReducer(
INITIAL_SEARCH_STATE,
on(actions.addSearchProcess, (s, a) =>
searchStateAdapter.addOne({ id: a.id, filters: INITIAL_FILTERS }, s)
searchStateAdapter.addOne({ id: a.id, ...INITIAL_SEARCH_PROCESS }, s)
),
on(actions.removeSearchProcess, (s, a) =>
searchStateAdapter.removeOne(a.id, s)
@@ -42,6 +42,36 @@ const _searchReducer = createReducer(
{ id: a.id, changes: { filters: { primaryFilters: a.filters } } },
s
)
),
on(actions.addSearchProcess, (s, a) =>
searchStateAdapter.addOne({ ...INITIAL_SEARCH_PROCESS, id: a.id }, s)
),
on(actions.removeSearchProcess, (s, a) =>
searchStateAdapter.removeOne(a.id, s)
),
on(actions.clearResults, (s, a) =>
searchStateAdapter.updateOne({ id: a.id, changes: { result: [] } }, s)
),
on(actions.addResult, (s, a) =>
searchStateAdapter.updateOne(
{
id: a.id,
changes: { result: [...s.entities[a.id].result, ...a.result] },
},
s
)
),
on(actions.clearHits, (s, a) =>
searchStateAdapter.updateOne({ id: a.id, changes: { hits: undefined } }, s)
),
on(actions.setHits, (s, a) =>
searchStateAdapter.updateOne({ id: a.id, changes: { hits: a.hits } }, s)
),
on(actions.fetchResult, (s, a) =>
searchStateAdapter.updateOne({ id: a.id, changes: { fetching: true } }, s)
),
on(actions.fetchResultDone, (s, a) =>
searchStateAdapter.updateOne({ id: a.id, changes: { fetching: false } }, s)
)
);

View File

@@ -1,55 +1,60 @@
import { createSelector } from '@ngrx/store';
import { selectShelfState } from '../shelf.selectors';
import { searchStateAdapter } from './search.state';
import { Dictionary } from '@ngrx/entity';
import { SearchProcess } from './defs';
import { ShelfPrimaryFilterOptions } from 'apps/sales/src/app/modules/shelf/defs';
export const selectSearchState = createSelector(
selectShelfState,
(s) => s.search
);
export const { selectAll } = searchStateAdapter.getSelectors();
export const { selectAll, selectEntities } = searchStateAdapter.getSelectors();
export const selectAllSearchProcesses = createSelector(
selectSearchState,
selectAll
);
export const selectSearchProcessById = createSelector(
selectAllSearchProcesses,
(s: SearchProcess[], processId: number) =>
s.find((searchProcess) => searchProcess.id === processId)
export const selectProcess = createSelector(
selectEntities,
(entities: Dictionary<SearchProcess>, id: number) => entities[id]
);
export const selectSearchProcessFiltersById = createSelector(
selectAllSearchProcesses,
(s: SearchProcess[], processId: number) => {
const searchProcess = s.find((p) => p.id === processId);
if (searchProcess) {
return searchProcess.filters;
}
}
export const selectInput = createSelector(
selectEntities,
(entities: Dictionary<SearchProcess>, id: number) => entities[id].input
);
export const selectProcessSelectedFiltersById = createSelector(
selectAllSearchProcesses,
(s: SearchProcess[], processId: number) => {
const searchProcess = s.find((p) => p.id === processId);
if (searchProcess && searchProcess.filters) {
return searchProcess.filters.selectedFilters;
}
}
export const selectResult = createSelector(
selectEntities,
(entities: Dictionary<SearchProcess>, id: number) => entities[id].result
);
export const selectProcessPrimaryFiltersById = createSelector(
selectAllSearchProcesses,
(s: SearchProcess[], processId: number) => {
const searchProcess = s.find((p) => p.id === processId);
if (searchProcess && searchProcess.filters) {
return searchProcess.filters.primaryFilters;
}
}
export const selectHits = createSelector(
selectEntities,
(entities: Dictionary<SearchProcess>, id: number) => entities[id].hits
);
export const selectFetching = createSelector(
selectEntities,
(entities: Dictionary<SearchProcess>, id: number) => entities[id].fetching
);
export const selectFilters = createSelector(
selectEntities,
(entities: Dictionary<SearchProcess>, id: number) => entities[id].filters
);
export const selectSelectedFilters = createSelector(
selectEntities,
(entities: Dictionary<SearchProcess>, id: number) =>
entities[id].filters.selectedFilters
);
export const selectPrimaryFilters = createSelector(
selectEntities,
(entities: Dictionary<SearchProcess>, id: number) =>
entities[id].filters.primaryFilters
);

View File

@@ -24,3 +24,11 @@ export const INITIAL_FILTERS: {
selectedFilters: {},
primaryFilters: INITIAL_PRIMARY_FILTERS,
};
export const INITIAL_SEARCH_PROCESS: SearchProcess = {
id: undefined,
result: [],
input: 'Müller',
fetching: false,
filters: INITIAL_FILTERS,
};