mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
Merge develop to feature/HIMA-15
This commit is contained in:
@@ -107,7 +107,7 @@ export const filterMock: Filter[] = <Filter[]>[
|
||||
},
|
||||
<FilterItem> {
|
||||
id: 2,
|
||||
name: 'Deutsche',
|
||||
name: 'Deutsch',
|
||||
selected: false
|
||||
}
|
||||
],
|
||||
|
||||
11706
package-lock.json
generated
11706
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -22,6 +22,7 @@
|
||||
"@ngxs/store": "^3.3.4",
|
||||
"angular2-signaturepad": "^2.8.0",
|
||||
"core-js": "^2.5.4",
|
||||
"ngx-infinite-scroll": "^7.0.1",
|
||||
"rxjs": "~6.3.3",
|
||||
"tslib": "^1.9.0",
|
||||
"zone.js": "~0.8.26"
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import { TestBed, getTestBed } from '@angular/core/testing';
|
||||
import { CatAvailabilityService } from './cat-availability.service';
|
||||
import { HttpTestingController, HttpClientTestingModule } from '@angular/common/http/testing';
|
||||
import { CAT_AV_SERVICE_ENDPOINT } from './tokens';
|
||||
import { AvailabilityRequestDTO } from './dtos';
|
||||
|
||||
|
||||
describe('CatAvailabilityService', () => {
|
||||
const endpoint = 'https://fake-endpoint.de';
|
||||
let injector: TestBed;
|
||||
let service: CatAvailabilityService;
|
||||
let httpMock: HttpTestingController;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [HttpClientTestingModule],
|
||||
providers: [
|
||||
CatAvailabilityService,
|
||||
{ provide: CAT_AV_SERVICE_ENDPOINT, useValue: endpoint }
|
||||
]
|
||||
});
|
||||
|
||||
injector = getTestBed();
|
||||
service = injector.get(CatAvailabilityService);
|
||||
httpMock = injector.get(HttpTestingController);
|
||||
});
|
||||
|
||||
describe('#get', () => {
|
||||
it('should issue a POST request to /ola/availability', () => {
|
||||
service.get([]).subscribe();
|
||||
|
||||
const req = httpMock.expectOne({ method: 'POST', url: `${endpoint}/ola/availability` });
|
||||
});
|
||||
|
||||
it('should have the argument in the request body', () => {
|
||||
const av: AvailabilityRequestDTO[] = [{ ean: '123456789', qty: 1, supplier: 'L', shopId: 1 }];
|
||||
service.get(av).subscribe();
|
||||
const req = httpMock.expectOne({ method: 'POST', url: `${endpoint}/ola/availability` });
|
||||
expect(req.request.body).toBe(av);
|
||||
});
|
||||
|
||||
it('should throw an Error if the validation fails', () => {
|
||||
const av: AvailabilityRequestDTO[] = [null];
|
||||
|
||||
service.get(av).subscribe(
|
||||
() => { console.log('fsag'); },
|
||||
(error) => {
|
||||
expect(error).toBeDefined();
|
||||
}
|
||||
);
|
||||
|
||||
httpMock.expectNone({ method: 'POST', url: `${endpoint}/ola/availability` });
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
38
projects/cat-service/src/lib/cat-availability.service.ts
Normal file
38
projects/cat-service/src/lib/cat-availability.service.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { Injectable, Inject } from '@angular/core';
|
||||
import { AvailabilityRequestDTO, ArticleAvailabilityDTO } from './dtos';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { CAT_AV_SERVICE_ENDPOINT } from './tokens';
|
||||
import { Observable, isObservable, throwError, } from 'rxjs';
|
||||
import { ApiResponse } from './response';
|
||||
import { availabilityRequestValidator } from './validators';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class CatAvailabilityService {
|
||||
|
||||
endpoint = '';
|
||||
|
||||
constructor(
|
||||
private http: HttpClient,
|
||||
@Inject(CAT_AV_SERVICE_ENDPOINT) endpoint: string | Observable<string>
|
||||
) {
|
||||
if (isObservable(endpoint)) {
|
||||
endpoint.subscribe(e => this.endpoint = e);
|
||||
} else {
|
||||
this.endpoint = endpoint;
|
||||
}
|
||||
}
|
||||
|
||||
get(req: AvailabilityRequestDTO[]): Observable<ApiResponse<ArticleAvailabilityDTO[]>> {
|
||||
|
||||
for (const result of req.map(item => availabilityRequestValidator(item))) {
|
||||
if (!result.valid) {
|
||||
return throwError(new Error(result.message));
|
||||
}
|
||||
}
|
||||
|
||||
return this.http.post<ApiResponse<ArticleAvailabilityDTO[]>>(
|
||||
`${this.endpoint}/ola/availability`,
|
||||
req
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,8 @@ import { CAT_SERVICE_ENDPOINT } from './tokens';
|
||||
import { QueryTokenDTO, AutocompleteTokenDTO } from './dtos/query';
|
||||
import { UISettingsDTO } from './dtos/ui';
|
||||
import { shareReplay } from 'rxjs/operators';
|
||||
import { DEFAULT_QUERY_TOKEN_DTO } from './defaults/query-token-dto';
|
||||
|
||||
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
@@ -36,7 +38,7 @@ export class CatSearchService {
|
||||
return this.http
|
||||
.post<PagedApiResponse<ItemDTO>>(
|
||||
`${this.endpoint}/s/top`,
|
||||
query
|
||||
{ ...DEFAULT_QUERY_TOKEN_DTO, ...query }
|
||||
);
|
||||
}
|
||||
|
||||
@@ -50,7 +52,7 @@ export class CatSearchService {
|
||||
return this.http
|
||||
.post<PagedApiResponse<ItemDTO>>(
|
||||
`${this.endpoint}/s`,
|
||||
query
|
||||
{ ...DEFAULT_QUERY_TOKEN_DTO, ...query }
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
5
projects/cat-service/src/lib/defaults/query-token-dto.ts
Normal file
5
projects/cat-service/src/lib/defaults/query-token-dto.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { QueryTokenDTO } from '../dtos';
|
||||
|
||||
export const DEFAULT_QUERY_TOKEN_DTO: Partial<QueryTokenDTO> = {
|
||||
take: 10
|
||||
};
|
||||
@@ -0,0 +1,84 @@
|
||||
import { PriceDTO } from './price.dto';
|
||||
import { AvailabilityType } from '../../enums';
|
||||
|
||||
export interface ArticleAvailabilityDTO {
|
||||
/**
|
||||
* Produkt / Artikel PK
|
||||
*/
|
||||
itemId?: number;
|
||||
|
||||
/**
|
||||
* Eindeutige Referenz zur Zuordnung
|
||||
*/
|
||||
requestReference: string;
|
||||
|
||||
/**
|
||||
* EAN
|
||||
*/
|
||||
ean: string;
|
||||
|
||||
/**
|
||||
* Shop Id
|
||||
*/
|
||||
shop?: number;
|
||||
|
||||
/**
|
||||
* Price
|
||||
*/
|
||||
price: PriceDTO;
|
||||
|
||||
/**
|
||||
* Lieferant
|
||||
*/
|
||||
supplier: string;
|
||||
|
||||
/**
|
||||
* Stock Status Code / Meldeschlüssel
|
||||
*/
|
||||
ssc: string;
|
||||
|
||||
/**
|
||||
* Verfügbare Menge
|
||||
*/
|
||||
qty?: number;
|
||||
|
||||
/**
|
||||
* Vorgemerkt
|
||||
*/
|
||||
isPrebooked?: boolean;
|
||||
|
||||
/**
|
||||
* Voraussichtliches Lieferdatum
|
||||
*/
|
||||
at?: Date;
|
||||
|
||||
/**
|
||||
* Alternatives Voraussichtliches Lieferdatum
|
||||
*/
|
||||
altAt?: Date;
|
||||
|
||||
/**
|
||||
* Verfügbarkeitsstatus
|
||||
*/
|
||||
availabilityType: AvailabilityType;
|
||||
|
||||
/**
|
||||
* Rang
|
||||
*/
|
||||
preferred?: number;
|
||||
|
||||
/**
|
||||
* Zeitstemple der Anfrage
|
||||
*/
|
||||
requested?: Date;
|
||||
|
||||
/**
|
||||
* StatusCode der Verfügbarkeitsanfrage
|
||||
*/
|
||||
requestStatusCode: string;
|
||||
|
||||
/**
|
||||
* Beschreibung des StatusCode
|
||||
*/
|
||||
requestMessage: string;
|
||||
}
|
||||
@@ -1,10 +1,12 @@
|
||||
// start:ng42.barrel
|
||||
export * from './article-availability.dto';
|
||||
export * from './availability.dto';
|
||||
export * from './item.dto';
|
||||
export * from './price-value.dto';
|
||||
export * from './price.dto';
|
||||
export * from './product.dto';
|
||||
export * from './shelf-info.dto';
|
||||
export * from './shop.dto';
|
||||
export * from './size.dto';
|
||||
export * from './spec.dto';
|
||||
export * from './stock-info.dto';
|
||||
|
||||
11
projects/cat-service/src/lib/dtos/data/shop.dto.ts
Normal file
11
projects/cat-service/src/lib/dtos/data/shop.dto.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export interface ShopDTO {
|
||||
/**
|
||||
* PK
|
||||
*/
|
||||
id?: number;
|
||||
|
||||
/**
|
||||
* Name / Bezeichner
|
||||
*/
|
||||
name: string;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import { PriceDTO } from '../data';
|
||||
|
||||
export interface AvailabilityRequestDTO {
|
||||
/**
|
||||
* Artikel / Produkt PK
|
||||
*/
|
||||
itemId?: string;
|
||||
|
||||
/**
|
||||
* EAN
|
||||
*/
|
||||
ean?: string;
|
||||
|
||||
/**
|
||||
* Menge / Stück
|
||||
*/
|
||||
qty: number;
|
||||
|
||||
/**
|
||||
* Bestellzeichen
|
||||
*/
|
||||
orderCode?: string;
|
||||
|
||||
/**
|
||||
* Lieferant
|
||||
*/
|
||||
supplier: string;
|
||||
|
||||
/**
|
||||
* Bestellung vormerken
|
||||
*/
|
||||
preBook?: boolean;
|
||||
|
||||
/**
|
||||
* Preis
|
||||
*/
|
||||
price?: PriceDTO;
|
||||
|
||||
/**
|
||||
* Stock Status Code
|
||||
*/
|
||||
ssc?: string;
|
||||
|
||||
/**
|
||||
* Vsl. Lieferdatum
|
||||
*/
|
||||
estimatedShipping?: Date;
|
||||
|
||||
/**
|
||||
* Shop PK
|
||||
*/
|
||||
shopId: number;
|
||||
|
||||
/**
|
||||
* Zuordnungs-ID
|
||||
*/
|
||||
availabilityReference?: string;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
// start:ng42.barrel
|
||||
export * from './autocomplete-token.dto';
|
||||
export * from './availability-request.dto';
|
||||
export * from './query-token.dto';
|
||||
// end:ng42.barrel
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// start:ng42.barrel
|
||||
export * from './cat-image.service';
|
||||
export * from './cat-availability.service';
|
||||
export * from './cat-search-mock.data';
|
||||
export * from './cat-search-mock.service';
|
||||
export * from './cat-search.service';
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// start:ng42.barrel
|
||||
export * from './dictionary.model';
|
||||
export * from './valiation-result.model';
|
||||
// end:ng42.barrel
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import { StringDictionary } from './dictionary.model';
|
||||
|
||||
export class ValidationResult {
|
||||
private _valid = true;
|
||||
get valid() { return this._valid; }
|
||||
private _message?: string;
|
||||
get message() { return this._message; }
|
||||
private _invalidProperties?: StringDictionary<string>;
|
||||
get invalidProperties() { return this._invalidProperties; }
|
||||
|
||||
setMessage(message: string) {
|
||||
this._message = message;
|
||||
this._valid = false;
|
||||
}
|
||||
|
||||
addPropertyError(proeprty: string, reason: string) {
|
||||
const ip = this.invalidProperties || {};
|
||||
|
||||
ip[proeprty] = reason;
|
||||
|
||||
this._invalidProperties = ip;
|
||||
|
||||
if (this.message == null) {
|
||||
this.setMessage('Invalid property.');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,3 +2,4 @@ import { InjectionToken } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
export const CAT_SERVICE_ENDPOINT = new InjectionToken<string | Observable<string>>('cat:service:endpoint');
|
||||
export const CAT_AV_SERVICE_ENDPOINT = new InjectionToken<string | Observable<string>>('cat:av:service:endpoint');
|
||||
|
||||
19
projects/cat-service/src/lib/utils/common.spec.ts
Normal file
19
projects/cat-service/src/lib/utils/common.spec.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { isNil } from './common';
|
||||
|
||||
describe('utils common isNil', () => {
|
||||
it('should return true if parameter is null', () => {
|
||||
expect(isNil(null)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should return true if parameter is undefined', () => {
|
||||
expect(isNil(undefined)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should return false if parameter is an empty string', () => {
|
||||
expect(isNil('')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should return false if parameter is 0', () => {
|
||||
expect(isNil(0)).toBeFalsy();
|
||||
});
|
||||
});
|
||||
3
projects/cat-service/src/lib/utils/common.ts
Normal file
3
projects/cat-service/src/lib/utils/common.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export function isNil(val: any): boolean {
|
||||
return val == null;
|
||||
}
|
||||
5
projects/cat-service/src/lib/utils/index.ts
Normal file
5
projects/cat-service/src/lib/utils/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
// start:ng42.barrel
|
||||
export * from './common';
|
||||
export * from './string';
|
||||
// end:ng42.barrel
|
||||
|
||||
23
projects/cat-service/src/lib/utils/string.spec.ts
Normal file
23
projects/cat-service/src/lib/utils/string.spec.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { stringIsNilOrEmpty } from './string';
|
||||
|
||||
describe('utils common stringIsNilOrEmpty', () => {
|
||||
it('should return true if the parameter is null', () => {
|
||||
expect(stringIsNilOrEmpty(null)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should return true if the parameter is undefined', () => {
|
||||
expect(stringIsNilOrEmpty(undefined)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should return true if the parameter is an empty string', () => {
|
||||
expect(stringIsNilOrEmpty('')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should return true if the parameter is a string with whitespaces', () => {
|
||||
expect(stringIsNilOrEmpty(' ')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should return false if the parameter is a string with content', () => {
|
||||
expect(stringIsNilOrEmpty(' Hello World ')).toBeFalsy();
|
||||
});
|
||||
});
|
||||
10
projects/cat-service/src/lib/utils/string.ts
Normal file
10
projects/cat-service/src/lib/utils/string.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { isNil } from './common';
|
||||
|
||||
export function stringIsNilOrEmpty(value: string) {
|
||||
if (isNil(value)) {
|
||||
return true;
|
||||
} else if (typeof value === 'string' && value.trim() === '') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { availabilityRequestValidator } from './availability-request.validator';
|
||||
|
||||
describe('Validators availabilityRequestValidator', () => {
|
||||
it('should return an invalid ValidationResult when ean and itemId is empty', () => {
|
||||
const result = availabilityRequestValidator({ qty: 1, shopId: 1, supplier: 'L' });
|
||||
expect(result.valid).toBeFalsy();
|
||||
expect(result.invalidProperties['itemId']).toBeDefined();
|
||||
expect(result.invalidProperties['ean']).toBeDefined();
|
||||
});
|
||||
|
||||
it('should return an invalid ValidationResult when qty is less than 0', () => {
|
||||
const result = availabilityRequestValidator({ itemId: '123', qty: 0, shopId: 1, supplier: 'L' });
|
||||
expect(result.valid).toBeFalsy();
|
||||
expect(result.invalidProperties['qty']).toBeDefined();
|
||||
});
|
||||
|
||||
it('should return an invalid ValidationResult when shopId is less than 0', () => {
|
||||
const result = availabilityRequestValidator({ ean: '123', qty: 1, shopId: 0, supplier: 'L' });
|
||||
expect(result.valid).toBeFalsy();
|
||||
expect(result.invalidProperties['shopId']).toBeDefined();
|
||||
});
|
||||
|
||||
it('should return an invalid ValidationResult when supplier is empty', () => {
|
||||
const result = availabilityRequestValidator({ itemId: '123', qty: 1, shopId: 1, supplier: '' });
|
||||
expect(result.valid).toBeFalsy();
|
||||
expect(result.invalidProperties['supplier']).toBeDefined();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,32 @@
|
||||
import { AvailabilityRequestDTO } from '../dtos';
|
||||
import { ValidationResult, StringDictionary } from '../models';
|
||||
import { stringIsNilOrEmpty } from '../utils/string';
|
||||
import { isNil } from '../utils';
|
||||
|
||||
export function availabilityRequestValidator(av: AvailabilityRequestDTO): ValidationResult {
|
||||
|
||||
const result = new ValidationResult();
|
||||
|
||||
if (isNil(av)) {
|
||||
result.setMessage('AvailabilityRequestDTO is null or undefined.');
|
||||
} else {
|
||||
if (stringIsNilOrEmpty(av.itemId) && stringIsNilOrEmpty(av.ean)) {
|
||||
result.addPropertyError('itemId', 'Either itemId or ean must be set.');
|
||||
result.addPropertyError('ean', 'Either itemId or ean must be set.');
|
||||
}
|
||||
|
||||
if (!(av.qty > 0)) {
|
||||
result.addPropertyError('qty', 'Property qty must be grater than 0.');
|
||||
}
|
||||
|
||||
if (stringIsNilOrEmpty(av.supplier)) {
|
||||
result.addPropertyError('supplier', 'Property supplier is required.');
|
||||
}
|
||||
|
||||
if (!(av.shopId > 0)) {
|
||||
result.addPropertyError('shopId', `Value ${av.shopId} is not valid.`);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
4
projects/cat-service/src/lib/validators/index.ts
Normal file
4
projects/cat-service/src/lib/validators/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
// start:ng42.barrel
|
||||
export * from './availability-request.validator';
|
||||
// end:ng42.barrel
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Public API Surface of cat-service
|
||||
*/
|
||||
export * from './lib/cat-image.service';
|
||||
export * from './lib/cat-availability.service';
|
||||
export * from './lib/cat-search-mock.data';
|
||||
export * from './lib/cat-search-mock.service';
|
||||
export * from './lib/cat-search.service';
|
||||
|
||||
@@ -7,7 +7,7 @@ import { ComponentsModule } from './modules/components.module';
|
||||
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
|
||||
import { BasicAuthorizationInterceptor, BasicAuthorizationOptions } from './core/interceptors';
|
||||
import { SearchResultsComponent } from './components/search-results/search-results.component';
|
||||
import { CatServiceModule, CAT_SERVICE_ENDPOINT, CatSearchService } from 'cat-service';
|
||||
import { CatServiceModule, CAT_SERVICE_ENDPOINT, CatSearchService, CAT_AV_SERVICE_ENDPOINT } from 'cat-service';
|
||||
import { ProductCardComponent } from './components/product-card/product-card.component';
|
||||
import { ConfigService } from './core/services/config.service';
|
||||
import { ProductDetailsComponent } from './components/product-details/product-details.component';
|
||||
@@ -17,7 +17,6 @@ import { NgxsReduxDevtoolsPluginModule } from '@ngxs/devtools-plugin';
|
||||
import { NgxsLoggerPluginModule } from '@ngxs/logger-plugin';
|
||||
import { FeedState } from './core/store/state/feed.state';
|
||||
import { ProcessState } from './core/store/state/process.state';
|
||||
import { ProductsState } from './core/store/state/products.state';
|
||||
import { environment } from 'src/environments/environment';
|
||||
import { BreadcrumbsState } from './core/store/state/breadcrumbs.state';
|
||||
import { FilterState } from './core/store/state/filter.state';
|
||||
@@ -30,7 +29,6 @@ import { CustomerSearchResultComponent } from './components/customer-search-resu
|
||||
const states = [
|
||||
FeedState,
|
||||
ProcessState,
|
||||
ProductsState,
|
||||
BreadcrumbsState,
|
||||
FilterState
|
||||
];
|
||||
@@ -45,7 +43,11 @@ export function _basicAuthorizationInterceptorFactory(conf: ConfigService) {
|
||||
}
|
||||
|
||||
export function _catServiceEndpointProviderFactory(conf: ConfigService) {
|
||||
return conf.select<string>('catService', 'endpoint');
|
||||
return conf.select<string>('catService', 'endpoint', 'catService');
|
||||
}
|
||||
|
||||
export function _catAvServiceEndpointProviderFactory(conf: ConfigService) {
|
||||
return conf.select<string>('catService', 'endpoint', 'avService');
|
||||
}
|
||||
|
||||
export function _feedServiceEndpointProviderFactory(conf: ConfigService) {
|
||||
@@ -79,6 +81,7 @@ export function _feedServiceEndpointProviderFactory(conf: ConfigService) {
|
||||
{ provide: APP_INITIALIZER, useFactory: _configInitializer, multi: true, deps: [ ConfigService ] },
|
||||
{ provide: HTTP_INTERCEPTORS, useFactory: _basicAuthorizationInterceptorFactory, deps: [ConfigService], multi: true },
|
||||
{ provide: CAT_SERVICE_ENDPOINT, useFactory: _catServiceEndpointProviderFactory, deps: [ConfigService] },
|
||||
{ provide: CAT_AV_SERVICE_ENDPOINT, useFactory: _catAvServiceEndpointProviderFactory, deps: [ConfigService] },
|
||||
{ provide: FEED_SERVICE_ENDPOINT, useFactory: _feedServiceEndpointProviderFactory, deps: [ConfigService] },
|
||||
// { provide: CatSearchService, useClass: CatSearchMockService }, // Uncomment if u want to use the CatSearchMockService
|
||||
{ provide: FeedService, useClass: FeedMockService } // Uncomment if u want to use the FeedMockService
|
||||
|
||||
@@ -10,8 +10,8 @@ import { Process } from 'src/app/core/models/process.model';
|
||||
import { getRandomPic } from 'src/app/core/utils/process.util';
|
||||
import { Breadcrumb } from 'src/app/core/models/breadcrumb.model';
|
||||
import { ProcessState } from 'src/app/core/store/state/process.state';
|
||||
import { AddProcess, ChangeCurrentRoute, AddSearch } from 'src/app/core/store/actions/process.actions';
|
||||
import { ProductsState } from 'src/app/core/store/state/products.state';
|
||||
import { AddProcess, ChangeCurrentRoute, AddSearch} from 'src/app/core/store/actions/process.actions';
|
||||
import { AllowProductLoad } from 'src/app/core/store/actions/process.actions';
|
||||
import { LoadRecentProducts } from 'src/app/core/store/actions/product.actions';
|
||||
|
||||
@Component({
|
||||
@@ -21,7 +21,7 @@ import { LoadRecentProducts } from 'src/app/core/store/actions/product.actions';
|
||||
})
|
||||
export class ArticleSearchComponent implements OnInit {
|
||||
|
||||
@Select(ProductsState.getRecentProducts) recentArticles$: Observable<RecentArticleSearch[]>;
|
||||
@Select(ProcessState.getRecentProducts) recentArticles$: Observable<RecentArticleSearch[]>;
|
||||
recentArticles: RecentArticleSearch[];
|
||||
products$: Observable<Product[]>;
|
||||
products: Product[];
|
||||
@@ -42,10 +42,14 @@ export class ArticleSearchComponent implements OnInit {
|
||||
this.loadSelectedFilters();
|
||||
const search = <Search>{
|
||||
query: this.searchParams,
|
||||
fitlers: this.filters
|
||||
fitlers: this.filters,
|
||||
take: 5,
|
||||
skip: 0,
|
||||
firstLoad: true
|
||||
};
|
||||
this.store.dispatch(new AllowProductLoad());
|
||||
this.store.dispatch(new AddSearch(search));
|
||||
this.navigateToRoute('search-results');
|
||||
this.navigateToRoute('search-results#start');
|
||||
}
|
||||
|
||||
navigateToRoute(route: string) {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Component, OnInit, Input } from '@angular/core';
|
||||
import { Process } from 'src/app/core/models/process.model';
|
||||
import { Router } from '@angular/router';
|
||||
import { Store } from '@ngxs/store';
|
||||
import { DeleteProcess, SelectProcess } from 'src/app/core/store/actions/process.actions';
|
||||
import { DeleteProcess, SelectProcess, PreventProductLoad } from 'src/app/core/store/actions/process.actions';
|
||||
import { Cart } from '../../core/models/cart.model';
|
||||
|
||||
@Component({
|
||||
@@ -27,6 +27,7 @@ export class ProcessTabComponent implements OnInit {
|
||||
|
||||
selectProcess(process: Process): void {
|
||||
this.store.dispatch(new SelectProcess(process));
|
||||
this.store.dispatch(new PreventProductLoad());
|
||||
this.router.navigate([process.currentRoute]);
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
</div>
|
||||
<div class="publisher-order">
|
||||
<div class="publisher-serial">
|
||||
<div class="publisher align-left">
|
||||
<div class="publisher align-left wrap-text-more">
|
||||
<span>{{product.publisher}}</span>
|
||||
</div>
|
||||
<div class="align-left">
|
||||
|
||||
@@ -3,8 +3,13 @@ import { Product } from 'src/app/core/models/product.model';
|
||||
import { Router } from '@angular/router';
|
||||
import { CatImageService } from 'cat-service';
|
||||
import { ReplaySubject, Observable, of } from 'rxjs';
|
||||
import { flatMap, catchError } from 'rxjs/operators';
|
||||
import { flatMap, catchError, filter, map } from 'rxjs/operators';
|
||||
import { getProductTypeIcon } from 'src/app/core/utils/product.util';
|
||||
import { ItemDTO } from 'projects/cat-service/src/lib';
|
||||
import { Select, Store } from '@ngxs/store';
|
||||
import { AddSelectedProduct } from 'src/app/core/store/actions/product.actions';
|
||||
import { ChangeCurrentRoute } from 'src/app/core/store/actions/process.actions';
|
||||
import { ProcessState } from 'src/app/core/store/state/process.state';
|
||||
|
||||
@Component({
|
||||
selector: 'app-product-card',
|
||||
@@ -14,6 +19,7 @@ import { getProductTypeIcon } from 'src/app/core/utils/product.util';
|
||||
export class ProductCardComponent implements OnInit {
|
||||
|
||||
private _product: Product;
|
||||
@Select(ProcessState.getProducts) items$: Observable<ItemDTO[]>;
|
||||
@Input()
|
||||
get product() { return this._product; }
|
||||
set product(val) {
|
||||
@@ -29,7 +35,11 @@ export class ProductCardComponent implements OnInit {
|
||||
|
||||
productTypeIcon: string;
|
||||
|
||||
constructor(private router: Router, private catImageService: CatImageService) {
|
||||
constructor(
|
||||
private router: Router,
|
||||
private catImageService: CatImageService,
|
||||
private store: Store
|
||||
) {
|
||||
this.imageUrl$ = this.eanChangedSub.pipe(
|
||||
flatMap(ean => this.catImageService.getImageUrl(ean)),
|
||||
catchError(() => of(''))
|
||||
@@ -37,7 +47,20 @@ export class ProductCardComponent implements OnInit {
|
||||
}
|
||||
|
||||
productDetails(product: Product) {
|
||||
this.router.navigate(['product-details/' + product.id]);
|
||||
// TODO: this is temporary solution for the incostency of product detail API
|
||||
this.items$.pipe(
|
||||
map(item => {
|
||||
if (item) {
|
||||
return item.find(i => i.id === product.id);
|
||||
}
|
||||
})
|
||||
).subscribe(
|
||||
(data: ItemDTO) => this.store.dispatch(new AddSelectedProduct(data))
|
||||
);
|
||||
|
||||
const currentRoute = 'product-details/' + product.id;
|
||||
this.store.dispatch(new ChangeCurrentRoute(currentRoute));
|
||||
this.router.navigate([currentRoute]);
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
|
||||
@@ -147,11 +147,9 @@
|
||||
width: 149px;
|
||||
padding: 12px;
|
||||
cursor: pointer;
|
||||
//position: absolute;
|
||||
//bottom: 25px;
|
||||
|
||||
|
||||
&-active, &:hover {
|
||||
&-active {
|
||||
background-color: #f70400;
|
||||
border: none;
|
||||
border-radius: 25px;
|
||||
|
||||
@@ -4,8 +4,11 @@ import { CheckoutComponent } from '../checkout/checkout.component';
|
||||
import { ProductService } from 'src/app/core/services/product.service';
|
||||
import { ItemDTO, CatImageService } from 'cat-service';
|
||||
import { Observable } from 'rxjs';
|
||||
import { monthNames, getFormatedPublicationDate } from 'src/app/core/utils/product.util';
|
||||
import { publishBehavior } from 'rxjs/operators';
|
||||
import { getFormatedPublicationDate } from 'src/app/core/utils/product.util';
|
||||
import { ProcessState, ProcessStateModel } from 'src/app/core/store/state/process.state';
|
||||
import { Select } from '@ngxs/store';
|
||||
import { stateNameErrorMessage } from '@ngxs/store/src/decorators/state';
|
||||
import { Process } from 'src/app/core/models/process.model';
|
||||
|
||||
@Component({
|
||||
selector: 'app-product-details',
|
||||
@@ -17,6 +20,8 @@ export class ProductDetailsComponent implements OnInit {
|
||||
|
||||
id: number;
|
||||
item: ItemDTO;
|
||||
selectedItem: ItemDTO;
|
||||
@Select(ProcessState.getSelectedProduct) selectedItem$: Observable<ItemDTO>;
|
||||
|
||||
readonly FULL_DESCRIPTION_LABEL = 'Klappentext';
|
||||
readonly AUTOR = 'Autor';
|
||||
@@ -55,6 +60,9 @@ export class ProductDetailsComponent implements OnInit {
|
||||
return this.product = this.productDetailMapper(item);
|
||||
}
|
||||
);
|
||||
this.selectedItem$.subscribe(
|
||||
(data: ItemDTO) => this.selectedItem = data
|
||||
);
|
||||
}
|
||||
|
||||
productDetailMapper(item: ItemDTO) {
|
||||
@@ -77,12 +85,12 @@ export class ProductDetailsComponent implements OnInit {
|
||||
// product object mapping
|
||||
if (item.pr) {
|
||||
ean = item.pr.ean;
|
||||
eanTag = 'EAN ' + ean;
|
||||
eanTag = ean;
|
||||
productIcon$ = this.catImageService.getImageUrl(ean, { width: 469, height: 575});
|
||||
locale = item.pr.locale;
|
||||
publicationDate = getFormatedPublicationDate(item.pr.publicationDate);
|
||||
format = item.pr.formatDetail;
|
||||
formatIcon = item.pr.format;
|
||||
format = this.selectedItem ? this.selectedItem.pr.formatDetail : null;
|
||||
formatIcon = this.selectedItem ? this.selectedItem.pr.format : null;
|
||||
category = item.pr.productGroup;
|
||||
publisher = item.pr.manufacturer;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<div class="result-container">
|
||||
<app-filter></app-filter>
|
||||
<app-product-card *ngFor="let product of products"
|
||||
[product]="product">
|
||||
</app-product-card>
|
||||
<div id="start" infiniteScroll [infiniteScrollDistance]="1" [infiniteScrollThrottle]="50" (scrolled)="onScroll()" [scrollWindow]="true">
|
||||
<app-product-card *ngFor="let product of products" [product]="product">
|
||||
</app-product-card>
|
||||
</div>
|
||||
</div>
|
||||
@@ -6,10 +6,11 @@ import { Router } from '@angular/router';
|
||||
import { ProductMapping } from '../../core/mappings/product.mapping';
|
||||
import { map, filter } from 'rxjs/operators';
|
||||
import { Select, Store } from '@ngxs/store';
|
||||
import { ProductsState } from 'src/app/core/store/state/products.state';
|
||||
import { ItemDTO } from 'dist/cat-service/lib/dtos';
|
||||
import { Observable } from 'rxjs';
|
||||
import { GetProducts } from 'src/app/core/store/actions/product.actions';
|
||||
import { ProcessState } from 'src/app/core/store/state/process.state';
|
||||
import { AddSearch, AllowProductLoad } from 'src/app/core/store/actions/process.actions';
|
||||
|
||||
@Component({
|
||||
selector: 'app-search-results',
|
||||
@@ -20,7 +21,8 @@ export class SearchResultsComponent implements OnInit {
|
||||
|
||||
currentSearch: Search;
|
||||
products: Product[];
|
||||
@Select(ProductsState.getProducts) products$: Observable<ItemDTO[]>;
|
||||
@Select(ProcessState.getProducts) products$: Observable<ItemDTO[]>;
|
||||
skip = 0;
|
||||
|
||||
constructor(
|
||||
private store: Store,
|
||||
@@ -34,7 +36,7 @@ export class SearchResultsComponent implements OnInit {
|
||||
this.router.navigate(['dashboard']);
|
||||
return;
|
||||
}
|
||||
this.store.dispatch(new GetProducts(this.currentSearch.query));
|
||||
this.store.dispatch(new GetProducts(this.currentSearch));
|
||||
this.loadProducts();
|
||||
}
|
||||
|
||||
@@ -50,6 +52,14 @@ export class SearchResultsComponent implements OnInit {
|
||||
);
|
||||
}
|
||||
|
||||
onScroll() {
|
||||
this.store.dispatch(new AllowProductLoad());
|
||||
this.skip = this.skip + 5;
|
||||
const search = { ...this.currentSearch, firstLoad: false, skip: this.skip};
|
||||
this.store.dispatch(new GetProducts(search));
|
||||
this.store.dispatch(new AddSearch(search));
|
||||
}
|
||||
|
||||
loadProducts() {
|
||||
this.products$.pipe(
|
||||
filter(f => Array.isArray(f)),
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Breadcrumb } from './breadcrumb.model';
|
||||
import { Search } from './search.model';
|
||||
import { User } from './user.model';
|
||||
import { Cart } from './cart.model';
|
||||
import { ItemDTO } from 'cat-service';
|
||||
|
||||
export interface Process {
|
||||
id: number;
|
||||
@@ -12,5 +13,8 @@ export interface Process {
|
||||
breadcrumbs: Breadcrumb[];
|
||||
search: Search;
|
||||
users: User[];
|
||||
cart: Cart[]
|
||||
cart: Cart[];
|
||||
itemsDTO: ItemDTO[];
|
||||
selectedItem: ItemDTO;
|
||||
preventLoading: boolean;
|
||||
}
|
||||
|
||||
@@ -2,5 +2,8 @@ import { Filter } from './filter.model';
|
||||
|
||||
export interface Search {
|
||||
query: string;
|
||||
skip: number;
|
||||
take: number;
|
||||
fitlers: Filter[];
|
||||
firstLoad: boolean;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
PagedApiResponse
|
||||
} from 'cat-service';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { Search } from '../models/search.model';
|
||||
|
||||
|
||||
@Injectable({
|
||||
@@ -61,10 +62,12 @@ export class ProductService {
|
||||
}
|
||||
|
||||
// service method for calling product search API
|
||||
searchItems(searchTerm: string): Observable<ItemDTO[]> {
|
||||
this.persistLastSearchToLocalStorage(searchTerm);
|
||||
searchItems(search: Search): Observable<ItemDTO[]> {
|
||||
this.persistLastSearchToLocalStorage(search.query);
|
||||
const queryToken = <QueryTokenDTO> {
|
||||
input: {qs: searchTerm}
|
||||
input: {qs: search.query},
|
||||
skip: search.skip,
|
||||
take: search.take
|
||||
};
|
||||
|
||||
return this.searchService.search(queryToken).pipe(
|
||||
|
||||
@@ -19,7 +19,7 @@ export class UserService {
|
||||
) {
|
||||
return user;
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
return of(filteredUsers);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ export const ADD_SEARCH = '[PROCESS] Add search';
|
||||
export const SEARCH_USER = '[PROCESS] Search for user';
|
||||
export const SET_USER_NAME = '[PROCESS] Set the users name in tab';
|
||||
export const SET_CART = '[PROCESS] Set cart data for user';
|
||||
export const PREVENT_PRODUCT_LOAD = '[POCESS] Prevent product load';
|
||||
export const ALLOW_PRODUCT_LOAD = '[POCESS] Allow product load';
|
||||
|
||||
export class AddProcess {
|
||||
static readonly type = ADD_PROCESS;
|
||||
@@ -59,3 +61,11 @@ export class SetCartData {
|
||||
|
||||
constructor(public quantity: number, public payload: ItemDTO, public breadcrumb: Breadcrumb) {}
|
||||
}
|
||||
|
||||
export class PreventProductLoad {
|
||||
static readonly type = PREVENT_PRODUCT_LOAD;
|
||||
}
|
||||
|
||||
export class AllowProductLoad {
|
||||
static readonly type = ALLOW_PRODUCT_LOAD;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { ItemDTO } from 'projects/cat-service/src/lib';
|
||||
import { Search } from '../../models/search.model';
|
||||
|
||||
export const LOAD_RECENT_PRODUCTS = '[PRODUCTS] Load recent';
|
||||
export const GET_PRODUCTS = '[PRODUCTS] Get';
|
||||
export const ADD_SELECTED_PRODUCT = '[PRODUCTS] Add selected';
|
||||
|
||||
export class LoadRecentProducts {
|
||||
static readonly type = LOAD_RECENT_PRODUCTS;
|
||||
@@ -10,5 +14,11 @@ export class LoadRecentProducts {
|
||||
export class GetProducts {
|
||||
static readonly type = GET_PRODUCTS;
|
||||
|
||||
constructor(public payload: string) {}
|
||||
constructor(public payload: Search) {}
|
||||
}
|
||||
|
||||
export class AddSelectedProduct {
|
||||
static readonly type = ADD_SELECTED_PRODUCT;
|
||||
|
||||
constructor(public payload: ItemDTO) {}
|
||||
}
|
||||
|
||||
@@ -5,26 +5,53 @@ import { UserService } from '../../services/user.service';
|
||||
import { User } from '../../models/user.model';
|
||||
import { Breadcrumb } from '../../models/breadcrumb.model';
|
||||
import { Cart } from '../../models/cart.model';
|
||||
import { ProductService } from '../../services/product.service';
|
||||
import { RecentArticleSearch } from '../../models/recent-article-search.model';
|
||||
import { GetProducts, LoadRecentProducts, AddSelectedProduct } from '../actions/product.actions';
|
||||
import { ItemDTO } from 'dist/cat-service/lib/dtos';
|
||||
import { getCurrentProcess } from '../../utils/process.util';
|
||||
|
||||
export class ProcessStateModel {
|
||||
processes: Process[];
|
||||
recentArticles: RecentArticleSearch[];
|
||||
}
|
||||
|
||||
@State<ProcessStateModel>({
|
||||
name: 'processes',
|
||||
defaults: {
|
||||
processes: []
|
||||
processes: [],
|
||||
recentArticles: []
|
||||
}
|
||||
})
|
||||
export class ProcessState {
|
||||
|
||||
constructor(private usersService: UserService) { }
|
||||
constructor(private usersService: UserService, protected productService: ProductService) { }
|
||||
|
||||
@Selector()
|
||||
static getState(state: ProcessStateModel) {
|
||||
return state;
|
||||
}
|
||||
|
||||
@Selector()
|
||||
static getProcesses(state: ProcessStateModel) {
|
||||
return state.processes;
|
||||
}
|
||||
|
||||
@Selector()
|
||||
static getRecentProducts(state: ProcessStateModel) {
|
||||
return state.recentArticles;
|
||||
}
|
||||
|
||||
@Selector()
|
||||
static getProducts(state: ProcessStateModel) {
|
||||
return state.processes.find(t => t.selected === true).itemsDTO;
|
||||
}
|
||||
|
||||
@Selector()
|
||||
static getSelectedProduct(state: ProcessStateModel) {
|
||||
return state.processes.find(t => t.selected === true).selectedItem;
|
||||
}
|
||||
|
||||
@Selector()
|
||||
static getCurrentProcess(state: ProcessStateModel) {
|
||||
const currenProcces = state.processes.map(
|
||||
@@ -265,7 +292,7 @@ export class ProcessState {
|
||||
}
|
||||
|
||||
@Action(actions.SetCartData)
|
||||
setCartData(ctx: StateContext<ProcessStateModel>, { quantity , payload, breadcrumb }: actions.SetCartData) {
|
||||
setCartData(ctx: StateContext<ProcessStateModel>, { quantity, payload, breadcrumb }: actions.SetCartData) {
|
||||
const state = ctx.getState();
|
||||
const newProcessState = state.processes.map(
|
||||
(process: Process) => {
|
||||
@@ -281,11 +308,11 @@ export class ProcessState {
|
||||
book: payload
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
return item;
|
||||
});
|
||||
|
||||
return {
|
||||
return {
|
||||
...process,
|
||||
breadcrumbs: [
|
||||
breadcrumb
|
||||
@@ -296,7 +323,7 @@ export class ProcessState {
|
||||
};
|
||||
} else {
|
||||
// Add new item to cart
|
||||
return {
|
||||
return {
|
||||
...process,
|
||||
breadcrumbs: [
|
||||
breadcrumb
|
||||
@@ -320,4 +347,121 @@ export class ProcessState {
|
||||
processes: [...newProcessState]
|
||||
});
|
||||
}
|
||||
|
||||
@Action(GetProducts)
|
||||
getProducts(ctx: StateContext<ProcessStateModel>, { payload }: GetProducts) {
|
||||
const state = ctx.getState();
|
||||
if (!state.processes) {
|
||||
return;
|
||||
}
|
||||
const currentProcess = getCurrentProcess(state.processes);
|
||||
if (currentProcess.search === payload
|
||||
&& currentProcess.itemsDTO
|
||||
&& currentProcess.itemsDTO.length > 0
|
||||
&& currentProcess.preventLoading
|
||||
) {
|
||||
ctx.patchState({
|
||||
...state
|
||||
});
|
||||
} else {
|
||||
this.productService.searchItems(payload).subscribe(
|
||||
(items: ItemDTO[]) => {
|
||||
if (items) {
|
||||
ctx.patchState({
|
||||
...state,
|
||||
processes: payload.skip === 0 ?
|
||||
this.changeProducResultsForCurrentProcess(state.processes, items)
|
||||
: this.extendProducResultsForCurrentProcess(state.processes, items)
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Action(LoadRecentProducts)
|
||||
loadRecentProducts(ctx: StateContext<ProcessStateModel>) {
|
||||
const state = ctx.getState();
|
||||
this.productService.getRecentSearches().subscribe(
|
||||
(products: RecentArticleSearch[]) => {
|
||||
if (products) {
|
||||
ctx.patchState({
|
||||
...state,
|
||||
recentArticles: products.reverse().slice(0, 5)
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Action(AddSelectedProduct)
|
||||
AddSelectedProduct(ctx: StateContext<ProcessStateModel>, { payload }: AddSelectedProduct) {
|
||||
const state = ctx.getState();
|
||||
ctx.patchState({
|
||||
...state,
|
||||
processes: this.changeSelectedItemForCurrentProcess(state.processes, payload)
|
||||
});
|
||||
}
|
||||
|
||||
@Action(actions.PreventProductLoad)
|
||||
preventProductLoad(ctx: StateContext<ProcessStateModel>) {
|
||||
const state = ctx.getState();
|
||||
ctx.patchState({
|
||||
...state,
|
||||
processes: state.processes.map(
|
||||
process => {
|
||||
if (process.selected === true) {
|
||||
return {...process, preventLoading: true};
|
||||
}
|
||||
return {...process};
|
||||
}
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
@Action(actions.AllowProductLoad)
|
||||
allowProductLoad(ctx: StateContext<ProcessStateModel>) {
|
||||
const state = ctx.getState();
|
||||
ctx.patchState({
|
||||
...state,
|
||||
processes: state.processes.map(
|
||||
process => {
|
||||
if (process.selected === true) {
|
||||
return {...process, preventLoading: false};
|
||||
}
|
||||
return {...process};
|
||||
}
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
changeSelectedItemForCurrentProcess(processes: Process[], item: ItemDTO): Process[] {
|
||||
const newProcessState = processes.map(process => {
|
||||
if (process.selected === true) {
|
||||
return { ...process, selectedItem: item };
|
||||
}
|
||||
return { ...process };
|
||||
});
|
||||
return newProcessState;
|
||||
}
|
||||
|
||||
changeProducResultsForCurrentProcess(processes: Process[], items: ItemDTO[]): Process[] {
|
||||
const newProcessState = processes.map(process => {
|
||||
if (process.selected === true) {
|
||||
return { ...process, itemsDTO: items };
|
||||
}
|
||||
return { ...process };
|
||||
});
|
||||
return newProcessState;
|
||||
}
|
||||
|
||||
extendProducResultsForCurrentProcess(processes: Process[], items: ItemDTO[]): Process[] {
|
||||
const newProcessState = processes.map(process => {
|
||||
if (process.selected === true) {
|
||||
return { ...process, itemsDTO: [...process.itemsDTO, ...items] };
|
||||
}
|
||||
return { ...process };
|
||||
});
|
||||
return newProcessState;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
import { RecentArticleSearch } from '../../models/recent-article-search.model';
|
||||
import { State, Selector, Action, StateContext } from '@ngxs/store';
|
||||
import { LoadRecentProducts, GetProducts } from '../actions/product.actions';
|
||||
import { ProductService } from '../../services/product.service';
|
||||
import { ItemDTO } from 'cat-service';
|
||||
|
||||
export class ProductsStateModel {
|
||||
recentArticles: RecentArticleSearch[];
|
||||
itemsDTO: ItemDTO[];
|
||||
}
|
||||
|
||||
@State<ProductsStateModel>({
|
||||
name: 'products',
|
||||
defaults: {
|
||||
recentArticles: [],
|
||||
itemsDTO: []
|
||||
}
|
||||
})
|
||||
export class ProductsState {
|
||||
|
||||
constructor(private productService: ProductService) {}
|
||||
|
||||
@Selector()
|
||||
static getRecentProducts(state: ProductsStateModel) {
|
||||
return state.recentArticles;
|
||||
}
|
||||
|
||||
@Selector()
|
||||
static getProducts(state: ProductsStateModel) {
|
||||
return state.itemsDTO;
|
||||
}
|
||||
|
||||
@Action(LoadRecentProducts)
|
||||
loadRecentProducts(ctx: StateContext<ProductsStateModel>) {
|
||||
const state = ctx.getState();
|
||||
this.productService.getRecentSearches().subscribe(
|
||||
(products: RecentArticleSearch[]) => {
|
||||
if (products) {
|
||||
ctx.patchState({
|
||||
...state,
|
||||
recentArticles: products.reverse().slice(0, 5)
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Action(GetProducts)
|
||||
GetProducts(ctx: StateContext<ProductsStateModel>, { payload }: GetProducts) {
|
||||
const state = ctx.getState();
|
||||
this.productService.searchItems(payload).subscribe(
|
||||
(items: ItemDTO[]) => {
|
||||
if (items) {
|
||||
ctx.patchState({
|
||||
...state,
|
||||
itemsDTO: items
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,3 +1,9 @@
|
||||
import { Process } from '../models/process.model';
|
||||
|
||||
export function getRandomPic() {
|
||||
return 'Pic_' + (Math.floor(Math.random() * 6) + 1) + '-3x';
|
||||
}
|
||||
|
||||
export function getCurrentProcess(processes: Process[]): Process {
|
||||
return processes.find(t => t.selected === true);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import { RecommandationCardComponent } from '../components/recommandation-card/r
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { FilterComponent } from '../components/filter/filter.component';
|
||||
import { FilterItemComponent } from '../components/filter-item/filter-item.component';
|
||||
import { InfiniteScrollModule } from 'ngx-infinite-scroll';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@@ -40,6 +41,7 @@ import { FilterItemComponent } from '../components/filter-item/filter-item.compo
|
||||
AppRoutingModule,
|
||||
FormsModule,
|
||||
NewsletterSignupModule
|
||||
InfiniteScrollModule
|
||||
],
|
||||
exports: [
|
||||
HeaderComponent,
|
||||
@@ -55,7 +57,8 @@ import { FilterItemComponent } from '../components/filter-item/filter-item.compo
|
||||
NewsCardComponent,
|
||||
RecommandationCardComponent,
|
||||
FilterComponent,
|
||||
FilterItemComponent
|
||||
FilterItemComponent,
|
||||
InfiniteScrollModule
|
||||
]
|
||||
})
|
||||
export class ComponentsModule {}
|
||||
|
||||
@@ -13,7 +13,7 @@ export const routes: Routes = [
|
||||
{ path: 'article-search', component: ArticleSearchComponent },
|
||||
{ path: 'customer-search', component: CustomerSearchComponent },
|
||||
{ path: 'customer-search-result', component: CustomerSearchResultComponent },
|
||||
{ path: 'search-results', component: SearchResultsComponent },
|
||||
{ path: 'search-results#start', component: SearchResultsComponent },
|
||||
{ path: 'product-details/:id', component: ProductDetailsComponent },
|
||||
{ path: 'newsletter', component: NewsletterSignupComponent }
|
||||
];
|
||||
|
||||
@@ -3,11 +3,15 @@
|
||||
"client": "eu6YYrF3NB4CtxMTwrgC",
|
||||
"password": "rf5f9JUzKW7cjwd6vb6YHv2L2knEZ6m4mNsbpLMF",
|
||||
"endpoints": [
|
||||
"https://catsearch.paragon-data.de"
|
||||
"https://catsearch.paragon-data.de",
|
||||
"https://ava.paragon-data.de"
|
||||
]
|
||||
},
|
||||
"catService": {
|
||||
"endpoint": "https://catsearch.paragon-data.de"
|
||||
"endpoint": {
|
||||
"catService": "https://catsearch.paragon-data.de",
|
||||
"avService": "https://ava.paragon-data.de"
|
||||
}
|
||||
},
|
||||
"feedService": {
|
||||
"endpoint": "https://isa.paragon-data.de"
|
||||
|
||||
@@ -4379,6 +4379,13 @@ ng-packagr@^4.2.0:
|
||||
uglify-js "^3.0.7"
|
||||
update-notifier "^2.3.0"
|
||||
|
||||
ngx-infinite-scroll@^7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/ngx-infinite-scroll/-/ngx-infinite-scroll-7.0.1.tgz#59472108f5b6960519c269d6997fe3ae0961be07"
|
||||
integrity sha512-be9DAAuabV7VGI06/JUnS6pXC6mcBOzA4+SBCwOcP9WwJ2r5GjdZyOa34ls9hi1MnCOj3zrXLvPKQ/UDp6csIw==
|
||||
dependencies:
|
||||
opencollective "^1.0.3"
|
||||
|
||||
nice-try@^1.0.4:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
|
||||
|
||||
Reference in New Issue
Block a user