mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
Merge branch 'develop' into feature/ISA-9-Filiale-Taetigkeitskalender
This commit is contained in:
18
.npmrc
18
.npmrc
@@ -1,13 +1,13 @@
|
||||
registry=https://pkgs.dev.azure.com/hugendubel/_packaging/hugendubel/npm/registry/
|
||||
registry=https://pkgs.dev.azure.com/hugendubel/_packaging/hugendubel%40Local/npm/registry/
|
||||
|
||||
; Treat this auth token like a password. Do not share it with anyone, including Microsoft support. This token expires on or before 24.05.2020.
|
||||
always-auth=true
|
||||
; Treat this auth token like a password. Do not share it with anyone, including Microsoft support. This token expires on or before 25.08.2020.
|
||||
; begin auth token
|
||||
//pkgs.dev.azure.com/hugendubel/_packaging/hugendubel/npm/registry/:username=hugendubel
|
||||
//pkgs.dev.azure.com/hugendubel/_packaging/hugendubel/npm/registry/:_password=dG5yYTJ2bnBtbnZyeDZmeHJqc2N4bGV4aDRxd2xvZHplbzducGo3YzNyeDRkeGppZmRtYQ==
|
||||
//pkgs.dev.azure.com/hugendubel/_packaging/hugendubel/npm/registry/:email=npm requires email to be set but doesn't use the value
|
||||
//pkgs.dev.azure.com/hugendubel/_packaging/hugendubel/npm/:username=hugendubel
|
||||
//pkgs.dev.azure.com/hugendubel/_packaging/hugendubel/npm/:_password=dG5yYTJ2bnBtbnZyeDZmeHJqc2N4bGV4aDRxd2xvZHplbzducGo3YzNyeDRkeGppZmRtYQ==
|
||||
//pkgs.dev.azure.com/hugendubel/_packaging/hugendubel/npm/:email=npm requires email to be set but doesn't use the value
|
||||
//pkgs.dev.azure.com/hugendubel/_packaging/hugendubel%40Local/npm/registry/:username=hugendubel
|
||||
//pkgs.dev.azure.com/hugendubel/_packaging/hugendubel%40Local/npm/registry/:_password=M2JkaDdwNjIzbWVoZGlmeDU3N2Ficjc3M252NXBkaWg1M2VtaW94dXp5amwyejNkaW5yYQ==
|
||||
//pkgs.dev.azure.com/hugendubel/_packaging/hugendubel%40Local/npm/registry/:email=npm requires email to be set but doesn't use the value
|
||||
//pkgs.dev.azure.com/hugendubel/_packaging/hugendubel%40Local/npm/:username=hugendubel
|
||||
//pkgs.dev.azure.com/hugendubel/_packaging/hugendubel%40Local/npm/:_password=M2JkaDdwNjIzbWVoZGlmeDU3N2Ficjc3M252NXBkaWg1M2VtaW94dXp5amwyejNkaW5yYQ==
|
||||
//pkgs.dev.azure.com/hugendubel/_packaging/hugendubel%40Local/npm/:email=npm requires email to be set but doesn't use the value
|
||||
; end auth token
|
||||
|
||||
|
||||
|
||||
91
angular.json
91
angular.json
@@ -27,10 +27,7 @@
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"libs/ui/tsconfig.lib.json",
|
||||
"libs/ui/tsconfig.spec.json"
|
||||
],
|
||||
"tsConfig": ["libs/ui/tsconfig.lib.json", "libs/ui/tsconfig.spec.json"],
|
||||
"exclude": ["**/node_modules/**"]
|
||||
}
|
||||
}
|
||||
@@ -145,20 +142,13 @@
|
||||
"karmaConfig": "apps/sales/karma.conf.js",
|
||||
"styles": ["apps/sales/src/styles.scss"],
|
||||
"scripts": [],
|
||||
"assets": [
|
||||
"apps/sales/src/favicon.ico",
|
||||
"apps/sales/src/assets",
|
||||
"apps/sales/src/manifest.webmanifest"
|
||||
]
|
||||
"assets": ["apps/sales/src/favicon.ico", "apps/sales/src/assets", "apps/sales/src/manifest.webmanifest"]
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"apps/sales/tsconfig.app.json",
|
||||
"apps/sales/tsconfig.spec.json"
|
||||
],
|
||||
"tsConfig": ["apps/sales/tsconfig.app.json", "apps/sales/tsconfig.spec.json"],
|
||||
"exclude": ["**/node_modules/**"]
|
||||
}
|
||||
}
|
||||
@@ -217,10 +207,7 @@
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"libs/sso/tsconfig.lib.json",
|
||||
"libs/sso/tsconfig.spec.json"
|
||||
],
|
||||
"tsConfig": ["libs/sso/tsconfig.lib.json", "libs/sso/tsconfig.spec.json"],
|
||||
"exclude": ["**/node_modules/**"]
|
||||
}
|
||||
}
|
||||
@@ -250,10 +237,7 @@
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"apps/swagger/availability/tsconfig.lib.json",
|
||||
"apps/swagger/availability/tsconfig.spec.json"
|
||||
],
|
||||
"tsConfig": ["apps/swagger/availability/tsconfig.lib.json", "apps/swagger/availability/tsconfig.spec.json"],
|
||||
"exclude": ["**/node_modules/**"]
|
||||
}
|
||||
}
|
||||
@@ -283,10 +267,7 @@
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"apps/swagger/checkout/tsconfig.lib.json",
|
||||
"apps/swagger/checkout/tsconfig.spec.json"
|
||||
],
|
||||
"tsConfig": ["apps/swagger/checkout/tsconfig.lib.json", "apps/swagger/checkout/tsconfig.spec.json"],
|
||||
"exclude": ["**/node_modules/**"]
|
||||
}
|
||||
}
|
||||
@@ -316,10 +297,7 @@
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"apps/swagger/crm/tsconfig.lib.json",
|
||||
"apps/swagger/crm/tsconfig.spec.json"
|
||||
],
|
||||
"tsConfig": ["apps/swagger/crm/tsconfig.lib.json", "apps/swagger/crm/tsconfig.spec.json"],
|
||||
"exclude": ["**/node_modules/**"]
|
||||
}
|
||||
}
|
||||
@@ -349,10 +327,7 @@
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"apps/swagger/isa/tsconfig.lib.json",
|
||||
"apps/swagger/isa/tsconfig.spec.json"
|
||||
],
|
||||
"tsConfig": ["apps/swagger/isa/tsconfig.lib.json", "apps/swagger/isa/tsconfig.spec.json"],
|
||||
"exclude": ["**/node_modules/**"]
|
||||
}
|
||||
}
|
||||
@@ -382,10 +357,7 @@
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"apps/swagger/oms/tsconfig.lib.json",
|
||||
"apps/swagger/oms/tsconfig.spec.json"
|
||||
],
|
||||
"tsConfig": ["apps/swagger/oms/tsconfig.lib.json", "apps/swagger/oms/tsconfig.spec.json"],
|
||||
"exclude": ["**/node_modules/**"]
|
||||
}
|
||||
}
|
||||
@@ -415,10 +387,7 @@
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"apps/swagger/print/tsconfig.lib.json",
|
||||
"apps/swagger/print/tsconfig.spec.json"
|
||||
],
|
||||
"tsConfig": ["apps/swagger/print/tsconfig.lib.json", "apps/swagger/print/tsconfig.spec.json"],
|
||||
"exclude": ["**/node_modules/**"]
|
||||
}
|
||||
}
|
||||
@@ -448,10 +417,7 @@
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"apps/swagger/cat/tsconfig.lib.json",
|
||||
"apps/swagger/cat/tsconfig.spec.json"
|
||||
],
|
||||
"tsConfig": ["apps/swagger/cat/tsconfig.lib.json", "apps/swagger/cat/tsconfig.spec.json"],
|
||||
"exclude": ["**/node_modules/**"]
|
||||
}
|
||||
}
|
||||
@@ -481,10 +447,37 @@
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"apps/swagger/eis/tsconfig.lib.json",
|
||||
"apps/swagger/eis/tsconfig.spec.json"
|
||||
],
|
||||
"tsConfig": ["apps/swagger/eis/tsconfig.lib.json", "apps/swagger/eis/tsconfig.spec.json"],
|
||||
"exclude": ["**/node_modules/**"]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"native-container": {
|
||||
"projectType": "library",
|
||||
"root": "apps/native-container",
|
||||
"sourceRoot": "apps/native-container/src",
|
||||
"prefix": "lib",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-ng-packagr:build",
|
||||
"options": {
|
||||
"tsConfig": "apps/native-container/tsconfig.lib.json",
|
||||
"project": "apps/native-container/ng-package.json"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"main": "apps/native-container/src/test.ts",
|
||||
"tsConfig": "apps/native-container/tsconfig.spec.json",
|
||||
"karmaConfig": "apps/native-container/karma.conf.js"
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": ["apps/native-container/tsconfig.lib.json", "apps/native-container/tsconfig.spec.json"],
|
||||
"exclude": ["**/node_modules/**"]
|
||||
}
|
||||
}
|
||||
|
||||
24
apps/native-container/README.md
Normal file
24
apps/native-container/README.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# NativeContainer
|
||||
|
||||
This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.2.14.
|
||||
|
||||
## Code scaffolding
|
||||
|
||||
Run `ng generate component component-name --project native-container` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project native-container`.
|
||||
> Note: Don't forget to add `--project native-container` or else it will be added to the default project in your `angular.json` file.
|
||||
|
||||
## Build
|
||||
|
||||
Run `ng build native-container` to build the project. The build artifacts will be stored in the `dist/` directory.
|
||||
|
||||
## Publishing
|
||||
|
||||
After building your library with `ng build native-container`, go to the dist folder `cd dist/native-container` and run `npm publish`.
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `ng test native-container` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
||||
|
||||
## Further help
|
||||
|
||||
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
|
||||
32
apps/native-container/karma.conf.js
Normal file
32
apps/native-container/karma.conf.js
Normal file
@@ -0,0 +1,32 @@
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
basePath: '',
|
||||
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
||||
plugins: [
|
||||
require('karma-jasmine'),
|
||||
require('karma-chrome-launcher'),
|
||||
require('karma-jasmine-html-reporter'),
|
||||
require('karma-coverage-istanbul-reporter'),
|
||||
require('@angular-devkit/build-angular/plugins/karma')
|
||||
],
|
||||
client: {
|
||||
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
||||
},
|
||||
coverageIstanbulReporter: {
|
||||
dir: require('path').join(__dirname, '../../coverage/native-container'),
|
||||
reports: ['html', 'lcovonly', 'text-summary'],
|
||||
fixWebpackSourcePaths: true
|
||||
},
|
||||
reporters: ['progress', 'kjhtml'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
singleRun: false,
|
||||
restartOnFileChange: true
|
||||
});
|
||||
};
|
||||
7
apps/native-container/ng-package.json
Normal file
7
apps/native-container/ng-package.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
|
||||
"dest": "../../dist/native-container",
|
||||
"lib": {
|
||||
"entryFile": "src/public-api.ts"
|
||||
}
|
||||
}
|
||||
8
apps/native-container/package.json
Normal file
8
apps/native-container/package.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "native-container",
|
||||
"version": "0.0.1",
|
||||
"peerDependencies": {
|
||||
"@angular/common": "^8.2.14",
|
||||
"@angular/core": "^8.2.14"
|
||||
}
|
||||
}
|
||||
3
apps/native-container/src/lib/index.ts
Normal file
3
apps/native-container/src/lib/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from './native-container.service';
|
||||
export * from './scan-request.type';
|
||||
export * from './window-ref.service';
|
||||
89
apps/native-container/src/lib/native-container.service.ts
Normal file
89
apps/native-container/src/lib/native-container.service.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable, BehaviorSubject, fromEvent, Subject } from 'rxjs';
|
||||
import { map, take } from 'rxjs/operators';
|
||||
import { WindowRef } from './window-ref.service';
|
||||
import { ScanRequestType } from './scan-request.type';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class NativeContainerService {
|
||||
private wm: Observable<any>;
|
||||
public windowMessages = new Subject<any>();
|
||||
|
||||
private webViewDetected = false;
|
||||
private webViewEventRecieved = false;
|
||||
private browserDetected = false;
|
||||
|
||||
constructor(private windowRef: WindowRef) {
|
||||
this.defineWindowCallback();
|
||||
|
||||
this.wm = fromEvent(this.windowRef.nativeWindow, 'message').pipe(
|
||||
map((e: MessageEvent) => {
|
||||
return e.data;
|
||||
})
|
||||
);
|
||||
|
||||
this.wm.subscribe((data) => {
|
||||
if (data.status === 'INIT') {
|
||||
this.webViewEventRecieved = true;
|
||||
}
|
||||
this.windowMessages.next(data);
|
||||
});
|
||||
}
|
||||
|
||||
public openScanner(scanRequestType: ScanRequestType) {
|
||||
const scanRequest = {
|
||||
[scanRequestType]: true,
|
||||
};
|
||||
|
||||
this.sendMessage(scanRequest);
|
||||
return this.windowMessages.asObservable();
|
||||
}
|
||||
|
||||
public sendMessage(message: any) {
|
||||
this.windowRef.nativeWindow.postMessage({ status: 'IN_PROGRESS', data: 'Scan Started' }, '*');
|
||||
|
||||
if (this.isUiWebview()) {
|
||||
(this.windowRef.nativeWindow as any).webkit.messageHandlers.scanRequest.postMessage(message);
|
||||
} else {
|
||||
this.windowRef.nativeWindow.postMessage({ status: 'ERROR', data: 'Not a WebView' }, '*');
|
||||
this.windowRef.nativeWindow.postMessage(message, '*');
|
||||
}
|
||||
}
|
||||
|
||||
public isUiWebview() {
|
||||
const navigator = this.windowRef.nativeWindow.navigator as Navigator;
|
||||
const standalone = (navigator as any).standalone,
|
||||
userAgent = navigator.userAgent.toLowerCase(),
|
||||
safari = /safari/.test(userAgent),
|
||||
ios = /iphone|ipod|ipad/.test(userAgent),
|
||||
chrome = /chrome/.test(userAgent) && /Google Inc/.test(navigator.vendor),
|
||||
crios = /crios/.test(userAgent);
|
||||
|
||||
this.webViewDetected = ios && !standalone && !safari;
|
||||
this.browserDetected = !standalone && (safari || chrome) && !crios;
|
||||
return {
|
||||
isSafari: this.browserDetected,
|
||||
isNative: this.webViewDetected || this.webViewEventRecieved,
|
||||
};
|
||||
}
|
||||
|
||||
private defineWindowCallback() {
|
||||
if (this.windowRef.nativeWindow['scanResults'] === undefined) {
|
||||
this.windowRef.nativeWindow['scanResults'] = (result) => window.postMessage(result, '*');
|
||||
}
|
||||
|
||||
if (this.windowRef.nativeWindow['isRunningNative'] === undefined) {
|
||||
this.windowRef.nativeWindow['isRunningNative'] = (_) => window.postMessage({ status: 'INIT', data: 'Is a WebView' }, '*');
|
||||
}
|
||||
|
||||
// Try sending ping request, to invoke the containers isRunningNative event
|
||||
try {
|
||||
(this.windowRef.nativeWindow as any).webkit.messageHandlers.scanRequest.postMessage('PING');
|
||||
} catch (error) {
|
||||
this.windowRef.nativeWindow.postMessage({ status: 'ERROR', data: 'Not a WebView' }, '*');
|
||||
this.windowRef.nativeWindow.postMessage('PING', '*');
|
||||
}
|
||||
}
|
||||
}
|
||||
8
apps/native-container/src/lib/scan-request.type.ts
Normal file
8
apps/native-container/src/lib/scan-request.type.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export type ScanRequestType =
|
||||
| 'scanLogin'
|
||||
| 'scanBook'
|
||||
| 'scanShelf'
|
||||
| 'scanCustomer'
|
||||
| 'remissionContainer'
|
||||
| 'remissionProduct'
|
||||
| 'shippingDocument';
|
||||
10
apps/native-container/src/lib/window-ref.service.ts
Normal file
10
apps/native-container/src/lib/window-ref.service.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class WindowRef {
|
||||
get nativeWindow(): Window {
|
||||
return window;
|
||||
}
|
||||
}
|
||||
5
apps/native-container/src/public-api.ts
Normal file
5
apps/native-container/src/public-api.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
/*
|
||||
* Public API Surface of native-container
|
||||
*/
|
||||
|
||||
export * from './lib';
|
||||
21
apps/native-container/src/test.ts
Normal file
21
apps/native-container/src/test.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
|
||||
|
||||
import 'zone.js/dist/zone';
|
||||
import 'zone.js/dist/zone-testing';
|
||||
import { getTestBed } from '@angular/core/testing';
|
||||
import {
|
||||
BrowserDynamicTestingModule,
|
||||
platformBrowserDynamicTesting
|
||||
} from '@angular/platform-browser-dynamic/testing';
|
||||
|
||||
declare const require: any;
|
||||
|
||||
// First, initialize the Angular testing environment.
|
||||
getTestBed().initTestEnvironment(
|
||||
BrowserDynamicTestingModule,
|
||||
platformBrowserDynamicTesting()
|
||||
);
|
||||
// Then we find all the tests.
|
||||
const context = require.context('./', true, /\.spec\.ts$/);
|
||||
// And load the modules.
|
||||
context.keys().map(context);
|
||||
26
apps/native-container/tsconfig.lib.json
Normal file
26
apps/native-container/tsconfig.lib.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../out-tsc/lib",
|
||||
"target": "es2015",
|
||||
"declaration": true,
|
||||
"inlineSources": true,
|
||||
"types": [],
|
||||
"lib": [
|
||||
"dom",
|
||||
"es2018"
|
||||
]
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"annotateForClosureCompiler": true,
|
||||
"skipTemplateCodegen": true,
|
||||
"strictMetadataEmit": true,
|
||||
"fullTemplateTypeCheck": true,
|
||||
"strictInjectionParameters": true,
|
||||
"enableResourceInlining": true
|
||||
},
|
||||
"exclude": [
|
||||
"src/test.ts",
|
||||
"**/*.spec.ts"
|
||||
]
|
||||
}
|
||||
17
apps/native-container/tsconfig.spec.json
Normal file
17
apps/native-container/tsconfig.spec.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../out-tsc/spec",
|
||||
"types": [
|
||||
"jasmine",
|
||||
"node"
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
"src/test.ts"
|
||||
],
|
||||
"include": [
|
||||
"**/*.spec.ts",
|
||||
"**/*.d.ts"
|
||||
]
|
||||
}
|
||||
17
apps/native-container/tslint.json
Normal file
17
apps/native-container/tslint.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"extends": "../../tslint.json",
|
||||
"rules": {
|
||||
"directive-selector": [
|
||||
true,
|
||||
"attribute",
|
||||
"lib",
|
||||
"camelCase"
|
||||
],
|
||||
"component-selector": [
|
||||
true,
|
||||
"element",
|
||||
"lib",
|
||||
"kebab-case"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -3,15 +3,15 @@
|
||||
<app-header [ngClass]="{ loading: loading }" *ngIf="authenticated"></app-header>
|
||||
<app-content [ngClass]="{ loading: loading }" *ngIf="authenticated"></app-content>
|
||||
<app-menu [ngClass]="{ loading: loading }" *ngIf="authenticated"></app-menu>
|
||||
<img *ngIf="loading" src="/assets/images/Icon_Loading.svg" class="app-loader"
|
||||
/>
|
||||
<img *ngIf="loading" src="/assets/images/Icon_Loading.svg" class="app-loader" />
|
||||
</lib-offline-overlay>
|
||||
<router-outlet></router-outlet>
|
||||
<script *ngIf="includeGoogleAnalytics">
|
||||
(function(i, s, o, g, r, a, m) {
|
||||
(function (i, s, o, g, r, a, m) {
|
||||
i['GoogleAnalyticsObject'] = r;
|
||||
(i[r] =
|
||||
i[r] ||
|
||||
function() {
|
||||
function () {
|
||||
(i[r].q = i[r].q || []).push(arguments);
|
||||
}),
|
||||
(i[r].l = 1 * new Date());
|
||||
@@ -22,4 +22,4 @@
|
||||
})(window, document, 'script', 'https://www.google-analytics.com/analytics.js', 'ga');
|
||||
|
||||
ga('create', 'UA-76423009-4', 'auto');
|
||||
</script>
|
||||
</script>
|
||||
@@ -7,9 +7,8 @@ import {
|
||||
import { Observable } from 'rxjs';
|
||||
import { RemissionOverlayService } from '../../modules/remission/services/remission-overlay.service';
|
||||
import { RemissionSelectors } from '../../core/store/selectors/remission.selectors';
|
||||
import { RemissionSelectedFilters } from '@isa/remission';
|
||||
import { Select } from '@ngxs/store';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { map, tap } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'app-content-header',
|
||||
@@ -18,7 +17,7 @@ import { map } from 'rxjs/operators';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ContentHeaderComponent implements OnInit {
|
||||
@Select(RemissionSelectors.getRemissionSelectedFilters)
|
||||
@Select(RemissionSelectors.getRemissionActiveFilters)
|
||||
remissionFilters$: Observable<string[]>;
|
||||
|
||||
@Input() showFilter$: Observable<boolean>;
|
||||
@@ -26,11 +25,11 @@ export class ContentHeaderComponent implements OnInit {
|
||||
|
||||
isFilterActive$: Observable<boolean>;
|
||||
|
||||
constructor(private remissionOverlayService: RemissionOverlayService) {}
|
||||
constructor(private remissionOverlayService: RemissionOverlayService) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.isFilterActive$ = this.remissionFilters$.pipe(
|
||||
map((selectedFilters) => selectedFilters && !!selectedFilters.length)
|
||||
map((selectedFilters) => selectedFilters && !!Object.entries(selectedFilters).length),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { SsoService } from 'sso';
|
||||
@Component({
|
||||
selector: 'app-log-in',
|
||||
templateUrl: './log-in.component.html',
|
||||
styleUrls: ['./log-in.component.scss']
|
||||
styleUrls: ['./log-in.component.scss'],
|
||||
})
|
||||
export class LogInComponent implements OnInit {
|
||||
constructor(private route: ActivatedRoute, private ssoService: SsoService) {}
|
||||
@@ -13,7 +13,7 @@ export class LogInComponent implements OnInit {
|
||||
ngOnInit() {
|
||||
const token = this.route.snapshot.paramMap.get('token');
|
||||
if (token === null) {
|
||||
this.ssoService.logoff();
|
||||
// this.ssoService.logoff();
|
||||
}
|
||||
|
||||
this.logIn(token);
|
||||
|
||||
@@ -6,7 +6,7 @@ import { NativeContainerService } from 'shared/lib/barcode-scanner';
|
||||
@Component({
|
||||
selector: 'app-log-out',
|
||||
templateUrl: './log-out.component.html',
|
||||
styleUrls: ['./log-out.component.scss']
|
||||
styleUrls: ['./log-out.component.scss'],
|
||||
})
|
||||
export class LogOutComponent implements OnInit {
|
||||
id = 'logout-modal';
|
||||
@@ -16,7 +16,6 @@ export class LogOutComponent implements OnInit {
|
||||
|
||||
logoff() {
|
||||
this.ssoService.logoff();
|
||||
this.containerNotificationMessage();
|
||||
}
|
||||
|
||||
openDialog() {
|
||||
@@ -26,12 +25,4 @@ export class LogOutComponent implements OnInit {
|
||||
closeModal() {
|
||||
this.modalService.close(this.id);
|
||||
}
|
||||
|
||||
private containerNotificationMessage() {
|
||||
// Notify the container app if the user has logged out
|
||||
// For the purpposes of removing the container Header element
|
||||
if (this.nativeContainer.isUiWebview()) {
|
||||
this.nativeContainer.sendMessage({ scanLogin: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,12 +14,14 @@ export class ProductMapping {
|
||||
let price = 0;
|
||||
|
||||
let priceDto: PriceDTO;
|
||||
let ssctext = '';
|
||||
let ssc = '';
|
||||
let sscText = '';
|
||||
let storeStatusCode = 0;
|
||||
|
||||
if (!!item.catalogAvailability) {
|
||||
priceDto = item.catalogAvailability.price;
|
||||
ssctext = item.catalogAvailability.sscText;
|
||||
ssc = item.catalogAvailability.ssc;
|
||||
sscText = item.catalogAvailability.sscText;
|
||||
storeStatusCode = item.catalogAvailability.status;
|
||||
}
|
||||
|
||||
@@ -63,7 +65,8 @@ export class ProductMapping {
|
||||
imageId: item.imageId,
|
||||
edition: item.product.edition,
|
||||
volume: item.product.volume,
|
||||
sscText: ssctext && ssctext.length > 28 ? ssctext.substr(0, 28) + '...' : ssctext,
|
||||
ssc,
|
||||
sscText,
|
||||
storeStatusCode: storeStatusCode,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ export interface Product {
|
||||
location: string;
|
||||
ean: string;
|
||||
imageId: string;
|
||||
ssc: string,
|
||||
sscText: string;
|
||||
storeStatusCode: number;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import {
|
||||
RemissionProcess,
|
||||
RemissionProduct,
|
||||
FilterOption,
|
||||
ShippingDocument
|
||||
ShippingDocument,
|
||||
} from '@isa/remission';
|
||||
import { RemissionResourceType } from '../../modules/remission/models/remission-resource-type.model';
|
||||
import { RemissionFinishingProcessStatus } from '../../modules/remission/models/remission-finishing-process-status.enum';
|
||||
import {
|
||||
RemissionActiveView,
|
||||
RemissionFinishingProcessStatus,
|
||||
RemissionResourceType,
|
||||
} from '../../modules/remission/models';
|
||||
|
||||
export interface Remission {
|
||||
remissionProcessCreated?: boolean;
|
||||
@@ -21,4 +23,5 @@ export interface Remission {
|
||||
blockReminder?: boolean;
|
||||
remissionFinishingProcessStatus?: RemissionFinishingProcessStatus;
|
||||
containerId?: string;
|
||||
activeView?: RemissionActiveView;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { PositionStrategy, OverlayRef } from '@angular/cdk/overlay';
|
||||
import { ElementRef } from '@angular/core';
|
||||
import { fromEvent, Subscription, merge } from 'rxjs';
|
||||
import { isNumber } from 'util';
|
||||
|
||||
export class ContainerPositionStrategy implements PositionStrategy {
|
||||
windowScroll$ = fromEvent(window, 'scroll');
|
||||
@@ -17,7 +17,7 @@ export class ContainerPositionStrategy implements PositionStrategy {
|
||||
return this.overlayRef.overlayElement;
|
||||
}
|
||||
|
||||
constructor(public containerElement: HTMLElement) {}
|
||||
constructor(public containerElement: HTMLElement, private position?: { top?: number; bottom?: number; left?: number; right?: number }) {}
|
||||
|
||||
attach(overlayRef: OverlayRef): void {
|
||||
this.overlayRef = overlayRef;
|
||||
@@ -40,15 +40,24 @@ export class ContainerPositionStrategy implements PositionStrategy {
|
||||
return;
|
||||
}
|
||||
|
||||
this.overlayElement.style.position = 'relative';
|
||||
this.overlayElement.style.overflow = 'auto';
|
||||
|
||||
const domRect = this.containerElement.getClientRects().item(0);
|
||||
|
||||
this.overlayElement.style.top = `${domRect.top}px`;
|
||||
this.overlayElement.style.left = `${domRect.left}px`;
|
||||
this.overlayElement.style.width = `${domRect.width}px`;
|
||||
this.overlayElement.style.height = `${domRect.height}px`;
|
||||
this.overlayElement.style.zIndex = '999';
|
||||
const position = this.position || {};
|
||||
|
||||
this.overlayElement.style.top = `${isNumber(position.top) ? position.top : domRect.top}px`;
|
||||
this.overlayElement.style.left = `${isNumber(position.left) ? position.left : domRect.left}px`;
|
||||
|
||||
if (isNumber(position.right)) {
|
||||
this.overlayElement.style.right = `${position.right}px`;
|
||||
} else {
|
||||
this.overlayElement.style.width = `${domRect.width}px`;
|
||||
}
|
||||
if (isNumber(position.bottom)) {
|
||||
this.overlayElement.style.bottom = `${position.bottom}px`;
|
||||
} else {
|
||||
this.overlayElement.style.height = `${domRect.height}px`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
} from '@isa/remission';
|
||||
import { RemissionResourceType } from '../../../modules/remission/models/remission-resource-type.model';
|
||||
import { RemissionFinishingProcessStatus } from '../../../modules/remission/models/remission-finishing-process-status.enum';
|
||||
import { RemissionActiveView } from '../../../modules/remission/models';
|
||||
|
||||
export const SET_REMISSION_CREATED = '[REMISSION] Set created';
|
||||
export const SET_REMISSION_STARTED = '[REMISSION] Set started';
|
||||
@@ -36,6 +37,8 @@ export const SET_ALL_OPEN_REMISSIONS = '[REMISSION] Set all open remissions';
|
||||
export const SET_ALL_REMISSIONS = '[REMISSION] Set all remissions';
|
||||
export const SET_REMISSION_FILTER = '[REMISSION] Set remission filter';
|
||||
export const SET_REMISSION_ISLOADING = '[REMISSION] Set remission isLoading';
|
||||
export const SET_REMISSION_ACTIVE_VIEW =
|
||||
'[REMISSION] Set remission active view';
|
||||
export const REQUEST_UPDATE_SHIPPING_DOCUMENT =
|
||||
'[REMISSION] Update shipping document';
|
||||
|
||||
@@ -204,3 +207,9 @@ export class SetRemissionIsLoading {
|
||||
|
||||
constructor(public isLoading: boolean) {}
|
||||
}
|
||||
|
||||
export class SetRemissionActiveView {
|
||||
static readonly type = SET_REMISSION_ACTIVE_VIEW;
|
||||
|
||||
constructor(public activeView: RemissionActiveView) {}
|
||||
}
|
||||
|
||||
@@ -216,4 +216,22 @@ export class RemissionSelectors {
|
||||
|
||||
return remission.target === 'Blank';
|
||||
}
|
||||
|
||||
@Selector([RemissionState])
|
||||
static getActiveView(remissionState: RemissionStateModel) {
|
||||
const remission = remissionState.remission;
|
||||
|
||||
return remission.activeView;
|
||||
}
|
||||
|
||||
@Selector([RemissionState])
|
||||
static getRemissionSupplierId(remissionState: RemissionStateModel) {
|
||||
const remission = remissionState.remission;
|
||||
const supplier =
|
||||
remission.remissionProcess &&
|
||||
remission.remissionProcess.filter &&
|
||||
remission.remissionProcess.filter.target;
|
||||
|
||||
return supplier && supplier.id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,12 +5,15 @@ import {
|
||||
AppDeleteProcess,
|
||||
AppUserDataSync,
|
||||
AppSwitchModule,
|
||||
ReloadSavedState
|
||||
ReloadSavedState,
|
||||
} from '../actions/app.actions';
|
||||
import { LoadBranches, LoadUserBranch } from '../actions/branch.actions';
|
||||
import { BranchSelectors } from '../selectors/branch.selector';
|
||||
import { isNullOrUndefined } from 'util';
|
||||
import { RemoveProcessNewState, ReloadProcessData } from '../actions/process.actions';
|
||||
import {
|
||||
RemoveProcessNewState,
|
||||
ReloadProcessData,
|
||||
} from '../actions/process.actions';
|
||||
import { UserStateService } from '../../services/user-state.service';
|
||||
import { UserStateSyncData } from '../../models/user-state-sync.model';
|
||||
import { ReloadCustomersData } from '../actions/customer.actions';
|
||||
@@ -31,7 +34,7 @@ import { ReloadRemission } from '../actions/remission.actions';
|
||||
import { ReloadFormState } from '../actions/forms.actions';
|
||||
import { FILIALE_LANDING_PAGE } from '../../utils/app.constants';
|
||||
|
||||
export const SYNC_DATA_VERSION = 204;
|
||||
export const SYNC_DATA_VERSION = 205;
|
||||
|
||||
export class AppStateModel {
|
||||
currentProcesssId: number;
|
||||
@@ -48,8 +51,8 @@ export class AppStateModel {
|
||||
processIds: [],
|
||||
activeModule: ModuleSwitcher.Customer,
|
||||
synced: false,
|
||||
syncedUserData: ''
|
||||
}
|
||||
syncedUserData: '',
|
||||
},
|
||||
})
|
||||
export class AppState {
|
||||
userStateService$ = new Subject<string>();
|
||||
@@ -93,7 +96,7 @@ export class AppState {
|
||||
.pipe(
|
||||
debounceTime(500),
|
||||
distinctUntilChanged((prev, next) => prev && next && prev === next),
|
||||
switchMap(state => {
|
||||
switchMap((state) => {
|
||||
return this.userStateService.saveUserState(state);
|
||||
})
|
||||
)
|
||||
@@ -101,12 +104,16 @@ export class AppState {
|
||||
}
|
||||
|
||||
@Action(AppSetCurrentProcess)
|
||||
appSetCurrentProcess(ctx: StateContext<AppStateModel>, { payload }: AppSetCurrentProcess) {
|
||||
appSetCurrentProcess(
|
||||
ctx: StateContext<AppStateModel>,
|
||||
{ payload }: AppSetCurrentProcess
|
||||
) {
|
||||
const state = ctx.getState();
|
||||
const processExists = state.processIds.findIndex(t => t === payload) !== -1;
|
||||
const processExists =
|
||||
state.processIds.findIndex((t) => t === payload) !== -1;
|
||||
if (processExists) {
|
||||
ctx.patchState({
|
||||
currentProcesssId: payload
|
||||
currentProcesssId: payload,
|
||||
});
|
||||
this.store.dispatch(new RemoveProcessNewState());
|
||||
}
|
||||
@@ -115,18 +122,23 @@ export class AppState {
|
||||
@Action(AppAddProcess)
|
||||
appAddProcess(ctx: StateContext<AppStateModel>, { payload }: AppAddProcess) {
|
||||
const state = ctx.getState();
|
||||
const processExists = state.processIds.findIndex(t => t === payload) !== -1;
|
||||
const processExists =
|
||||
state.processIds.findIndex((t) => t === payload) !== -1;
|
||||
if (!processExists) {
|
||||
const processIds = [...state.processIds, payload];
|
||||
const currentProcesssId = payload;
|
||||
ctx.patchState({ currentProcesssId, processIds });
|
||||
|
||||
const branchesLoaded = this.store.selectSnapshot(BranchSelectors.getBranches);
|
||||
const branchesLoaded = this.store.selectSnapshot(
|
||||
BranchSelectors.getBranches
|
||||
);
|
||||
if (!branchesLoaded || Object.keys(branchesLoaded).length === 0) {
|
||||
this.store.dispatch(new LoadBranches());
|
||||
}
|
||||
|
||||
const userBranch = this.store.selectSnapshot(BranchSelectors.getUserBranch);
|
||||
const userBranch = this.store.selectSnapshot(
|
||||
BranchSelectors.getUserBranch
|
||||
);
|
||||
if (isNullOrUndefined(userBranch)) {
|
||||
this.store.dispatch(new LoadUserBranch());
|
||||
}
|
||||
@@ -136,18 +148,24 @@ export class AppState {
|
||||
}
|
||||
|
||||
@Action(AppDeleteProcess)
|
||||
appDeleteProcess(ctx: StateContext<AppStateModel>, { payload }: AppDeleteProcess) {
|
||||
appDeleteProcess(
|
||||
ctx: StateContext<AppStateModel>,
|
||||
{ payload }: AppDeleteProcess
|
||||
) {
|
||||
const state = ctx.getState();
|
||||
const currentIds = state.processIds;
|
||||
if (currentIds) {
|
||||
const processIds = currentIds.filter(t => t && t !== payload);
|
||||
const processIds = currentIds.filter((t) => t && t !== payload);
|
||||
ctx.patchState({ processIds });
|
||||
this.syncApiState(processIds, state.activeModule);
|
||||
}
|
||||
}
|
||||
|
||||
@Action(AppSwitchModule)
|
||||
appSwitchModule(ctx: StateContext<AppStateModel>, { payload }: AppSwitchModule) {
|
||||
appSwitchModule(
|
||||
ctx: StateContext<AppStateModel>,
|
||||
{ payload }: AppSwitchModule
|
||||
) {
|
||||
const state = ctx.getState();
|
||||
ctx.patchState({ activeModule: payload });
|
||||
this.syncApiState(state.processIds, payload);
|
||||
@@ -157,7 +175,10 @@ export class AppState {
|
||||
* Save store data on backend
|
||||
*/
|
||||
@Action(AppUserDataSync)
|
||||
appUserDataSynced(ctx: StateContext<AppStateModel>, { data, sync }: AppUserDataSync) {
|
||||
appUserDataSynced(
|
||||
ctx: StateContext<AppStateModel>,
|
||||
{ data, sync }: AppUserDataSync
|
||||
) {
|
||||
const state = ctx.getState();
|
||||
|
||||
let currentUserData: UserStateSyncData = {};
|
||||
@@ -175,7 +196,7 @@ export class AppState {
|
||||
|
||||
ctx.patchState({
|
||||
synced: true,
|
||||
syncedUserData: updatedUserDataString
|
||||
syncedUserData: updatedUserDataString,
|
||||
});
|
||||
this.userStateService$.next(updatedUserDataString);
|
||||
}
|
||||
@@ -184,7 +205,10 @@ export class AppState {
|
||||
* Initial store loading from API, triggered once on page load
|
||||
*/
|
||||
@Action(ReloadSavedState)
|
||||
reloadSavedState(ctx: StateContext<AppStateModel>, { data, sync }: ReloadSavedState) {
|
||||
reloadSavedState(
|
||||
ctx: StateContext<AppStateModel>,
|
||||
{ data, sync }: ReloadSavedState
|
||||
) {
|
||||
const state = ctx.getState();
|
||||
|
||||
let currentUserData: UserStateSyncData = {};
|
||||
@@ -208,18 +232,25 @@ export class AppState {
|
||||
if (sync && syncedData.version === SYNC_DATA_VERSION) {
|
||||
this.reloadDataFromAPI(syncedData);
|
||||
|
||||
if (syncedData.currentProcesssId || syncedData.activeModule === ModuleSwitcher.Branch) {
|
||||
if (
|
||||
syncedData.currentProcesssId ||
|
||||
syncedData.activeModule === ModuleSwitcher.Branch
|
||||
) {
|
||||
ctx.patchState({
|
||||
...state,
|
||||
synced: true,
|
||||
syncedUserData: updatedUserDataString,
|
||||
currentProcesssId: serverData.currentProcesssId,
|
||||
processIds: serverData.processIds,
|
||||
activeModule: serverData.activeModule
|
||||
activeModule: serverData.activeModule,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
ctx.patchState({ ...state, synced: true, syncedUserData: updatedUserDataString });
|
||||
ctx.patchState({
|
||||
...state,
|
||||
synced: true,
|
||||
syncedUserData: updatedUserDataString,
|
||||
});
|
||||
}
|
||||
|
||||
this.userStateService$.next(updatedUserDataString);
|
||||
@@ -231,11 +262,19 @@ export class AppState {
|
||||
return;
|
||||
}
|
||||
if (data.customers) {
|
||||
this.store.dispatch(new ReloadCustomersData(data.customers, data.lastCreatedCustomerId, data.cachedCustomerSearch));
|
||||
this.store.dispatch(
|
||||
new ReloadCustomersData(
|
||||
data.customers,
|
||||
data.lastCreatedCustomerId,
|
||||
data.cachedCustomerSearch
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (data.products) {
|
||||
this.store.dispatch(new ReloadProductsData(data.products, data.cachedProductResults));
|
||||
this.store.dispatch(
|
||||
new ReloadProductsData(data.products, data.cachedProductResults)
|
||||
);
|
||||
}
|
||||
|
||||
if (data.carts) {
|
||||
@@ -255,7 +294,9 @@ export class AppState {
|
||||
}
|
||||
|
||||
if (data.processes) {
|
||||
this.store.dispatch(new ReloadProcessData(data.processes, data.recentArticles));
|
||||
this.store.dispatch(
|
||||
new ReloadProcessData(data.processes, data.recentArticles)
|
||||
);
|
||||
}
|
||||
|
||||
if (data.formsState) {
|
||||
@@ -263,11 +304,20 @@ export class AppState {
|
||||
}
|
||||
|
||||
if (data.activeModule === ModuleSwitcher.Customer) {
|
||||
if (data.processes && data.currentProcesssId && data.processes[data.currentProcesssId]) {
|
||||
if (
|
||||
data.processes &&
|
||||
data.currentProcesssId &&
|
||||
data.processes[data.currentProcesssId]
|
||||
) {
|
||||
const currentProcesssId = data.currentProcesssId;
|
||||
const currentRoute = data.processes[currentProcesssId].currentRoute;
|
||||
if (currentRoute && currentRoute.length > 0) {
|
||||
this.routingAvailableAction(data, currentProcesssId, currentRoute, data.activeModule);
|
||||
this.routingAvailableAction(
|
||||
data,
|
||||
currentProcesssId,
|
||||
currentRoute,
|
||||
data.activeModule
|
||||
);
|
||||
} else {
|
||||
this.router.navigate(['/dashboard']);
|
||||
}
|
||||
@@ -279,7 +329,12 @@ export class AppState {
|
||||
const currentProcesssId = -1;
|
||||
const currentRoute = data.branchProcess.currentRoute;
|
||||
if (currentRoute && currentRoute.length > 0) {
|
||||
this.routingAvailableAction(data, currentProcesssId, currentRoute, data.activeModule);
|
||||
this.routingAvailableAction(
|
||||
data,
|
||||
currentProcesssId,
|
||||
currentRoute,
|
||||
data.activeModule
|
||||
);
|
||||
} else {
|
||||
this.router.navigate([FILIALE_LANDING_PAGE]);
|
||||
}
|
||||
@@ -293,11 +348,24 @@ export class AppState {
|
||||
}
|
||||
|
||||
if (data.activeModule === ModuleSwitcher.Branch) {
|
||||
this.moduleSwitcherService.switch(ModuleSwitcher.Branch, data.branchProcess);
|
||||
this.moduleSwitcherService.switch(
|
||||
ModuleSwitcher.Branch,
|
||||
data.branchProcess
|
||||
);
|
||||
}
|
||||
|
||||
if (data.processesBreadcrumbs && data.activeCrumbs && (data.currentProcesssId || data.activeModule === ModuleSwitcher.Branch)) {
|
||||
this.store.dispatch(new ReloadBreadcrumbsData(data.processesBreadcrumbs, data.activeCrumbs, data.previusMenuPath));
|
||||
if (
|
||||
data.processesBreadcrumbs &&
|
||||
data.activeCrumbs &&
|
||||
(data.currentProcesssId || data.activeModule === ModuleSwitcher.Branch)
|
||||
) {
|
||||
this.store.dispatch(
|
||||
new ReloadBreadcrumbsData(
|
||||
data.processesBreadcrumbs,
|
||||
data.activeCrumbs,
|
||||
data.previusMenuPath
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (data.filters && data.processesSelectedFilters && data.dropdownFilters) {
|
||||
@@ -323,16 +391,28 @@ export class AppState {
|
||||
);
|
||||
}
|
||||
|
||||
private routingAvailableAction(data: UserStateSyncData, currentProcesssId: number, currentRoute: string, module: ModuleSwitcher) {
|
||||
const hasActiveCrumbsAvailableForProcess = data.activeCrumbs && data.activeCrumbs[currentProcesssId];
|
||||
private routingAvailableAction(
|
||||
data: UserStateSyncData,
|
||||
currentProcesssId: number,
|
||||
currentRoute: string,
|
||||
module: ModuleSwitcher
|
||||
) {
|
||||
const hasActiveCrumbsAvailableForProcess =
|
||||
data.activeCrumbs && data.activeCrumbs[currentProcesssId];
|
||||
const activeCrumbs = data.activeCrumbs[currentProcesssId];
|
||||
const hasProcessBreadcrumbsAvailableForProcess = data.processesBreadcrumbs && data.processesBreadcrumbs[activeCrumbs];
|
||||
const hasProcessBreadcrumbsAvailableForProcess =
|
||||
data.processesBreadcrumbs && data.processesBreadcrumbs[activeCrumbs];
|
||||
|
||||
const breadcrumb = hasProcessBreadcrumbsAvailableForProcess
|
||||
? data.processesBreadcrumbs[activeCrumbs].find(t => t.processId === currentProcesssId)
|
||||
? data.processesBreadcrumbs[activeCrumbs].find(
|
||||
(t) => t.processId === currentProcesssId
|
||||
)
|
||||
: null;
|
||||
|
||||
const breadcrumbPath = breadcrumb && breadcrumb.breadcrumbs ? breadcrumb.breadcrumbs.find(t => t && t.path === currentRoute) : null;
|
||||
const breadcrumbPath =
|
||||
breadcrumb && breadcrumb.breadcrumbs
|
||||
? breadcrumb.breadcrumbs.find((t) => t && t.path === currentRoute)
|
||||
: null;
|
||||
|
||||
if (
|
||||
hasActiveCrumbsAvailableForProcess &&
|
||||
@@ -341,7 +421,9 @@ export class AppState {
|
||||
breadcrumb.breadcrumbs &&
|
||||
breadcrumbPath
|
||||
) {
|
||||
this.router.navigate([breadcrumbPath.path], { queryParams: breadcrumbPath.queryParams });
|
||||
this.router.navigate([breadcrumbPath.path], {
|
||||
queryParams: breadcrumbPath.queryParams,
|
||||
});
|
||||
} else {
|
||||
this.navigateToCourrentRoute(
|
||||
currentRoute,
|
||||
@@ -364,7 +446,7 @@ export class AppState {
|
||||
const userSyncData: UserStateSyncData = {
|
||||
version: SYNC_DATA_VERSION,
|
||||
processIds: processIds,
|
||||
activeModule: activeModule
|
||||
activeModule: activeModule,
|
||||
};
|
||||
const userData = JSON.stringify(userSyncData);
|
||||
this.store.dispatch(new AppUserDataSync(userData));
|
||||
|
||||
@@ -5,10 +5,14 @@ import * as actions from '../actions/remission.actions';
|
||||
import { UserStateSyncData } from '../../models/user-state-sync.model';
|
||||
import { AppUserDataSync } from '../actions/app.actions';
|
||||
import { isNullOrUndefined } from 'util';
|
||||
import { RemissionResourceType, RemissionTargetType } from '../../../modules/remission/models/remission-resource-type.model';
|
||||
import {
|
||||
RemissionResourceType,
|
||||
RemissionTargetType,
|
||||
} from '../../../modules/remission/models/remission-resource-type.model';
|
||||
import { RemissionService, RemissionProcess } from '@isa/remission';
|
||||
import { RemissionFinishingProcessStatus } from '../../../modules/remission/models/remission-finishing-process-status.enum';
|
||||
import get from 'lodash/get';
|
||||
import { RemissionActiveView } from '../../../modules/remission/models';
|
||||
|
||||
export class RemissionStateModel {
|
||||
remission: Remission;
|
||||
@@ -25,22 +29,29 @@ export class RemissionStateModel {
|
||||
},
|
||||
})
|
||||
export class RemissionState {
|
||||
constructor(private store: Store, private remissionService: RemissionService) {}
|
||||
constructor(private store: Store) {}
|
||||
|
||||
@Action(actions.SetRemissionCreated)
|
||||
setRemissionCreated(ctx: StateContext<RemissionStateModel>, { status }: actions.SetRemissionCreated) {
|
||||
setRemissionCreated(
|
||||
ctx: StateContext<RemissionStateModel>,
|
||||
{ status }: actions.SetRemissionCreated
|
||||
) {
|
||||
const state = ctx.getState();
|
||||
const currentRemission = state.remission;
|
||||
const remission: Remission = {
|
||||
...currentRemission,
|
||||
remissionProcessCreated: status,
|
||||
activeView: RemissionActiveView.PRODUCTS,
|
||||
};
|
||||
ctx.patchState({ remission });
|
||||
this.syncApiState(remission);
|
||||
}
|
||||
|
||||
@Action(actions.SetRemissionStarted)
|
||||
setRemissionStarted(ctx: StateContext<RemissionStateModel>, { status }: actions.SetRemissionStarted) {
|
||||
setRemissionStarted(
|
||||
ctx: StateContext<RemissionStateModel>,
|
||||
{ status }: actions.SetRemissionStarted
|
||||
) {
|
||||
const state = ctx.getState();
|
||||
const currentRemission = state.remission;
|
||||
const remission: Remission = {
|
||||
@@ -52,7 +63,10 @@ export class RemissionState {
|
||||
}
|
||||
|
||||
@Action(actions.SetRemissionCompleted)
|
||||
setRemissionCompleted(ctx: StateContext<RemissionStateModel>, { status }: actions.SetRemissionCompleted) {
|
||||
setRemissionCompleted(
|
||||
ctx: StateContext<RemissionStateModel>,
|
||||
{ status }: actions.SetRemissionCompleted
|
||||
) {
|
||||
const state = ctx.getState();
|
||||
const currentRemission = state.remission;
|
||||
const remission: Remission = {
|
||||
@@ -64,14 +78,19 @@ export class RemissionState {
|
||||
}
|
||||
|
||||
@Action(actions.SetRemissionProcess)
|
||||
setRemissionProcess(ctx: StateContext<RemissionStateModel>, { remissionProcess, initialValue }: actions.SetRemissionProcess) {
|
||||
setRemissionProcess(
|
||||
ctx: StateContext<RemissionStateModel>,
|
||||
{ remissionProcess, initialValue }: actions.SetRemissionProcess
|
||||
) {
|
||||
const state = ctx.getState();
|
||||
const currentRemission = state.remission;
|
||||
const filter = get(remissionProcess, 'filter.filter', null);
|
||||
const remissionWithEmptyFilters =
|
||||
filter === null ||
|
||||
Object.keys(filter).every((sourceKey) =>
|
||||
Object.keys(filter[sourceKey]).every((targetKey) => Object.keys(filter[sourceKey][targetKey]).length === 0)
|
||||
Object.keys(filter[sourceKey]).every(
|
||||
(targetKey) => Object.keys(filter[sourceKey][targetKey]).length === 0
|
||||
)
|
||||
);
|
||||
const remission: Remission = {
|
||||
...currentRemission,
|
||||
@@ -92,7 +111,6 @@ export class RemissionState {
|
||||
const remissionState: RemissionStateModel = {
|
||||
...state,
|
||||
remission,
|
||||
isLoading: false,
|
||||
};
|
||||
|
||||
ctx.patchState(remissionState);
|
||||
@@ -100,7 +118,10 @@ export class RemissionState {
|
||||
}
|
||||
|
||||
@Action(actions.SetRemissionFinishedProcessStatus)
|
||||
setRemissionFinishedProcessStatus(ctx: StateContext<RemissionStateModel>, { status }: actions.SetRemissionFinishedProcessStatus) {
|
||||
setRemissionFinishedProcessStatus(
|
||||
ctx: StateContext<RemissionStateModel>,
|
||||
{ status }: actions.SetRemissionFinishedProcessStatus
|
||||
) {
|
||||
const state = ctx.getState();
|
||||
const currentRemission = state.remission;
|
||||
const remission: Remission = {
|
||||
@@ -112,7 +133,10 @@ export class RemissionState {
|
||||
}
|
||||
|
||||
@Action(actions.SetRemissionScannedContainerId)
|
||||
setRemissionScannedContainerId(ctx: StateContext<RemissionStateModel>, { containerId }: actions.SetRemissionScannedContainerId) {
|
||||
setRemissionScannedContainerId(
|
||||
ctx: StateContext<RemissionStateModel>,
|
||||
{ containerId }: actions.SetRemissionScannedContainerId
|
||||
) {
|
||||
const state = ctx.getState();
|
||||
const currentRemission = state.remission;
|
||||
const remission: Remission = {
|
||||
@@ -124,7 +148,10 @@ export class RemissionState {
|
||||
}
|
||||
|
||||
@Action(actions.SetRemissionTarget)
|
||||
setRemissionTarget(ctx: StateContext<RemissionStateModel>, { target, source }: actions.SetRemissionTarget) {
|
||||
setRemissionTarget(
|
||||
ctx: StateContext<RemissionStateModel>,
|
||||
{ target, source }: actions.SetRemissionTarget
|
||||
) {
|
||||
const state = ctx.getState();
|
||||
const currentRemission = state.remission;
|
||||
let remission: Remission;
|
||||
@@ -137,7 +164,10 @@ export class RemissionState {
|
||||
}
|
||||
|
||||
@Action(actions.SetRemissionSource)
|
||||
setRemissionSource(ctx: StateContext<RemissionStateModel>, { source }: actions.SetRemissionSource) {
|
||||
setRemissionSource(
|
||||
ctx: StateContext<RemissionStateModel>,
|
||||
{ source }: actions.SetRemissionSource
|
||||
) {
|
||||
const state = ctx.getState();
|
||||
const currentRemission = state.remission;
|
||||
const remission: Remission = {
|
||||
@@ -156,7 +186,10 @@ export class RemissionState {
|
||||
}
|
||||
|
||||
@Action(actions.SetRemissionProducts)
|
||||
setRemissionProducts(ctx: StateContext<RemissionStateModel>, { products }: actions.SetRemissionProducts) {
|
||||
setRemissionProducts(
|
||||
ctx: StateContext<RemissionStateModel>,
|
||||
{ products }: actions.SetRemissionProducts
|
||||
) {
|
||||
const state = ctx.getState();
|
||||
const currentRemission = state.remission;
|
||||
const remission: Remission = {
|
||||
@@ -168,7 +201,10 @@ export class RemissionState {
|
||||
}
|
||||
|
||||
@Action(actions.SetRemissionSearchedProduct)
|
||||
setRemissionSearchedProduct(ctx: StateContext<RemissionStateModel>, { product }: actions.SetRemissionSearchedProduct) {
|
||||
setRemissionSearchedProduct(
|
||||
ctx: StateContext<RemissionStateModel>,
|
||||
{ product }: actions.SetRemissionSearchedProduct
|
||||
) {
|
||||
const state = ctx.getState();
|
||||
const currentRemission = state.remission;
|
||||
const remission: Remission = {
|
||||
@@ -206,7 +242,10 @@ export class RemissionState {
|
||||
}
|
||||
|
||||
@Action(actions.CompleteRemissionShippingDocument)
|
||||
completeRemissionShippingDocument(ctx: StateContext<RemissionStateModel>, { shippingDocument }: actions.SetRemissionShippingDocument) {
|
||||
completeRemissionShippingDocument(
|
||||
ctx: StateContext<RemissionStateModel>,
|
||||
{ shippingDocument }: actions.SetRemissionShippingDocument
|
||||
) {
|
||||
const state = ctx.getState();
|
||||
const currentRemission = state.remission;
|
||||
const currentRemissionProcess = currentRemission.remissionProcess;
|
||||
@@ -216,7 +255,9 @@ export class RemissionState {
|
||||
|
||||
if (currentRemissionProcess.shippingDocuments) {
|
||||
const shippingDocumentAlreadyExists =
|
||||
currentRemissionProcess.shippingDocuments.findIndex((sd) => sd.id === shippingDocument.id) !== -1;
|
||||
currentRemissionProcess.shippingDocuments.findIndex(
|
||||
(sd) => sd.id === shippingDocument.id
|
||||
) !== -1;
|
||||
if (shippingDocumentAlreadyExists) {
|
||||
updatedRemissionProcess = {
|
||||
...updatedRemissionProcess,
|
||||
@@ -236,7 +277,8 @@ export class RemissionState {
|
||||
}
|
||||
const remission: Remission = {
|
||||
...currentRemission,
|
||||
remissionFinishingProcessStatus: RemissionFinishingProcessStatus.containerScanned,
|
||||
remissionFinishingProcessStatus:
|
||||
RemissionFinishingProcessStatus.containerScanned,
|
||||
remissionProcess: updatedRemissionProcess,
|
||||
};
|
||||
ctx.patchState({ remission });
|
||||
@@ -244,7 +286,10 @@ export class RemissionState {
|
||||
}
|
||||
|
||||
@Action(actions.DeleteRemissionShippingDocument)
|
||||
deleteShippingDocument(ctx: StateContext<RemissionStateModel>, { remissionProcessId }: actions.DeleteRemissionShippingDocument) {
|
||||
deleteShippingDocument(
|
||||
ctx: StateContext<RemissionStateModel>,
|
||||
{ remissionProcessId }: actions.DeleteRemissionShippingDocument
|
||||
) {
|
||||
const state = ctx.getState();
|
||||
const currentRemission = state.remission;
|
||||
|
||||
@@ -260,7 +305,10 @@ export class RemissionState {
|
||||
}
|
||||
|
||||
@Action(actions.SetRemissionShippingDocument)
|
||||
setRemissionShippingDocument(ctx: StateContext<RemissionStateModel>, { shippingDocument }: actions.SetRemissionShippingDocument) {
|
||||
setRemissionShippingDocument(
|
||||
ctx: StateContext<RemissionStateModel>,
|
||||
{ shippingDocument }: actions.SetRemissionShippingDocument
|
||||
) {
|
||||
const state = ctx.getState();
|
||||
const currentRemission = state.remission;
|
||||
const currentRemissionProcess = currentRemission.remissionProcess;
|
||||
@@ -268,7 +316,9 @@ export class RemissionState {
|
||||
|
||||
if (currentRemissionProcess.shippingDocuments) {
|
||||
const shippingDocumentAlreadyExists =
|
||||
currentRemissionProcess.shippingDocuments.findIndex((sd) => sd.id === shippingDocument.id) !== -1;
|
||||
currentRemissionProcess.shippingDocuments.findIndex(
|
||||
(sd) => sd.id === shippingDocument.id
|
||||
) !== -1;
|
||||
if (shippingDocumentAlreadyExists) {
|
||||
updatedRemissionProcess = {
|
||||
...currentRemission.remissionProcess,
|
||||
@@ -284,13 +334,19 @@ export class RemissionState {
|
||||
} else {
|
||||
updatedRemissionProcess = {
|
||||
...currentRemission.remissionProcess,
|
||||
shippingDocuments: [...currentRemission.remissionProcess.shippingDocuments, shippingDocument],
|
||||
shippingDocuments: [
|
||||
...currentRemission.remissionProcess.shippingDocuments,
|
||||
shippingDocument,
|
||||
],
|
||||
};
|
||||
}
|
||||
} else {
|
||||
updatedRemissionProcess = {
|
||||
...currentRemission.remissionProcess,
|
||||
shippingDocuments: [...currentRemission.remissionProcess.shippingDocuments, shippingDocument],
|
||||
shippingDocuments: [
|
||||
...currentRemission.remissionProcess.shippingDocuments,
|
||||
shippingDocument,
|
||||
],
|
||||
};
|
||||
}
|
||||
const remission: Remission = {
|
||||
@@ -363,7 +419,10 @@ export class RemissionState {
|
||||
}
|
||||
|
||||
@Action(actions.ReloadRemission)
|
||||
reload(ctx: StateContext<RemissionStateModel>, { remission }: actions.ReloadRemission) {
|
||||
reload(
|
||||
ctx: StateContext<RemissionStateModel>,
|
||||
{ remission }: actions.ReloadRemission
|
||||
) {
|
||||
ctx.patchState({
|
||||
remission: {
|
||||
...remission,
|
||||
@@ -375,7 +434,10 @@ export class RemissionState {
|
||||
}
|
||||
|
||||
@Action(actions.SetAllOpenExistingRemissions)
|
||||
setAllOpenExistingRemissions(ctx: StateContext<RemissionStateModel>, { remissionProcesses }: actions.SetAllOpenExistingRemissions) {
|
||||
setAllOpenExistingRemissions(
|
||||
ctx: StateContext<RemissionStateModel>,
|
||||
{ remissionProcesses }: actions.SetAllOpenExistingRemissions
|
||||
) {
|
||||
const state = ctx.getState();
|
||||
const currentExistingRemissions = state.existingRemissions;
|
||||
const existingRemissions: RemissionExistingOverview = {
|
||||
@@ -387,7 +449,10 @@ export class RemissionState {
|
||||
}
|
||||
|
||||
@Action(actions.SetAllExistingRemissions)
|
||||
setAllExistingRemissions(ctx: StateContext<RemissionStateModel>, { remissionProcesses }: actions.SetAllExistingRemissions) {
|
||||
setAllExistingRemissions(
|
||||
ctx: StateContext<RemissionStateModel>,
|
||||
{ remissionProcesses }: actions.SetAllExistingRemissions
|
||||
) {
|
||||
const state = ctx.getState();
|
||||
const currentExistingRemissions = state.existingRemissions;
|
||||
const existingRemissions: RemissionExistingOverview = {
|
||||
@@ -407,7 +472,10 @@ export class RemissionState {
|
||||
}
|
||||
|
||||
@Action(actions.SetRemissionFilter)
|
||||
setRemissionFilter(ctx: StateContext<RemissionStateModel>, { filter }: actions.SetRemissionFilter) {
|
||||
setRemissionFilter(
|
||||
ctx: StateContext<RemissionStateModel>,
|
||||
{ filter }: actions.SetRemissionFilter
|
||||
) {
|
||||
const state = ctx.getState();
|
||||
const remissionState = state.remission;
|
||||
|
||||
@@ -423,13 +491,15 @@ export class RemissionState {
|
||||
|
||||
ctx.patchState({
|
||||
remission,
|
||||
isLoading: false,
|
||||
});
|
||||
this.syncApiState(remission);
|
||||
}
|
||||
|
||||
@Action(actions.RequestUpdateRemissionFilter)
|
||||
setIsLoadingFilters(ctx: StateContext<RemissionStateModel>, { filter }: actions.RequestUpdateRemissionFilter) {
|
||||
setIsLoadingFilters(
|
||||
ctx: StateContext<RemissionStateModel>,
|
||||
{ filter }: actions.RequestUpdateRemissionFilter
|
||||
) {
|
||||
const state = ctx.getState();
|
||||
const remissionState = state.remission;
|
||||
|
||||
@@ -441,8 +511,10 @@ export class RemissionState {
|
||||
...remissionState.remissionProcess,
|
||||
filter: {
|
||||
...remissionState.remissionProcess.filter,
|
||||
source: filter.source || remissionState.remissionProcess.filter.source,
|
||||
target: filter.target || remissionState.remissionProcess.filter.target,
|
||||
source:
|
||||
filter.source || remissionState.remissionProcess.filter.source,
|
||||
target:
|
||||
filter.target || remissionState.remissionProcess.filter.target,
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -452,11 +524,13 @@ export class RemissionState {
|
||||
remission,
|
||||
isLoading: true,
|
||||
});
|
||||
this.syncApiState(remission);
|
||||
}
|
||||
|
||||
@Action(actions.SetRemissionIsLoading)
|
||||
setIsLoading(ctx: StateContext<RemissionStateModel>, { isLoading }: actions.SetRemissionIsLoading) {
|
||||
setIsLoading(
|
||||
ctx: StateContext<RemissionStateModel>,
|
||||
{ isLoading }: actions.SetRemissionIsLoading
|
||||
) {
|
||||
const state = ctx.getState();
|
||||
|
||||
ctx.patchState({
|
||||
@@ -464,4 +538,21 @@ export class RemissionState {
|
||||
isLoading,
|
||||
});
|
||||
}
|
||||
|
||||
@Action(actions.SetRemissionActiveView)
|
||||
setActiveView(
|
||||
ctx: StateContext<RemissionStateModel>,
|
||||
{ activeView }: actions.SetRemissionActiveView
|
||||
) {
|
||||
const state = ctx.getState();
|
||||
const remissionState = state.remission;
|
||||
|
||||
ctx.patchState({
|
||||
...state,
|
||||
remission: {
|
||||
...remissionState,
|
||||
activeView,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
import { Component, EventEmitter, OnInit, ViewChild, OnDestroy, Output } from '@angular/core';
|
||||
import {
|
||||
Component,
|
||||
EventEmitter,
|
||||
OnInit,
|
||||
ViewChild,
|
||||
OnDestroy,
|
||||
Output,
|
||||
} from '@angular/core';
|
||||
import { BarcodeFormat, Result } from '@zxing/library';
|
||||
import { ZXingScannerComponent } from '@zxing/ngx-scanner';
|
||||
import { Subject } from 'rxjs';
|
||||
@@ -7,13 +14,18 @@ import { takeUntil } from 'rxjs/operators';
|
||||
@Component({
|
||||
selector: 'app-barcode-scanner',
|
||||
templateUrl: 'barcode-scanner.component.html',
|
||||
styleUrls: ['barcode-scanner.component.scss']
|
||||
styleUrls: ['barcode-scanner.component.scss'],
|
||||
})
|
||||
export class BarcodeScannerComponent implements OnInit, OnDestroy {
|
||||
@Output() scan: EventEmitter<string> = new EventEmitter();
|
||||
@ViewChild('scanner', { static: false }) scanner: ZXingScannerComponent;
|
||||
@ViewChild('scanner', { static: true }) scanner: ZXingScannerComponent;
|
||||
|
||||
allowedFormats = [BarcodeFormat.QR_CODE, BarcodeFormat.EAN_13, BarcodeFormat.CODE_128, BarcodeFormat.UPC_A];
|
||||
allowedFormats = [
|
||||
BarcodeFormat.QR_CODE,
|
||||
BarcodeFormat.EAN_13,
|
||||
BarcodeFormat.CODE_128,
|
||||
BarcodeFormat.UPC_A,
|
||||
];
|
||||
enabled = false;
|
||||
debugMode = true;
|
||||
code = '';
|
||||
@@ -24,11 +36,17 @@ export class BarcodeScannerComponent implements OnInit, OnDestroy {
|
||||
destroy$ = new Subject();
|
||||
|
||||
ngOnInit() {
|
||||
this.scanner.camerasNotFound.pipe(takeUntil(this.destroy$)).subscribe(() => (this.hasDevices = false));
|
||||
this.scanner.scanComplete.pipe(takeUntil(this.destroy$)).subscribe(result => this.scanSuccess(result));
|
||||
this.scanner.permissionResponse.pipe(takeUntil(this.destroy$)).subscribe((perm: boolean) => {
|
||||
this.hasPermission = perm;
|
||||
});
|
||||
this.scanner.camerasNotFound
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe(() => (this.hasDevices = false));
|
||||
this.scanner.scanComplete
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe((result) => this.scanSuccess(result));
|
||||
this.scanner.permissionResponse
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe((perm: boolean) => {
|
||||
this.hasPermission = perm;
|
||||
});
|
||||
}
|
||||
|
||||
scanSuccess(result: Result) {
|
||||
|
||||
@@ -74,7 +74,7 @@ export class CustomerCardBarcodeSearchComponent implements OnInit, OnDestroy, Af
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe(
|
||||
(customers: CustomerInfoDTO[]) => {
|
||||
if (customers.length > 0) {
|
||||
if (Array.isArray(customers) && customers.length > 0) {
|
||||
this.store.dispatch(
|
||||
new AddBreadcrumb(
|
||||
{
|
||||
|
||||
@@ -1,11 +1,21 @@
|
||||
import { Component, OnInit, ChangeDetectionStrategy, ChangeDetectorRef, OnDestroy, Output, EventEmitter, Input, ViewRef } from '@angular/core';
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
OnDestroy,
|
||||
Output,
|
||||
EventEmitter,
|
||||
Input,
|
||||
ViewRef,
|
||||
} from '@angular/core';
|
||||
import { Cart } from '../../../../core/models/cart.model';
|
||||
import { DeliveryOption } from '../../../../core/models/delivery-option.model';
|
||||
import {
|
||||
UpdateCustomerFormState,
|
||||
ChangeCurrentRoute,
|
||||
SetDetailsCustomer,
|
||||
RemoveSelectedItem
|
||||
RemoveSelectedItem,
|
||||
} from '../../../../core/store/actions/process.actions';
|
||||
import { CustomerFormState } from '../../../../core/models/process.model';
|
||||
import { Store, Select } from '@ngxs/store';
|
||||
@@ -22,12 +32,11 @@ import { isArrayMinLength } from '../../../../core/utils/app.utils';
|
||||
import { DeleteProductFromCart } from 'apps/sales/src/app/core/store/actions/cart.actions';
|
||||
import { cartItem } from 'apps/sales/src/app/shared/animations/cart-item.animation';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-delivery-cart',
|
||||
templateUrl: './delivery-cart.component.html',
|
||||
styleUrls: ['./delivery-cart.component.scss'],
|
||||
animations: [cartItem]
|
||||
animations: [cartItem],
|
||||
})
|
||||
export class DeliveryCartComponent implements OnInit, OnDestroy {
|
||||
@Select(SharedSelectors.getCart) cartData$: Observable<ProcessCart>;
|
||||
@@ -40,23 +49,26 @@ export class DeliveryCartComponent implements OnInit, OnDestroy {
|
||||
lastDeletedItemIndex = -1;
|
||||
destroy$ = new Subject();
|
||||
|
||||
constructor(private store: Store, private router: Router, private cdr: ChangeDetectorRef) { }
|
||||
constructor(private store: Store, private router: Router, private cdr: ChangeDetectorRef) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.delivery$ = this.cartData$.pipe(
|
||||
filter(c => isArrayMinLength(c, 'cart', 0)),
|
||||
map(review => review.cart),
|
||||
filter((c) => isArrayMinLength(c, 'cart', 0)),
|
||||
map((review) => review.cart),
|
||||
map(deliveryFilter(DeliveryOption.DELIVERY)),
|
||||
map(cart => cartToCartReviewArray(cart))
|
||||
map((cart) => cartToCartReviewArray(cart))
|
||||
);
|
||||
|
||||
this.cartData$
|
||||
.pipe(
|
||||
filter(cart => !!cart),
|
||||
filter((cart) => !!cart),
|
||||
takeUntil(this.destroy$)
|
||||
)
|
||||
.subscribe(cart => {
|
||||
this.validateDeliveryAddress(cart);
|
||||
.subscribe((cart) => {
|
||||
if (!this.validateDeliveryAddress(cart)) {
|
||||
this.destroy$.next();
|
||||
return;
|
||||
}
|
||||
this.parseDeliveryAddress(cart);
|
||||
});
|
||||
}
|
||||
@@ -69,7 +81,10 @@ export class DeliveryCartComponent implements OnInit, OnDestroy {
|
||||
if (!cart.customer) {
|
||||
return null;
|
||||
}
|
||||
this.validateDeliveryAddress(cart);
|
||||
if (!this.validateDeliveryAddress(cart)) {
|
||||
this.destroy$.next();
|
||||
return;
|
||||
}
|
||||
const customer = cart.customer;
|
||||
if (customer.delivery_addres) {
|
||||
const { first_name, last_name, street, streetNo, zip, city } = customer.delivery_addres;
|
||||
@@ -90,7 +105,7 @@ export class DeliveryCartComponent implements OnInit, OnDestroy {
|
||||
const newBread: Breadcrumb = {
|
||||
name: 'Lieferadresse',
|
||||
path: currentRoute,
|
||||
queryParams: { cart: true, notEditable: true }
|
||||
queryParams: { cart: true, notEditable: true },
|
||||
};
|
||||
|
||||
this.store.dispatch(new SetDetailsCustomer(customerId));
|
||||
@@ -106,7 +121,7 @@ export class DeliveryCartComponent implements OnInit, OnDestroy {
|
||||
new AddBreadcrumb(
|
||||
{
|
||||
name: item.product.name ? item.product.name.substring(0, 12) + (item.product.name.length > 12 ? '...' : '') : '',
|
||||
path: '/product/details/' + item.id
|
||||
path: '/product/details/' + item.id,
|
||||
},
|
||||
'product'
|
||||
)
|
||||
@@ -126,8 +141,10 @@ export class DeliveryCartComponent implements OnInit, OnDestroy {
|
||||
this.store.dispatch(new UpdateCustomerFormState(CustomerFormState.MISSING_DELIVERY));
|
||||
const currentRoute = `customer/search/missing-data/${processCart.customer.id}`;
|
||||
this.store.dispatch(new ChangeCurrentRoute(currentRoute));
|
||||
this.router.navigate([currentRoute]);
|
||||
this.router.navigate([currentRoute], { queryParams: { card: 'create' } });
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
isAddressSet(deliveryAddress: Address) {
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
<app-modal id="address-sugestions" *ngIf="addresses && addresses.length > 0">
|
||||
<div class="modal-wrapper">
|
||||
<app-modal id="address-sugestions">
|
||||
<div class="modal-wrapper" *ngIf="addresses && addresses.length > 0">
|
||||
<div class="header">
|
||||
<lib-icon (click)="closeDialog()" height="21px" class="close-icon" name="close" alt="close"></lib-icon>
|
||||
<lib-icon
|
||||
(click)="closeDialog()"
|
||||
height="21px"
|
||||
class="close-icon"
|
||||
name="close"
|
||||
alt="close"
|
||||
></lib-icon>
|
||||
</div>
|
||||
<div class="title">
|
||||
<span>Bitte überprüfen Sie die eingegebenen Adressdaten</span>
|
||||
@@ -10,7 +16,10 @@
|
||||
<span>Vorschläge:</span>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="item-row" *ngFor="let address of addresses; let first = first">
|
||||
<div
|
||||
class="item-row"
|
||||
*ngFor="let address of addresses; let first = first"
|
||||
>
|
||||
<hr *ngIf="first" />
|
||||
<div class="row-container" *ngIf="address">
|
||||
<div class="data">
|
||||
@@ -24,7 +33,9 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-action">
|
||||
<app-button (click)="ignoreSuggestions()" [primary]="true">Eingegebene Adresse übernehmen</app-button>
|
||||
<app-button (click)="ignoreSuggestions()" [primary]="true"
|
||||
>Eingegebene Adresse übernehmen</app-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</app-modal>
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
import { Component, OnInit, OnDestroy, HostListener, Renderer, ChangeDetectorRef, ViewChild, AfterViewInit, ViewRef } from '@angular/core';
|
||||
import { FormGroup, FormBuilder, Validators, AbstractControl } from '@angular/forms';
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
OnDestroy,
|
||||
HostListener,
|
||||
ChangeDetectorRef,
|
||||
ViewChild,
|
||||
ViewRef,
|
||||
} from '@angular/core';
|
||||
import {
|
||||
FormGroup,
|
||||
FormBuilder,
|
||||
Validators,
|
||||
AbstractControl,
|
||||
} from '@angular/forms';
|
||||
import { Store, Select } from '@ngxs/store';
|
||||
import {
|
||||
ChangeCurrentRoute,
|
||||
@@ -8,14 +21,35 @@ import {
|
||||
SetOnlineCustomerCreationStatus,
|
||||
ClearPartOfCustomerCreationStatus,
|
||||
} from '../../../../core/store/actions/process.actions';
|
||||
import { User, Address, Features, Organisation } from '../../../../core/models/user.model';
|
||||
import {
|
||||
User,
|
||||
Address,
|
||||
Features,
|
||||
Organisation,
|
||||
} from '../../../../core/models/user.model';
|
||||
import { Router } from '@angular/router';
|
||||
import { Process, CustomerFormState } from '../../../../core/models/process.model';
|
||||
import {
|
||||
Process,
|
||||
CustomerFormState,
|
||||
} from '../../../../core/models/process.model';
|
||||
import { Breadcrumb } from '../../../../core/models/breadcrumb.model';
|
||||
import { Observable, Subject, of } from 'rxjs';
|
||||
import { AddBreadcrumb } from '../../../../core/store/actions/breadcrumb.actions';
|
||||
import { AddUser, SetUserDetails } from '../../../../core/store/actions/customer.actions';
|
||||
import { takeUntil, distinctUntilChanged, switchMap, map, filter, tap, share, debounceTime, take, catchError } from 'rxjs/operators';
|
||||
import {
|
||||
AddUser,
|
||||
SetUserDetails,
|
||||
} from '../../../../core/store/actions/customer.actions';
|
||||
import {
|
||||
takeUntil,
|
||||
distinctUntilChanged,
|
||||
switchMap,
|
||||
map,
|
||||
filter,
|
||||
tap,
|
||||
debounceTime,
|
||||
take,
|
||||
catchError,
|
||||
} from 'rxjs/operators';
|
||||
import { CustomValidators } from '../../../../shared/validation/custom-validation';
|
||||
import { GENDERS, TIITLES } from '../../dropdown-values';
|
||||
import { ProcessSelectors } from '../../../../core/store/selectors/process.selectors';
|
||||
@@ -23,7 +57,11 @@ import { SharedSelectors } from '../../../../core/store/selectors/shared.selecto
|
||||
import { EditCustomerData } from '../../../../core/models/edit-customer.model';
|
||||
import { CountrySelector } from '../../../../core/store/selectors/countries.selector';
|
||||
import { Country } from '../../../../core/models/country.model';
|
||||
import { isoDateFromString, scrollToFirstInvalidElement, isEmptyString } from '../../../../core/utils/app.utils';
|
||||
import {
|
||||
isoDateFromString,
|
||||
scrollToFirstInvalidElement,
|
||||
isEmptyString,
|
||||
} from '../../../../core/utils/app.utils';
|
||||
import { isNullOrUndefined } from 'util';
|
||||
import { DeliveryOption } from 'apps/sales/src/app/core/models/delivery-option.model';
|
||||
import { CustomerService } from 'apps/sales/src/app/core/services/customer.service';
|
||||
@@ -34,9 +72,17 @@ import { AddressSugestionsComponent } from '../address-sugestions/address-sugest
|
||||
|
||||
import { ErrorService } from 'apps/sales/src/app/core/error/component/error.service';
|
||||
import { FormsSelectors } from 'apps/sales/src/app/core/store/selectors/forms.selectors';
|
||||
import { SaveFormState, DeleteFormState } from 'apps/sales/src/app/core/store/actions/forms.actions';
|
||||
import { USER_FORM_STATE_KEY, USER_EXTRAS_FORM_STATE_KEY, USER_ERRORS_FORM_STATE_KEY } from 'apps/sales/src/app/core/utils/app.constants';
|
||||
import {
|
||||
SaveFormState,
|
||||
DeleteFormState,
|
||||
} from 'apps/sales/src/app/core/store/actions/forms.actions';
|
||||
import {
|
||||
USER_FORM_STATE_KEY,
|
||||
USER_EXTRAS_FORM_STATE_KEY,
|
||||
USER_ERRORS_FORM_STATE_KEY,
|
||||
} from 'apps/sales/src/app/core/utils/app.constants';
|
||||
import { AppSelectors } from 'apps/sales/src/app/core/store/selectors/app.selectors';
|
||||
import { CustomerHelperService } from '../../services/customer-helper.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-create-customer-card',
|
||||
@@ -45,20 +91,30 @@ import { AppSelectors } from 'apps/sales/src/app/core/store/selectors/app.select
|
||||
})
|
||||
export class CreateCustomerCardComponent implements OnInit, OnDestroy {
|
||||
@Select(ProcessSelectors.getProcesses) processes$: Observable<Process[]>;
|
||||
@Select(ProcessSelectors.getCurrentProcess) currentProcess$: Observable<Process>;
|
||||
@Select(SharedSelectors.getCustomerEditData) customerEditData$: Observable<EditCustomerData>;
|
||||
@Select(CountrySelector.getCountriesIterable) countries$: Observable<Country[]>;
|
||||
@Select(CountrySelector.getDeutchlandIterable) deutchland$: Observable<Country[]>;
|
||||
@Select(ProcessSelectors.getCurrentProcess) currentProcess$: Observable<
|
||||
Process
|
||||
>;
|
||||
@Select(SharedSelectors.getCustomerEditData) customerEditData$: Observable<
|
||||
EditCustomerData
|
||||
>;
|
||||
@Select(CountrySelector.getCountriesIterable) countries$: Observable<
|
||||
Country[]
|
||||
>;
|
||||
@Select(CountrySelector.getDeutchlandIterable) deutchland$: Observable<
|
||||
Country[]
|
||||
>;
|
||||
@ViewChild('postBtn', { static: false }) postBtn: ButtonComponent;
|
||||
@ViewChild('landDD', { static: false }) landDD: SelectComponent;
|
||||
@ViewChild('suggestions', { static: false }) suggestions: AddressSugestionsComponent;
|
||||
@ViewChild('suggestions', { static: false })
|
||||
suggestions: AddressSugestionsComponent;
|
||||
destroy$ = new Subject();
|
||||
destroyForm$ = new Subject();
|
||||
userForm: FormGroup;
|
||||
submitted = false;
|
||||
processes: Process[] = [];
|
||||
id: number;
|
||||
headerText = 'Wenn Sie möchten legen wir Ihnen gerne ein Onlinekonto an. Dort können Sie Ihre Bestellungen einsehen.';
|
||||
headerText =
|
||||
'Wenn Sie möchten legen wir Ihnen gerne ein Onlinekonto an. Dort können Sie Ihre Bestellungen einsehen.';
|
||||
|
||||
countryList: Country[];
|
||||
|
||||
@@ -96,6 +152,9 @@ export class CreateCustomerCardComponent implements OnInit, OnDestroy {
|
||||
lastEmailValueChecked: string;
|
||||
lastEmailCheckResult: any = null;
|
||||
flagsSetByConditions = false;
|
||||
customerFormState: CustomerFormState = CustomerFormState.CREATE;
|
||||
firstLoad = true;
|
||||
formListenerSet = false;
|
||||
|
||||
addressSuggestions: Address[];
|
||||
addressFields = {
|
||||
@@ -128,12 +187,12 @@ export class CreateCustomerCardComponent implements OnInit, OnDestroy {
|
||||
private fb: FormBuilder,
|
||||
private store: Store,
|
||||
private router: Router,
|
||||
private renderer: Renderer,
|
||||
private customerService: CustomerService,
|
||||
private cdrf: ChangeDetectorRef,
|
||||
private appService: AppService,
|
||||
private errorService: ErrorService
|
||||
) { }
|
||||
private errorService: ErrorService,
|
||||
private customerHelper: CustomerHelperService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.isIPad = this.appService.isIPadEnv();
|
||||
@@ -144,13 +203,13 @@ export class CreateCustomerCardComponent implements OnInit, OnDestroy {
|
||||
this.store
|
||||
.select(SharedSelectors.cartHasItemsFor)
|
||||
.pipe(
|
||||
takeUntil(this.destroy$),
|
||||
distinctUntilChanged(),
|
||||
tap(this.processTypes),
|
||||
switchMap(() => this.customerEditData$),
|
||||
filter((data) => !isNullOrUndefined(data)),
|
||||
switchMap(this.customerToProcessSwitcher),
|
||||
filter((data) => !isNullOrUndefined(data))
|
||||
filter((data) => !isNullOrUndefined(data)),
|
||||
takeUntil(this.destroy$)
|
||||
)
|
||||
.subscribe(this.initializationSubscriptionHandler);
|
||||
|
||||
@@ -167,6 +226,21 @@ export class CreateCustomerCardComponent implements OnInit, OnDestroy {
|
||||
this.loadExtrasState();
|
||||
this.loadErrorsState();
|
||||
});
|
||||
|
||||
this.customerHelper.customerUpdateRedirect$
|
||||
.pipe(debounceTime(2000), take(1))
|
||||
.subscribe(() => {
|
||||
if (
|
||||
this.customerFormState === CustomerFormState.MISSING_DELIVERY ||
|
||||
(this.customerFormState === CustomerFormState.MISSING_ONLINE &&
|
||||
this.userForm.value.first_name !== undefined)
|
||||
) {
|
||||
this.valueChangesListenerImplementation(this.userForm.value);
|
||||
setTimeout(() => {
|
||||
this.updateFormLabelsState(null);
|
||||
}, 50);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
isAddressField = (field: string): boolean => {
|
||||
@@ -187,15 +261,24 @@ export class CreateCustomerCardComponent implements OnInit, OnDestroy {
|
||||
this.cartHasItemsForTakeNow = cartHasItemsFor(DeliveryOption.TAKE_NOW);
|
||||
this.cartHasItemsForDownload = cartHasItemsFor(DeliveryOption.DOWNLOAD);
|
||||
|
||||
this.cartHasItemsForDownload.ifTrue(() => {
|
||||
this.onlineChecked = true;
|
||||
this.guestChecked = false;
|
||||
this.flagsSetByConditions = true;
|
||||
this.headerText =
|
||||
'Um Ihnen das ebook bereitstellen zu können, brauchen Sie ein Onlinekonto bei Hugendubel. Wir richten es Ihnen gerne sofort ein.';
|
||||
});
|
||||
(this.cartHasItemsForDownload || this.cartHasItemsForDelivery).ifTrue(
|
||||
() => {
|
||||
this.onlineChecked = true;
|
||||
this.guestChecked = false;
|
||||
this.flagsSetByConditions = true;
|
||||
|
||||
(this.cartHasItemsForDelivery && !this.cartHasItemsForDownload && !this.onlineChecked).ifTrue(this.guestCheckboxInitialization);
|
||||
if (this.cartHasItemsForDownload) {
|
||||
this.headerText =
|
||||
'Um Ihnen das ebook bereitstellen zu können, brauchen Sie ein Onlinekonto bei Hugendubel. Wir richten es Ihnen gerne sofort ein.';
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
(
|
||||
this.cartHasItemsForDelivery &&
|
||||
!this.cartHasItemsForDownload &&
|
||||
!this.onlineChecked
|
||||
).ifTrue(this.guestCheckboxInitialization);
|
||||
};
|
||||
|
||||
guestCheckboxInitialization = () => {
|
||||
@@ -209,24 +292,34 @@ export class CreateCustomerCardComponent implements OnInit, OnDestroy {
|
||||
if (process) {
|
||||
this.backendValidation = false;
|
||||
clearTimeout(this.errorTimer);
|
||||
if (process.onlineCustomerCreationError && process.onlineCustomerCreationError.error === true) {
|
||||
if (
|
||||
process.onlineCustomerCreationError &&
|
||||
process.onlineCustomerCreationError.error === true
|
||||
) {
|
||||
this.buildCreateForm(this.fb);
|
||||
this.errorTimer = setTimeout(() => {
|
||||
this.onlineCustomerCreationErrorHandler(process.onlineCustomerCreationError.invalidProperties);
|
||||
this.onlineCustomerCreationErrorHandler(
|
||||
process.onlineCustomerCreationError.invalidProperties
|
||||
);
|
||||
}, 1000);
|
||||
this.loaded = true;
|
||||
return;
|
||||
}
|
||||
this.formState = process.customerFormState;
|
||||
this.canBeSetToActiveUser = !process.closeDirectlyTab;
|
||||
this.customerFormState = process.customerFormState;
|
||||
if (process.customerFormState === CustomerFormState.MISSING_DELIVERY) {
|
||||
this.userForm = this.buildSetDeliveryAddressForm(this.fb, this.customer.customer);
|
||||
this.buildSetDeliveryAddressForm(this.fb, this.customer.customer);
|
||||
this.customerHelper.customerUpdateRedirect();
|
||||
this.id = this.customer.customer.id;
|
||||
this.submitted = true;
|
||||
this.btnText = 'Weiter';
|
||||
this.create = false;
|
||||
} else if (process.customerFormState === CustomerFormState.MISSING_ONLINE) {
|
||||
this.userForm = this.buildSetDeliveryAddressForm(this.fb, this.customer.customer);
|
||||
} else if (
|
||||
process.customerFormState === CustomerFormState.MISSING_ONLINE
|
||||
) {
|
||||
this.buildSetDeliveryAddressForm(this.fb, this.customer.customer);
|
||||
this.customerHelper.customerUpdateRedirect();
|
||||
this.id = this.customer.customer.id;
|
||||
this.submitted = true;
|
||||
this.btnText = 'Weiter';
|
||||
@@ -246,20 +339,36 @@ export class CreateCustomerCardComponent implements OnInit, OnDestroy {
|
||||
};
|
||||
|
||||
private listenFormChanges() {
|
||||
// if (!this.formListenerSet) {
|
||||
this.destroyForm$.next();
|
||||
this.userForm.valueChanges.pipe(takeUntil(this.destroyForm$)).subscribe((v) => {
|
||||
const formStateString = JSON.stringify(v);
|
||||
const errors = {};
|
||||
Object.keys(v).forEach((field) => {
|
||||
errors[field] = this.userForm.get(field).errors;
|
||||
this.userForm.valueChanges
|
||||
.pipe(takeUntil(this.destroyForm$))
|
||||
.subscribe((v) => {
|
||||
this.valueChangesListenerImplementation(v);
|
||||
});
|
||||
this.store.dispatch(new SaveFormState(USER_FORM_STATE_KEY, formStateString));
|
||||
this.store.dispatch(new SaveFormState(USER_ERRORS_FORM_STATE_KEY, JSON.stringify(errors)));
|
||||
// this.formListenerSet = true;
|
||||
// }
|
||||
}
|
||||
|
||||
private valueChangesListenerImplementation(v: any) {
|
||||
const formStateString = JSON.stringify(v);
|
||||
const errors = {};
|
||||
Object.keys(v).forEach((field) => {
|
||||
errors[field] = this.userForm.get(field).errors;
|
||||
});
|
||||
this.store.dispatch(
|
||||
new SaveFormState(USER_FORM_STATE_KEY, formStateString)
|
||||
);
|
||||
this.store.dispatch(
|
||||
new SaveFormState(USER_ERRORS_FORM_STATE_KEY, JSON.stringify(errors))
|
||||
);
|
||||
}
|
||||
|
||||
onlineCustomerCreationErrorHandler(invalidProperties: any) {
|
||||
if (isNullOrUndefined(invalidProperties) || isNullOrUndefined(this.userForm)) {
|
||||
if (
|
||||
isNullOrUndefined(invalidProperties) ||
|
||||
isNullOrUndefined(this.userForm)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (invalidProperties.Email) {
|
||||
@@ -296,8 +405,15 @@ export class CreateCustomerCardComponent implements OnInit, OnDestroy {
|
||||
if (key !== 'Email' && key !== 'FirstName' && key !== 'LastName') {
|
||||
showErrorPopUp = true;
|
||||
}
|
||||
if (keysChecked === Object.keys(invalidProperties).length && showErrorPopUp) {
|
||||
this.errorService.addErrors(400, null, JSON.stringify(invalidProperties));
|
||||
if (
|
||||
keysChecked === Object.keys(invalidProperties).length &&
|
||||
showErrorPopUp
|
||||
) {
|
||||
this.errorService.addErrors(
|
||||
400,
|
||||
null,
|
||||
JSON.stringify(invalidProperties)
|
||||
);
|
||||
}
|
||||
});
|
||||
scrollToFirstInvalidElement();
|
||||
@@ -306,9 +422,12 @@ export class CreateCustomerCardComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
setValidations() {
|
||||
this.deliveryAndGuestOrOnline = this.cartHasItemsForDelivery && (this.guestChecked || this.onlineChecked);
|
||||
this.deliveryAndGuestOrOnline =
|
||||
this.cartHasItemsForDelivery && (this.guestChecked || this.onlineChecked);
|
||||
this.pickUpOrTakeNowAndOnline =
|
||||
(this.cartHasItemsForPickUp || this.cartHasItemsForTakeNow) && this.onlineChecked && !this.deliveryAndGuestOrOnline;
|
||||
(this.cartHasItemsForPickUp || this.cartHasItemsForTakeNow) &&
|
||||
this.onlineChecked &&
|
||||
!this.deliveryAndGuestOrOnline;
|
||||
this.downloadAndOnline = this.cartHasItemsForDownload && this.onlineChecked;
|
||||
(
|
||||
this.deliveryAndGuestOrOnline ||
|
||||
@@ -316,14 +435,20 @@ export class CreateCustomerCardComponent implements OnInit, OnDestroy {
|
||||
this.downloadAndOnline ||
|
||||
this.onlineChecked ||
|
||||
this.guestChecked
|
||||
).ifTrue(this.deliveryAndGuestValidationsOrpickUpOrTakeNowAndOnlineValidations);
|
||||
).ifTrue(
|
||||
this.deliveryAndGuestValidationsOrpickUpOrTakeNowAndOnlineValidations
|
||||
);
|
||||
this.onlineChecked.ifTrue(this.onlineExtraValidations);
|
||||
}
|
||||
|
||||
deliveryAndGuestValidationsOrpickUpOrTakeNowAndOnlineValidations = () => {
|
||||
this.userForm.get('gender').setValidators([Validators.required, CustomValidators.validateGender]);
|
||||
this.userForm
|
||||
.get('gender')
|
||||
.setValidators([Validators.required, CustomValidators.validateGender]);
|
||||
this.userForm.get('gender').updateValueAndValidity();
|
||||
this.userForm.get('email').setValidators([Validators.required, CustomValidators.validateEmail]);
|
||||
this.userForm
|
||||
.get('email')
|
||||
.setValidators([Validators.required, CustomValidators.validateEmail]);
|
||||
this.userForm.get('email').updateValueAndValidity();
|
||||
this.userForm.get('address').setValidators([Validators.required]);
|
||||
this.userForm.get('address').updateValueAndValidity();
|
||||
@@ -374,7 +499,9 @@ export class CreateCustomerCardComponent implements OnInit, OnDestroy {
|
||||
};
|
||||
|
||||
onlineExtraValidations = () => {
|
||||
this.userForm.get('email').setAsyncValidators(this.validateIfEmailExists.bind(this));
|
||||
this.userForm
|
||||
.get('email')
|
||||
.setAsyncValidators(this.validateIfEmailExists.bind(this));
|
||||
this.userForm.get('email').updateValueAndValidity();
|
||||
};
|
||||
|
||||
@@ -394,8 +521,11 @@ export class CreateCustomerCardComponent implements OnInit, OnDestroy {
|
||||
this.userForm.get('city').updateValueAndValidity();
|
||||
this.userForm.get('country').clearValidators();
|
||||
this.userForm.get('country').updateValueAndValidity();
|
||||
this.deliveryAndGuestOrOnline = this.cartHasItemsForDelivery && (this.guestChecked || this.onlineChecked);
|
||||
this.pickUpOrTakeNowAndOnline = (this.cartHasItemsForPickUp || this.cartHasItemsForTakeNow) && this.onlineChecked;
|
||||
this.deliveryAndGuestOrOnline =
|
||||
this.cartHasItemsForDelivery && (this.guestChecked || this.onlineChecked);
|
||||
this.pickUpOrTakeNowAndOnline =
|
||||
(this.cartHasItemsForPickUp || this.cartHasItemsForTakeNow) &&
|
||||
this.onlineChecked;
|
||||
};
|
||||
|
||||
validateIfEmailExists(control: AbstractControl) {
|
||||
@@ -431,7 +561,12 @@ export class CreateCustomerCardComponent implements OnInit, OnDestroy {
|
||||
errors[field] = this.userForm.get(field).errors;
|
||||
}
|
||||
});
|
||||
this.store.dispatch(new SaveFormState(USER_ERRORS_FORM_STATE_KEY, JSON.stringify(errors)));
|
||||
this.store.dispatch(
|
||||
new SaveFormState(
|
||||
USER_ERRORS_FORM_STATE_KEY,
|
||||
JSON.stringify(errors)
|
||||
)
|
||||
);
|
||||
result$.next(result);
|
||||
});
|
||||
}, 2000);
|
||||
@@ -511,11 +646,21 @@ export class CreateCustomerCardComponent implements OnInit, OnDestroy {
|
||||
const addressesLength = address.length;
|
||||
address.forEach((add) => {
|
||||
processedItems++;
|
||||
const street = add.street ? add.street : (this.userForm.get('address').value as string);
|
||||
const streetNo = add.streetNumber ? add.streetNumber : (this.userForm.get('streetNo').value as string);
|
||||
const zipCode = add.zipCode ? add.zipCode : (this.userForm.get('zipCode').value as string);
|
||||
const city = add.city ? add.city : (this.userForm.get('city').value as string);
|
||||
const countryValue = add.country ? add.country : (this.userForm.get('country').value as string);
|
||||
const street = add.street
|
||||
? add.street
|
||||
: (this.userForm.get('address').value as string);
|
||||
const streetNo = add.streetNumber
|
||||
? add.streetNumber
|
||||
: (this.userForm.get('streetNo').value as string);
|
||||
const zipCode = add.zipCode
|
||||
? add.zipCode
|
||||
: (this.userForm.get('zipCode').value as string);
|
||||
const city = add.city
|
||||
? add.city
|
||||
: (this.userForm.get('city').value as string);
|
||||
const countryValue = add.country
|
||||
? add.country
|
||||
: (this.userForm.get('country').value as string);
|
||||
|
||||
if (city && street && streetNo && zipCode && countryValue) {
|
||||
this.addressSuggestions.push({
|
||||
@@ -529,7 +674,10 @@ export class CreateCustomerCardComponent implements OnInit, OnDestroy {
|
||||
zip: zipCode,
|
||||
});
|
||||
}
|
||||
if (processedItems === addressesLength && this.addressSuggestions.length === addressesLength) {
|
||||
if (
|
||||
processedItems === addressesLength &&
|
||||
this.addressSuggestions.length === addressesLength
|
||||
) {
|
||||
this.openSuggestions();
|
||||
}
|
||||
});
|
||||
@@ -613,10 +761,10 @@ export class CreateCustomerCardComponent implements OnInit, OnDestroy {
|
||||
this.store
|
||||
.dispatch(new AddUser(data, this.canBeSetToActiveUser))
|
||||
.pipe(
|
||||
take(1),
|
||||
switchMap(() => {
|
||||
return this.store.select(ProcessSelectors.getDetailsUserId);
|
||||
}),
|
||||
take(1),
|
||||
catchError((error) => {
|
||||
this.postBtn.stopLoading();
|
||||
scrollToFirstInvalidElement();
|
||||
@@ -656,7 +804,9 @@ export class CreateCustomerCardComponent implements OnInit, OnDestroy {
|
||||
.toPromise()
|
||||
.then(() => {
|
||||
const currentRoute = 'cart/review';
|
||||
this.store.dispatch(new UpdateCustomerFormState(CustomerFormState.CREATE));
|
||||
this.store.dispatch(
|
||||
new UpdateCustomerFormState(CustomerFormState.CREATE)
|
||||
);
|
||||
this.store.dispatch(new ChangeCurrentRoute(currentRoute));
|
||||
this.postBtn.stopLoading();
|
||||
this.router.navigate([currentRoute]);
|
||||
@@ -664,9 +814,11 @@ export class CreateCustomerCardComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
loadProcesses() {
|
||||
this.processes$.pipe(takeUntil(this.destroy$)).subscribe((data: Process[]) => {
|
||||
this.processes = data;
|
||||
});
|
||||
this.processes$
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe((data: Process[]) => {
|
||||
this.processes = data;
|
||||
});
|
||||
}
|
||||
|
||||
createProcess() {
|
||||
@@ -709,7 +861,9 @@ export class CreateCustomerCardComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
setAddressValidators = () => {
|
||||
this.userForm.valueChanges.pipe(distinctUntilChanged(), take(1)).subscribe(this.addressValidatorForSimpleCustomer);
|
||||
this.userForm.valueChanges
|
||||
.pipe(distinctUntilChanged(), take(1))
|
||||
.subscribe(this.addressValidatorForSimpleCustomer);
|
||||
};
|
||||
|
||||
get f() {
|
||||
@@ -795,15 +949,29 @@ export class CreateCustomerCardComponent implements OnInit, OnDestroy {
|
||||
null,
|
||||
this.userForm.get('firstName').value,
|
||||
this.userForm.get('lastName').value,
|
||||
this.userForm.get('address').value ? this.userForm.get('address').value : '',
|
||||
this.userForm.get('streetNo').value ? this.userForm.get('streetNo').value : '',
|
||||
+this.userForm.get('zipCode').value ? this.userForm.get('zipCode').value : '',
|
||||
this.userForm.get('address').value
|
||||
? this.userForm.get('address').value
|
||||
: '',
|
||||
this.userForm.get('streetNo').value
|
||||
? this.userForm.get('streetNo').value
|
||||
: '',
|
||||
+this.userForm.get('zipCode').value
|
||||
? this.userForm.get('zipCode').value
|
||||
: '',
|
||||
this.userForm.get('city').value ? this.userForm.get('city').value : '',
|
||||
this.userForm.get('country').value ? this.getCountryCode(this.userForm.get('country').value) : '',
|
||||
this.userForm.get('company').value ? this.userForm.get('company').value : '',
|
||||
this.userForm.get('department').value ? this.userForm.get('department').value : '',
|
||||
this.userForm.get('country').value
|
||||
? this.getCountryCode(this.userForm.get('country').value)
|
||||
: '',
|
||||
this.userForm.get('company').value
|
||||
? this.userForm.get('company').value
|
||||
: '',
|
||||
this.userForm.get('department').value
|
||||
? this.userForm.get('department').value
|
||||
: '',
|
||||
this.userForm.get('note').value ? this.userForm.get('note').value : '',
|
||||
this.userForm.get('tax_number').value ? this.userForm.get('tax_number').value : '',
|
||||
this.userForm.get('tax_number').value
|
||||
? this.userForm.get('tax_number').value
|
||||
: '',
|
||||
this.userForm.get('title').value ? this.userForm.get('title').value : '',
|
||||
this.userForm.get('gender').value ? this.userForm.get('gender').value : ''
|
||||
);
|
||||
@@ -834,10 +1002,18 @@ export class CreateCustomerCardComponent implements OnInit, OnDestroy {
|
||||
let organisation: Organisation;
|
||||
if (this.showCompanyFields) {
|
||||
organisation = {
|
||||
name: this.userForm.get('company').value ? this.userForm.get('company').value : undefined,
|
||||
department: this.userForm.get('department').value ? this.userForm.get('department').value : undefined,
|
||||
extraAddress: this.userForm.get('note').value ? this.userForm.get('note').value : undefined,
|
||||
vatId: this.userForm.get('tax_number').value ? this.userForm.get('tax_number').value : undefined,
|
||||
name: this.userForm.get('company').value
|
||||
? this.userForm.get('company').value
|
||||
: undefined,
|
||||
department: this.userForm.get('department').value
|
||||
? this.userForm.get('department').value
|
||||
: undefined,
|
||||
extraAddress: this.userForm.get('note').value
|
||||
? this.userForm.get('note').value
|
||||
: undefined,
|
||||
vatId: this.userForm.get('tax_number').value
|
||||
? this.userForm.get('tax_number').value
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -845,16 +1021,26 @@ export class CreateCustomerCardComponent implements OnInit, OnDestroy {
|
||||
id: this.id,
|
||||
first_name: this.userForm.get('firstName').value,
|
||||
last_name: this.userForm.get('lastName').value,
|
||||
title: this.userForm.get('title').value ? this.userForm.get('title').value : '',
|
||||
gender: this.userForm.get('gender').value ? this.userForm.get('gender').value : '',
|
||||
mobile_number: this.userForm.get('mobile').value ? this.userForm.get('mobile').value.replace(' ', '') : '',
|
||||
phone_number: this.userForm.get('telephone').value ? this.userForm.get('telephone').value.replace(' ', '') : '',
|
||||
title: this.userForm.get('title').value
|
||||
? this.userForm.get('title').value
|
||||
: '',
|
||||
gender: this.userForm.get('gender').value
|
||||
? this.userForm.get('gender').value
|
||||
: '',
|
||||
mobile_number: this.userForm.get('mobile').value
|
||||
? this.userForm.get('mobile').value.replace(' ', '')
|
||||
: '',
|
||||
phone_number: this.userForm.get('telephone').value
|
||||
? this.userForm.get('telephone').value.replace(' ', '')
|
||||
: '',
|
||||
date_of_birth: isoDateFromString(this.userForm.get('dateOfBirth').value),
|
||||
delivery_addres: address,
|
||||
poossible_invoice_addresses: [address],
|
||||
invoice_address: address,
|
||||
poossible_delivery_addresses: [address],
|
||||
email: this.userForm.get('email').value ? this.userForm.get('email').value.trim() : '',
|
||||
email: this.userForm.get('email').value
|
||||
? this.userForm.get('email').value.trim()
|
||||
: '',
|
||||
shop: Math.floor(Math.random() * 6) + 1 > 3 ? true : false,
|
||||
newUser: true,
|
||||
features: features,
|
||||
@@ -864,7 +1050,11 @@ export class CreateCustomerCardComponent implements OnInit, OnDestroy {
|
||||
return data;
|
||||
}
|
||||
|
||||
private updateDataForSelectedOption(field: string, value: string, defaultValue: string) {
|
||||
private updateDataForSelectedOption(
|
||||
field: string,
|
||||
value: string,
|
||||
defaultValue: string
|
||||
) {
|
||||
if (value !== defaultValue) {
|
||||
this.userForm.get(field).patchValue(value);
|
||||
} else {
|
||||
@@ -873,33 +1063,140 @@ export class CreateCustomerCardComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
private buildSetDeliveryAddressForm(fb: FormBuilder, customer: User) {
|
||||
let country = this.getProperty(customer, 'delivery_addres.country');
|
||||
const countriesList = this.store.selectSnapshot(CountrySelector.getCountriesIterable);
|
||||
if (countriesList) {
|
||||
const countryObj = countriesList.find((c) => c.key === country);
|
||||
if (countryObj !== undefined) {
|
||||
country = countryObj.value;
|
||||
}
|
||||
}
|
||||
return fb.group({
|
||||
firstName: [this.getProperty(customer, 'first_name'), Validators.required],
|
||||
lastName: [this.getProperty(customer, 'last_name'), Validators.required],
|
||||
email: [this.getProperty(customer, 'email'), [CustomValidators.validateEmail, Validators.required]],
|
||||
address: [this.getProperty(customer, 'delivery_addres.street'), Validators.required],
|
||||
streetNo: [this.getProperty(customer, 'delivery_addres.streetNo'), Validators.required],
|
||||
zipCode: [this.getProperty(customer, 'delivery_addres.zip'), Validators.required],
|
||||
city: [this.getProperty(customer, 'delivery_addres.city'), Validators.required],
|
||||
dateOfBirth: [this.getProperty(customer, 'date_of_birth'), CustomValidators.validateDateOfBirth],
|
||||
mobile: [this.getProperty(customer, 'mobile_number'), CustomValidators.validateTelephone],
|
||||
telephone: [this.getProperty(customer, 'phone_number'), CustomValidators.validateTelephone],
|
||||
country: [country, Validators.required],
|
||||
gender: [this.getProperty(customer, 'gender')],
|
||||
title: [this.getProperty(customer, 'title')],
|
||||
company: [this.getProperty(customer, 'delivery_addres.company_name')],
|
||||
department: [this.getProperty(customer, 'delivery_addres.company_department')],
|
||||
tax_number: [this.getProperty(customer, 'delivery_addres.company_tax_number')],
|
||||
note: [this.getProperty(customer, 'delivery_addres.note')],
|
||||
});
|
||||
this.store
|
||||
.selectOnce(FormsSelectors.getFormState)
|
||||
.pipe(map((filterFn) => filterFn(USER_FORM_STATE_KEY)))
|
||||
.subscribe((value) => {
|
||||
if (
|
||||
this.customerFormState === CustomerFormState.MISSING_DELIVERY ||
|
||||
this.customerFormState === CustomerFormState.MISSING_ONLINE
|
||||
) {
|
||||
let data;
|
||||
if (this.firstLoad) {
|
||||
data = customer;
|
||||
} else {
|
||||
data = value ? JSON.parse(value) : undefined;
|
||||
}
|
||||
let country = this.getProperty(
|
||||
data,
|
||||
this.firstLoad ? 'delivery_addres.country' : 'country'
|
||||
);
|
||||
const countriesList = this.store.selectSnapshot(
|
||||
CountrySelector.getCountriesIterable
|
||||
);
|
||||
if (countriesList) {
|
||||
const countryObj = countriesList.find((c) => c.key === country);
|
||||
if (countryObj !== undefined) {
|
||||
country = countryObj.value;
|
||||
}
|
||||
}
|
||||
this.userForm = fb.group({
|
||||
firstName: [
|
||||
this.getProperty(
|
||||
data,
|
||||
this.firstLoad ? 'first_name' : 'firstName'
|
||||
),
|
||||
Validators.required,
|
||||
],
|
||||
lastName: [
|
||||
this.getProperty(data, this.firstLoad ? 'last_name' : 'lastName'),
|
||||
Validators.required,
|
||||
],
|
||||
email: [
|
||||
this.getProperty(data, 'email'),
|
||||
[CustomValidators.validateEmail, Validators.required],
|
||||
],
|
||||
address: [
|
||||
this.getProperty(
|
||||
data,
|
||||
this.firstLoad ? 'delivery_addres.street' : 'address'
|
||||
),
|
||||
Validators.required,
|
||||
],
|
||||
streetNo: [
|
||||
this.getProperty(
|
||||
data,
|
||||
this.firstLoad ? 'delivery_addres.streetNo' : 'streetNo'
|
||||
),
|
||||
Validators.required,
|
||||
],
|
||||
zipCode: [
|
||||
this.getProperty(
|
||||
data,
|
||||
this.firstLoad ? 'delivery_addres.zip' : 'zipCode'
|
||||
),
|
||||
Validators.required,
|
||||
],
|
||||
city: [
|
||||
this.getProperty(
|
||||
data,
|
||||
this.firstLoad ? 'delivery_addres.city' : 'city'
|
||||
),
|
||||
Validators.required,
|
||||
],
|
||||
dateOfBirth: [
|
||||
this.getProperty(
|
||||
data,
|
||||
this.firstLoad ? 'date_of_birth' : 'dateOfBirth'
|
||||
),
|
||||
CustomValidators.validateDateOfBirth,
|
||||
],
|
||||
mobile: [
|
||||
this.getProperty(
|
||||
data,
|
||||
this.firstLoad ? 'mobile_number' : 'mobile'
|
||||
),
|
||||
CustomValidators.validateTelephone,
|
||||
],
|
||||
telephone: [
|
||||
this.getProperty(
|
||||
data,
|
||||
this.firstLoad ? 'phone_number' : 'telephone'
|
||||
),
|
||||
CustomValidators.validateTelephone,
|
||||
],
|
||||
country: [country, Validators.required],
|
||||
gender: [this.getProperty(data, 'gender')],
|
||||
title: [this.getProperty(data, 'title')],
|
||||
company: [
|
||||
this.getProperty(
|
||||
data,
|
||||
this.firstLoad ? 'delivery_addres.company_name' : 'company'
|
||||
),
|
||||
],
|
||||
department: [
|
||||
this.getProperty(
|
||||
data,
|
||||
this.firstLoad
|
||||
? 'delivery_addres.company_department'
|
||||
: 'department'
|
||||
),
|
||||
],
|
||||
tax_number: [
|
||||
this.getProperty(
|
||||
data,
|
||||
this.firstLoad
|
||||
? 'delivery_addres.company_tax_number'
|
||||
: 'tax_number'
|
||||
),
|
||||
],
|
||||
note: [
|
||||
this.getProperty(
|
||||
data,
|
||||
this.firstLoad ? 'delivery_addres.note' : 'note'
|
||||
),
|
||||
],
|
||||
});
|
||||
|
||||
if (this.firstLoad) {
|
||||
this.valueChangesListenerImplementation(this.userForm.value);
|
||||
this.firstLoad = false;
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.updateFormLabelsState(null);
|
||||
}, 50);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private buildCreateForm(fb: FormBuilder) {
|
||||
@@ -907,29 +1204,49 @@ export class CreateCustomerCardComponent implements OnInit, OnDestroy {
|
||||
.selectOnce(FormsSelectors.getFormState)
|
||||
.pipe(map((filterFn) => filterFn(USER_FORM_STATE_KEY)))
|
||||
.subscribe((value) => {
|
||||
const data = value ? JSON.parse(value) : undefined;
|
||||
this.userForm = fb.group({
|
||||
firstName: [this.getProperty(data, 'firstName') || '', Validators.required],
|
||||
lastName: [this.getProperty(data, 'lastName') || '', Validators.required],
|
||||
email: [this.getProperty(data, 'email') || '', CustomValidators.validateEmail],
|
||||
address: [this.getProperty(data, 'address') || ''],
|
||||
streetNo: [this.getProperty(data, 'streetNo') || ''],
|
||||
zipCode: [this.getProperty(data, 'zipCode') || ''],
|
||||
city: [this.getProperty(data, 'city') || ''],
|
||||
dateOfBirth: [this.getProperty(data, 'dateOfBirth') || '', CustomValidators.validateDateOfBirth],
|
||||
mobile: [this.getProperty(data, 'mobile') || '', CustomValidators.validateTelephone],
|
||||
telephone: [this.getProperty(data, 'telephone') || '', CustomValidators.validateTelephone],
|
||||
country: [this.getProperty(data, 'country') || ''],
|
||||
gender: [this.getProperty(data, 'gender') || ''],
|
||||
title: [this.getProperty(data, 'title') || ''],
|
||||
company: [this.getProperty(data, 'company') || ''],
|
||||
department: [this.getProperty(data, 'department') || ''],
|
||||
tax_number: [this.getProperty(data, 'tax_number') || ''],
|
||||
note: [this.getProperty(data, 'note') || ''],
|
||||
});
|
||||
setTimeout(() => {
|
||||
this.updateFormLabelsState(data);
|
||||
}, 50);
|
||||
if (this.customerFormState === CustomerFormState.CREATE) {
|
||||
const data = value ? JSON.parse(value) : undefined;
|
||||
this.userForm = fb.group({
|
||||
firstName: [
|
||||
this.getProperty(data, 'firstName') || '',
|
||||
Validators.required,
|
||||
],
|
||||
lastName: [
|
||||
this.getProperty(data, 'lastName') || '',
|
||||
Validators.required,
|
||||
],
|
||||
email: [
|
||||
this.getProperty(data, 'email') || '',
|
||||
CustomValidators.validateEmail,
|
||||
],
|
||||
address: [this.getProperty(data, 'address') || ''],
|
||||
streetNo: [this.getProperty(data, 'streetNo') || ''],
|
||||
zipCode: [this.getProperty(data, 'zipCode') || ''],
|
||||
city: [this.getProperty(data, 'city') || ''],
|
||||
dateOfBirth: [
|
||||
this.getProperty(data, 'dateOfBirth') || '',
|
||||
CustomValidators.validateDateOfBirth,
|
||||
],
|
||||
mobile: [
|
||||
this.getProperty(data, 'mobile') || '',
|
||||
CustomValidators.validateTelephone,
|
||||
],
|
||||
telephone: [
|
||||
this.getProperty(data, 'telephone') || '',
|
||||
CustomValidators.validateTelephone,
|
||||
],
|
||||
country: [this.getProperty(data, 'country') || ''],
|
||||
gender: [this.getProperty(data, 'gender') || ''],
|
||||
title: [this.getProperty(data, 'title') || ''],
|
||||
company: [this.getProperty(data, 'company') || ''],
|
||||
department: [this.getProperty(data, 'department') || ''],
|
||||
tax_number: [this.getProperty(data, 'tax_number') || ''],
|
||||
note: [this.getProperty(data, 'note') || ''],
|
||||
});
|
||||
setTimeout(() => {
|
||||
this.updateFormLabelsState(data);
|
||||
}, 50);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -955,7 +1272,11 @@ export class CreateCustomerCardComponent implements OnInit, OnDestroy {
|
||||
|
||||
private updateFieldState(field: any) {
|
||||
if (field) {
|
||||
if (this.userForm.get(field) && this.userForm.get(field).value.length > 0) {
|
||||
if (
|
||||
this.userForm.get(field) &&
|
||||
this.userForm.get(field).value &&
|
||||
this.userForm.get(field).value.length > 0
|
||||
) {
|
||||
this.userForm.get(field).markAsDirty();
|
||||
} else {
|
||||
this.userForm.get(field).markAsUntouched();
|
||||
@@ -964,12 +1285,22 @@ export class CreateCustomerCardComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
private loadExtrasState() {
|
||||
const value = this.store.selectSnapshot(FormsSelectors.getFormState)(USER_EXTRAS_FORM_STATE_KEY);
|
||||
const value = this.store.selectSnapshot(FormsSelectors.getFormState)(
|
||||
USER_EXTRAS_FORM_STATE_KEY
|
||||
);
|
||||
const data = value ? JSON.parse(value) : undefined;
|
||||
const guest = data ? (this.getProperty(data, 'guest') as boolean) : undefined;
|
||||
const online = data ? (this.getProperty(data, 'online') as boolean) : undefined;
|
||||
const submitted = data ? (this.getProperty(data, 'submitted') as boolean) : undefined;
|
||||
const guestDisabled = data ? (this.getProperty(data, 'guestDisabled') as boolean) : undefined;
|
||||
const guest = data
|
||||
? (this.getProperty(data, 'guest') as boolean)
|
||||
: undefined;
|
||||
const online = data
|
||||
? (this.getProperty(data, 'online') as boolean)
|
||||
: undefined;
|
||||
const submitted = data
|
||||
? (this.getProperty(data, 'submitted') as boolean)
|
||||
: undefined;
|
||||
const guestDisabled = data
|
||||
? (this.getProperty(data, 'guestDisabled') as boolean)
|
||||
: undefined;
|
||||
setTimeout(() => {
|
||||
if (guest !== this.guestChecked && !isNullOrUndefined(guest)) {
|
||||
this.toggleGuest(false);
|
||||
@@ -982,14 +1313,19 @@ export class CreateCustomerCardComponent implements OnInit, OnDestroy {
|
||||
} else {
|
||||
this.submitted = false;
|
||||
}
|
||||
if (guestDisabled !== this.guestDisabled && !isNullOrUndefined(guestDisabled)) {
|
||||
if (
|
||||
guestDisabled !== this.guestDisabled &&
|
||||
!isNullOrUndefined(guestDisabled)
|
||||
) {
|
||||
this.guestDisabled = guestDisabled;
|
||||
}
|
||||
}, 400);
|
||||
}
|
||||
|
||||
private loadErrorsState() {
|
||||
const value = this.store.selectSnapshot(FormsSelectors.getFormState)(USER_ERRORS_FORM_STATE_KEY);
|
||||
const value = this.store.selectSnapshot(FormsSelectors.getFormState)(
|
||||
USER_ERRORS_FORM_STATE_KEY
|
||||
);
|
||||
if (value) {
|
||||
const data = JSON.parse(value);
|
||||
setTimeout(() => {
|
||||
@@ -1036,7 +1372,11 @@ export class CreateCustomerCardComponent implements OnInit, OnDestroy {
|
||||
if (this.guestDisabled) {
|
||||
return;
|
||||
}
|
||||
if (this.cartHasItemsForDelivery && this.guestChecked && !this.onlineChecked) {
|
||||
if (
|
||||
this.cartHasItemsForDelivery &&
|
||||
this.guestChecked &&
|
||||
!this.onlineChecked
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this.guestChecked = !this.guestChecked;
|
||||
@@ -1056,7 +1396,11 @@ export class CreateCustomerCardComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
toggleOnline(saveState = true) {
|
||||
if (this.cartHasItemsForDelivery && !this.guestChecked && this.onlineChecked) {
|
||||
if (
|
||||
this.cartHasItemsForDelivery &&
|
||||
!this.guestChecked &&
|
||||
this.onlineChecked
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (this.onlineChecked && this.cartHasItemsForDownload) {
|
||||
@@ -1112,13 +1456,17 @@ export class CreateCustomerCardComponent implements OnInit, OnDestroy {
|
||||
@HostListener('window:scroll', ['$event'])
|
||||
scrollHandler(event) {
|
||||
if (event.target.id === 'customer-form') {
|
||||
this.renderer.invokeElementMethod(document.activeElement, 'blur');
|
||||
document.activeElement['blur'].apply(document.activeElement);
|
||||
}
|
||||
}
|
||||
|
||||
detectChanges() {
|
||||
setTimeout(() => {
|
||||
if (this.cdrf !== null && this.cdrf !== undefined && !(this.cdrf as ViewRef).destroyed) {
|
||||
if (
|
||||
this.cdrf !== null &&
|
||||
this.cdrf !== undefined &&
|
||||
!(this.cdrf as ViewRef).destroyed
|
||||
) {
|
||||
this.cdrf.detectChanges();
|
||||
}
|
||||
}, 0);
|
||||
|
||||
@@ -85,7 +85,7 @@ export class CustomerEditCardComponent implements OnInit, OnDestroy {
|
||||
private fb: FormBuilder,
|
||||
private router: Router,
|
||||
private route: ActivatedRoute
|
||||
) {}
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.userForm = this.buildForm(this.fb);
|
||||
@@ -227,10 +227,13 @@ export class CustomerEditCardComponent implements OnInit, OnDestroy {
|
||||
this.deliveryAddressEditDetails = true;
|
||||
this.phoneNotEditable = true;
|
||||
this.mobileNotEditable = true;
|
||||
this.billingAddressEditable = false;
|
||||
this.deliveryAddressEditable = false;
|
||||
this.companyNameNotEditable = true;
|
||||
this.companyAddressNotEditable = true;
|
||||
this.companyDepartmentNotEditable = true;
|
||||
this.companyVatIdNotEditable = true;
|
||||
this.disableAssignment = false;
|
||||
};
|
||||
|
||||
guestState = () => {
|
||||
@@ -260,11 +263,13 @@ export class CustomerEditCardComponent implements OnInit, OnDestroy {
|
||||
this.phoneNotEditable = true;
|
||||
this.mobileNotEditable = true;
|
||||
this.billingAddressEditable = true;
|
||||
this.deliveryAddressEditable = false;
|
||||
this.deliveryAddressEditDetails = true;
|
||||
this.companyNameNotEditable = true;
|
||||
this.companyAddressNotEditable = true;
|
||||
this.companyDepartmentNotEditable = true;
|
||||
this.companyVatIdNotEditable = true;
|
||||
this.disableAssignment = false;
|
||||
};
|
||||
|
||||
cardState = () => {
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
import { Component, OnInit, AfterViewInit, ViewChild, ChangeDetectorRef, ChangeDetectionStrategy, ViewRef } from '@angular/core';
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
AfterViewInit,
|
||||
ViewChild,
|
||||
ChangeDetectorRef,
|
||||
ChangeDetectionStrategy,
|
||||
ViewRef,
|
||||
} from '@angular/core';
|
||||
import { Select, Store } from '@ngxs/store';
|
||||
import { Observable } from 'rxjs';
|
||||
import { User, Address } from '../../../../core/models/user.model';
|
||||
@@ -6,7 +14,7 @@ import {
|
||||
ChangeCurrentRoute,
|
||||
UpdateCustomerFormState,
|
||||
CustomerOrdersVisited,
|
||||
SetDetailsCustomer
|
||||
SetDetailsCustomer,
|
||||
} from '../../../../core/store/actions/process.actions';
|
||||
import { Router, ActivatedRoute } from '@angular/router';
|
||||
import { Breadcrumb } from '../../../../core/models/breadcrumb.model';
|
||||
@@ -39,11 +47,14 @@ export interface CustomerFilters {
|
||||
selector: 'app-search-customer-result',
|
||||
templateUrl: './customer-search-result.component.html',
|
||||
styleUrls: ['./customer-search-result.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class CustomerSearchResultComponent implements OnInit {
|
||||
@ViewChild(CdkVirtualScrollViewport, { static: false }) viewport: CdkVirtualScrollViewport;
|
||||
@Select(SharedSelectors.getSearchedCustomers) customers$: Observable<CustomerSearchResult>;
|
||||
@ViewChild(CdkVirtualScrollViewport, { static: false })
|
||||
viewport: CdkVirtualScrollViewport;
|
||||
@Select(SharedSelectors.getSearchedCustomers) customers$: Observable<
|
||||
CustomerSearchResult
|
||||
>;
|
||||
@Select(SharedSelectors.getCart) cartData$: Observable<ProcessCart>;
|
||||
customerSearch: CustomerSearch;
|
||||
customers: User[] = [];
|
||||
@@ -52,12 +63,13 @@ export class CustomerSearchResultComponent implements OnInit {
|
||||
ds: CustomerSearchDataSource;
|
||||
isScan = false;
|
||||
cartHasItemsForDownload = false;
|
||||
cartHasItemsForDelivery = false;
|
||||
selectedFilterMode: Side = Side.RIGHT;
|
||||
|
||||
filters: CustomerFilters = {
|
||||
guestaccount: undefined,
|
||||
onlineshop: undefined,
|
||||
bonuscard: undefined
|
||||
bonuscard: undefined,
|
||||
};
|
||||
|
||||
constructor(
|
||||
@@ -66,16 +78,18 @@ export class CustomerSearchResultComponent implements OnInit {
|
||||
private customerService: CustomerService,
|
||||
private route: ActivatedRoute,
|
||||
private cdrf: ChangeDetectorRef
|
||||
) { }
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.cartData$
|
||||
.pipe(
|
||||
filter(c => !isNullOrUndefined(c)),
|
||||
filter((c) => !isNullOrUndefined(c)),
|
||||
take(1),
|
||||
tap((processCart: ProcessCart) => {
|
||||
if (processCart && processCart.cart && processCart.cart.length > 0) {
|
||||
const count = processCart.cart.map(t => t.quantity).reduce((t1, t2) => t1 + t2);
|
||||
const count = processCart.cart
|
||||
.map((t) => t.quantity)
|
||||
.reduce((t1, t2) => t1 + t2);
|
||||
this.cartExist = count > 0 ? true : false;
|
||||
} else {
|
||||
this.cartExist = false;
|
||||
@@ -85,20 +99,23 @@ export class CustomerSearchResultComponent implements OnInit {
|
||||
return this.route.queryParams;
|
||||
})
|
||||
)
|
||||
.subscribe(params => {
|
||||
.subscribe((params) => {
|
||||
const isScan = params && params['scan'] && params['scan'] === 'true';
|
||||
this.isScan = isScan;
|
||||
this.filters = {
|
||||
guestaccount: this.selectedFilterMode === Side.RIGHT ? false : undefined,
|
||||
guestaccount:
|
||||
this.selectedFilterMode === Side.RIGHT ? false : undefined,
|
||||
bonuscard: undefined,
|
||||
onlineshop: undefined
|
||||
onlineshop: undefined,
|
||||
};
|
||||
this.loadDataSource(isScan, true);
|
||||
});
|
||||
}
|
||||
|
||||
loadDataSource(isScan: boolean, firstLoad = false) {
|
||||
this.customerSearch = this.store.selectSnapshot(ProcessSelectors.getCustomerSearch);
|
||||
this.customerSearch = this.store.selectSnapshot(
|
||||
ProcessSelectors.getCustomerSearch
|
||||
);
|
||||
if (!!this.customerSearch) {
|
||||
this.ds = new CustomerSearchDataSource(
|
||||
this.customerSearch.query,
|
||||
@@ -115,11 +132,21 @@ export class CustomerSearchResultComponent implements OnInit {
|
||||
}
|
||||
|
||||
details(user: User, index: number): void {
|
||||
const cartHasItemFor = this.store.selectSnapshot(SharedSelectors.cartHasItemsFor);
|
||||
this.cartHasItemsForDownload = cartHasItemFor && cartHasItemFor(DeliveryOption.DOWNLOAD);
|
||||
const hasOnlineAccount = this.hasOnlineAccount(user);
|
||||
const cartHasItemFor = this.store.selectSnapshot(
|
||||
SharedSelectors.cartHasItemsFor
|
||||
);
|
||||
this.cartHasItemsForDownload =
|
||||
cartHasItemFor && cartHasItemFor(DeliveryOption.DOWNLOAD);
|
||||
this.cartHasItemsForDelivery =
|
||||
cartHasItemFor && cartHasItemFor(DeliveryOption.DELIVERY);
|
||||
|
||||
if (this.cartHasItemsForDownload && !hasOnlineAccount) {
|
||||
const hasOnlineAccount = this.hasOnlineAccount(user);
|
||||
const hasAllRequiredData = this.hasAllRequiredData(user);
|
||||
|
||||
if (
|
||||
(this.cartHasItemsForDownload || this.cartHasItemsForDelivery) &&
|
||||
(!hasOnlineAccount || !hasAllRequiredData)
|
||||
) {
|
||||
this.redirectToCustomerUpdate(user);
|
||||
return;
|
||||
}
|
||||
@@ -133,7 +160,7 @@ export class CustomerSearchResultComponent implements OnInit {
|
||||
new AddBreadcrumb(
|
||||
{
|
||||
name: 'Kundendetails',
|
||||
path: currentRoute
|
||||
path: currentRoute,
|
||||
},
|
||||
'customer'
|
||||
)
|
||||
@@ -142,8 +169,31 @@ export class CustomerSearchResultComponent implements OnInit {
|
||||
this.router.navigate([currentRoute]);
|
||||
}
|
||||
|
||||
hasAllRequiredData = (user: User): boolean => {
|
||||
if (
|
||||
!user.gender ||
|
||||
!user.first_name ||
|
||||
!user.last_name ||
|
||||
!user.email ||
|
||||
!!(
|
||||
isNullOrUndefined(user.delivery_addres) &&
|
||||
isNullOrUndefined(user.invoice_address)
|
||||
) ||
|
||||
!(
|
||||
(user.delivery_addres && user.delivery_addres.country) ||
|
||||
(user.invoice_address && user.invoice_address.country)
|
||||
)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
hasOnlineAccount = (user: User) => {
|
||||
const found = user.features.filter(feature => feature.key === 'onlineshop');
|
||||
const found = user.features.filter(
|
||||
(feature) => feature.key === 'onlineshop'
|
||||
);
|
||||
|
||||
if (!found || found.length < 1) {
|
||||
return false;
|
||||
@@ -160,31 +210,39 @@ export class CustomerSearchResultComponent implements OnInit {
|
||||
const newBread: Breadcrumb = {
|
||||
name: 'Kundendaten erfassen',
|
||||
path: '/customer/search/create',
|
||||
queryParams: { card: 'create' }
|
||||
queryParams: { card: 'create' },
|
||||
};
|
||||
|
||||
this.store.dispatch(new UpdateCustomerFormState(CustomerFormState.CREATE));
|
||||
this.store.dispatch(new AddBreadcrumb(newBread, 'customer'));
|
||||
this.store.dispatch(new ChangeCurrentRoute('/customer/search/create'));
|
||||
this.router.navigate(['/customer/search/create'], { queryParams: { card: 'create' } });
|
||||
this.router.navigate(['/customer/search/create'], {
|
||||
queryParams: { card: 'create' },
|
||||
});
|
||||
}
|
||||
|
||||
redirectToCustomerUpdate(user: User) {
|
||||
const newBread: Breadcrumb = {
|
||||
name: 'Kundendaten erfassen',
|
||||
path: '/customer/search/create'
|
||||
path: '/customer/search/create',
|
||||
};
|
||||
|
||||
this.store.dispatch(new SetDetailsCustomer(user.id));
|
||||
this.store.dispatch(new UpdateCustomerFormState(CustomerFormState.MISSING_ONLINE));
|
||||
this.store.dispatch(
|
||||
new UpdateCustomerFormState(CustomerFormState.MISSING_ONLINE)
|
||||
);
|
||||
this.store.dispatch(new AddBreadcrumb(newBread, 'customer'));
|
||||
const currentRoute = `customer/search/missing-data/${user.id}`;
|
||||
this.store.dispatch(new ChangeCurrentRoute(currentRoute));
|
||||
this.router.navigate([currentRoute]);
|
||||
this.router.navigate([currentRoute], { queryParams: { card: 'create' } });
|
||||
}
|
||||
|
||||
updateFilter(val: boolean, type: string) {
|
||||
this.filters[type] = val ? (this.selectedFilterMode === Side.LEFT ? true : false) : undefined;
|
||||
this.filters[type] = val
|
||||
? this.selectedFilterMode === Side.LEFT
|
||||
? true
|
||||
: false
|
||||
: undefined;
|
||||
this.ds = new CustomerSearchDataSource(
|
||||
this.customerSearch.query,
|
||||
this.store,
|
||||
@@ -200,7 +258,9 @@ export class CustomerSearchResultComponent implements OnInit {
|
||||
if (!isNullOrUndefined(index)) {
|
||||
setTimeout(() => {
|
||||
const ids = index.split(':');
|
||||
const processId = this.store.selectSnapshot(AppState.getCurrentProcessId);
|
||||
const processId = this.store.selectSnapshot(
|
||||
AppState.getCurrentProcessId
|
||||
);
|
||||
if (processId === +ids[1]) {
|
||||
const idx = +ids[0];
|
||||
if (idx) {
|
||||
@@ -219,8 +279,16 @@ export class CustomerSearchResultComponent implements OnInit {
|
||||
return '';
|
||||
}
|
||||
const address = user.delivery_addres;
|
||||
const plz = address ? (isNullOrUndefined(address.zip) ? '' : address.zip) : '';
|
||||
const city = address ? (isNullOrUndefined(address.city) ? '' : address.city) : '';
|
||||
const plz = address
|
||||
? isNullOrUndefined(address.zip)
|
||||
? ''
|
||||
: address.zip
|
||||
: '';
|
||||
const city = address
|
||||
? isNullOrUndefined(address.city)
|
||||
? ''
|
||||
: address.city
|
||||
: '';
|
||||
return plz + ' ' + city;
|
||||
}
|
||||
|
||||
@@ -236,7 +304,7 @@ export class CustomerSearchResultComponent implements OnInit {
|
||||
this.filters = {
|
||||
guestaccount: side === Side.RIGHT ? false : undefined,
|
||||
bonuscard: undefined,
|
||||
onlineshop: undefined
|
||||
onlineshop: undefined,
|
||||
};
|
||||
if (!noFilters || side === Side.RIGHT) {
|
||||
this.ds = new CustomerSearchDataSource(
|
||||
@@ -253,7 +321,11 @@ export class CustomerSearchResultComponent implements OnInit {
|
||||
|
||||
detectChanges() {
|
||||
setTimeout(() => {
|
||||
if (this.cdrf !== null && this.cdrf !== undefined && !(this.cdrf as ViewRef).destroyed) {
|
||||
if (
|
||||
this.cdrf !== null &&
|
||||
this.cdrf !== undefined &&
|
||||
!(this.cdrf as ViewRef).destroyed
|
||||
) {
|
||||
this.cdrf.detectChanges();
|
||||
}
|
||||
}, 0);
|
||||
|
||||
@@ -6,7 +6,9 @@ import { Subject } from 'rxjs';
|
||||
})
|
||||
export class CustomerHelperService {
|
||||
resetCustomerSearchView$ = new Subject();
|
||||
constructor() {}
|
||||
customerUpdateRedirect$ = new Subject();
|
||||
constructor() { }
|
||||
|
||||
public resetCustomerSearchView = () => this.resetCustomerSearchView$.next();
|
||||
public customerUpdateRedirect = () => this.customerUpdateRedirect$.next();
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ import { WindowRef } from 'apps/sales/src/app/core/services/window-ref.service';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class BookCardComponent implements OnInit {
|
||||
@ViewChild('books', { read: ElementRef, static: false }) public books: ElementRef<any>;
|
||||
@ViewChild('books', { read: ElementRef, static: true }) public books: ElementRef<any>;
|
||||
@Select(ProcessSelectors.getProcesses) processes$: Observable<Process[]>;
|
||||
@Input() card: FeedCard;
|
||||
isIPad: boolean;
|
||||
|
||||
@@ -1,15 +1,29 @@
|
||||
<div class="filter-wrapper">
|
||||
<div class="filter-nav">
|
||||
<button *ngFor="let filter of value; trackBy: trackByKeyOrName; let first = first" type="button"
|
||||
(click)="selected = filter" class="isa-btn isa-btn-block isa-btn-default isa-btn-l isa-font-weight-bold"
|
||||
[class.isa-mt-10]="!first">
|
||||
<button
|
||||
*ngFor="let filter of value; trackBy: trackByKeyOrName; let first = first"
|
||||
type="button"
|
||||
(click)="selected = filter"
|
||||
class="isa-btn isa-btn-block isa-btn-default isa-btn-l"
|
||||
[class.isa-mt-10]="!first"
|
||||
[class.is-active]="filter === selected"
|
||||
[class.isa-font-weight-emphasis]="filter !== selected"
|
||||
[class.isa-font-weight-bold]="filter === selected"
|
||||
>
|
||||
<span>{{ filter.name }}</span>
|
||||
<lib-icon name="Arrow_Next" height="17px"></lib-icon>
|
||||
<lib-icon
|
||||
[name]="filter === selected ? 'Arrow_Next_Dark' : 'Arrow_Next_Grey'"
|
||||
height="17px"
|
||||
></lib-icon>
|
||||
</button>
|
||||
</div>
|
||||
<div class="filter-content isa-ml-10" [ngSwitch]="selected?.type">
|
||||
<app-select-filter *ngSwitchCase="'select'" [options]="selected.options" [max]="selected.max"
|
||||
(optionsChanged)="emitOnChange($event)">
|
||||
<app-select-filter
|
||||
*ngSwitchCase="'select'"
|
||||
[options]="selected.options"
|
||||
[max]="selected.max"
|
||||
(optionsChanged)="emitOnChange($event)"
|
||||
>
|
||||
</app-select-filter>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -16,6 +16,10 @@
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.is-active {
|
||||
background: #89949e;
|
||||
}
|
||||
}
|
||||
|
||||
.filter-content {
|
||||
|
||||
@@ -49,7 +49,7 @@ export class FilterGroupComponent implements OnInit, ControlValueAccessor, OnCha
|
||||
|
||||
trackByKeyOrName = (filter: Filter) => filter.key || filter.name;
|
||||
|
||||
constructor(private cdr: ChangeDetectorRef) {}
|
||||
constructor(private cdr: ChangeDetectorRef) { }
|
||||
|
||||
ngOnChanges({ value, selected }: SimpleChanges): void {
|
||||
if (value) {
|
||||
@@ -63,7 +63,7 @@ export class FilterGroupComponent implements OnInit, ControlValueAccessor, OnCha
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit() {}
|
||||
ngOnInit() { }
|
||||
|
||||
writeValue(obj: Filter[]): void {
|
||||
if (obj !== this.value) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div class="select-filter-option-wrapper">
|
||||
<div class="isa-form-group isa-pt-10 isa-pb-10">
|
||||
<div class="isa-form-group isa-pt-15 isa-pb-15">
|
||||
<input
|
||||
type="checkbox"
|
||||
[name]="option?.name"
|
||||
@@ -15,9 +15,9 @@
|
||||
type="button"
|
||||
class="isa-btn expand-button"
|
||||
[class.expanded]="option?.expanded"
|
||||
(click)="option.expanded = !option?.expanded"
|
||||
(click)="toggleExpanded()"
|
||||
>
|
||||
<lib-icon name="Arrow_Down_2"></lib-icon>
|
||||
<lib-icon name="Arrow_Down_2_grey"></lib-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -10,8 +10,11 @@
|
||||
|
||||
.expand-button {
|
||||
transition: all 150ms ease-in-out;
|
||||
padding: 0px;
|
||||
|
||||
&.expanded {
|
||||
padding-bottom: 18px;
|
||||
padding-top: 20px;
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ export class SelectFilterOptionComponent implements OnChanges {
|
||||
@Output()
|
||||
optionChanged = new EventEmitter<SelectFilterOption>();
|
||||
|
||||
constructor(private cdr: ChangeDetectorRef) {}
|
||||
constructor(private cdr: ChangeDetectorRef) { }
|
||||
|
||||
ngOnChanges({ option }: SimpleChanges): void {
|
||||
if (option) {
|
||||
@@ -62,6 +62,12 @@ export class SelectFilterOptionComponent implements OnChanges {
|
||||
this.optionChanged.emit(this.option);
|
||||
}
|
||||
|
||||
toggleExpanded() {
|
||||
this.option.expanded = !this.option.expanded;
|
||||
this.onChange();
|
||||
this.cdr.markForCheck();
|
||||
}
|
||||
|
||||
childChanged(option: SelectFilterOption, index: number) {
|
||||
this.option.options[index] = option;
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="isa-btn isa-p-0"
|
||||
class="isa-btn isa-p-0 isa-font-lightgrey isa-font-weight-emphasis"
|
||||
*ngIf="!max"
|
||||
(click)="selectAll()"
|
||||
>
|
||||
@@ -15,8 +15,9 @@
|
||||
<div class="select-filter-options isa-mt-10">
|
||||
<div
|
||||
class="select-filter-options-content"
|
||||
(scroll)="onOptionsContentScroll()"
|
||||
(scroll)="updateScrollButton()"
|
||||
#optionsContainer
|
||||
(cdkObserveContent)="updateScrollButton()"
|
||||
>
|
||||
<ng-container *ngFor="let option of options; let index = index">
|
||||
<app-select-filter-option
|
||||
@@ -36,5 +37,10 @@
|
||||
[class.down]="scrollPositionPersantage < 20"
|
||||
(click)="scroll()"
|
||||
>
|
||||
<lib-icon name="Arrow_back_branch"></lib-icon>
|
||||
<lib-icon
|
||||
width="18px"
|
||||
height="20px"
|
||||
mt="3px"
|
||||
name="Arrow_back_branch"
|
||||
></lib-icon>
|
||||
</button>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
:host {
|
||||
display: block;
|
||||
position: relative;
|
||||
}
|
||||
.isa-card {
|
||||
overflow: hidden;
|
||||
@@ -21,9 +20,10 @@
|
||||
}
|
||||
.scroll-btn {
|
||||
position: absolute;
|
||||
bottom: -3rem;
|
||||
right: 1rem;
|
||||
box-shadow: 0px 0px 3px 3px rgba(0, 0, 0, 0.2);
|
||||
bottom: 4px;
|
||||
right: 6px;
|
||||
box-shadow: 0px 0px 20px 0px rgba(89, 100, 112, 0.5);
|
||||
|
||||
transition: all 100ms linear;
|
||||
&.up {
|
||||
transform: rotate(90deg);
|
||||
|
||||
@@ -10,13 +10,11 @@ import {
|
||||
SimpleChanges,
|
||||
ChangeDetectorRef,
|
||||
AfterViewInit,
|
||||
OnDestroy,
|
||||
} from '@angular/core';
|
||||
import { SelectFilterOption } from '../../../models';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import {
|
||||
cloneSelectFilterOption,
|
||||
flattenSelectFilterOption,
|
||||
} from '../../../utils';
|
||||
import { BehaviorSubject, interval, Subscription } from 'rxjs';
|
||||
import { cloneSelectFilterOption, flattenSelectFilterOption } from '../../../utils';
|
||||
|
||||
@Component({
|
||||
selector: 'app-select-filter',
|
||||
@@ -24,7 +22,7 @@ import {
|
||||
styleUrls: ['select-filter.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class SelectFilterComponent implements OnChanges, AfterViewInit {
|
||||
export class SelectFilterComponent implements OnChanges, AfterViewInit, OnDestroy {
|
||||
@Input()
|
||||
options: SelectFilterOption[];
|
||||
|
||||
@@ -41,20 +39,38 @@ export class SelectFilterComponent implements OnChanges, AfterViewInit {
|
||||
scrollPositionPersantage = 0;
|
||||
allSelected$ = new BehaviorSubject<boolean>(false);
|
||||
|
||||
constructor(private cdr: ChangeDetectorRef) { }
|
||||
updateScrollPositionPersantageIntervalSub: Subscription;
|
||||
|
||||
constructor(private cdr: ChangeDetectorRef) {}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this.updateScrollPositionPersantageIntervalSub) {
|
||||
this.updateScrollPositionPersantageIntervalSub.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
this.onOptionsContentScroll();
|
||||
this.updateScrollButton();
|
||||
this.updateScrollPositionPersantageIntervalSub = interval(500).subscribe(() => this.updateScrollButton());
|
||||
}
|
||||
|
||||
updateScrollButton() {
|
||||
this.updateScrollPositionPersantage();
|
||||
this.setCanScroll();
|
||||
this.cdr.markForCheck();
|
||||
}
|
||||
|
||||
setCanScroll() {
|
||||
const container = this.optionsContainer.nativeElement;
|
||||
const scrollHeight = container.scrollHeight;
|
||||
const height = container.clientHeight;
|
||||
if (this.optionsContainer) {
|
||||
const container = this.optionsContainer.nativeElement;
|
||||
const scrollHeight = container.scrollHeight;
|
||||
const height = container.clientHeight;
|
||||
|
||||
this.canScroll = scrollHeight > height;
|
||||
this.cdr.markForCheck();
|
||||
if (this.canScroll !== scrollHeight > height) {
|
||||
this.canScroll = scrollHeight > height;
|
||||
this.cdr.markForCheck();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ngOnChanges({ options }: SimpleChanges): void {
|
||||
@@ -62,7 +78,6 @@ export class SelectFilterComponent implements OnChanges, AfterViewInit {
|
||||
this.options = this.options.map(cloneSelectFilterOption);
|
||||
this.setAllSelected(options.currentValue);
|
||||
this.cdr.markForCheck();
|
||||
setTimeout(() => this.setCanScroll(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,9 +90,7 @@ export class SelectFilterComponent implements OnChanges, AfterViewInit {
|
||||
return {
|
||||
...option,
|
||||
selected: !this.allSelected$.value,
|
||||
options: Array.isArray(option.options)
|
||||
? option.options.map(selectFn)
|
||||
: option.options,
|
||||
options: Array.isArray(option.options) ? option.options.map(selectFn) : option.options,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -91,9 +104,7 @@ export class SelectFilterComponent implements OnChanges, AfterViewInit {
|
||||
optionChanged(option: SelectFilterOption, index: number) {
|
||||
let options = [...this.options];
|
||||
options[index] = option;
|
||||
const selectedOptions = flattenSelectFilterOption(options).filter(
|
||||
(o) => o.selected
|
||||
);
|
||||
const selectedOptions = flattenSelectFilterOption(options).filter((o) => o.selected);
|
||||
|
||||
if (this.max > 0 && selectedOptions.length > this.max) {
|
||||
const unselectOptions = selectedOptions
|
||||
@@ -101,9 +112,7 @@ export class SelectFilterComponent implements OnChanges, AfterViewInit {
|
||||
.slice(0, selectedOptions.length - this.max);
|
||||
|
||||
const updateOption = (o: SelectFilterOption): SelectFilterOption =>
|
||||
unselectOptions.some((s) => s.key === o.key && s.name === o.name)
|
||||
? { ...o, selected: false }
|
||||
: o;
|
||||
unselectOptions.some((s) => s.key === o.key && s.name === o.name) ? { ...o, selected: false } : o;
|
||||
options = options.map(updateOption);
|
||||
}
|
||||
|
||||
@@ -131,15 +140,19 @@ export class SelectFilterComponent implements OnChanges, AfterViewInit {
|
||||
});
|
||||
}
|
||||
|
||||
onOptionsContentScroll() {
|
||||
const container: HTMLElement = this.optionsContainer.nativeElement;
|
||||
const scrollHeight = container.scrollHeight;
|
||||
const scrollTop = container.scrollTop;
|
||||
const height = container.clientHeight;
|
||||
updateScrollPositionPersantage() {
|
||||
if (this.optionsContainer) {
|
||||
const container: HTMLElement = this.optionsContainer.nativeElement;
|
||||
const scrollHeight = container.scrollHeight;
|
||||
const scrollTop = container.scrollTop;
|
||||
const height = container.clientHeight;
|
||||
|
||||
this.scrollPositionPersantage = Math.round(
|
||||
(100 / (scrollHeight - height)) * scrollTop
|
||||
);
|
||||
const scrollPositionPersantage = Math.round((100 / (scrollHeight - height)) * scrollTop);
|
||||
if (this.scrollPositionPersantage !== scrollPositionPersantage) {
|
||||
this.scrollPositionPersantage = scrollPositionPersantage;
|
||||
this.cdr.markForCheck();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scroll() {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FilterGroupModule } from './filter-group/filter-group.module';
|
||||
import { SelectedFilterOptionsModule } from './selected-filter-options';
|
||||
import { ObserversModule } from '@angular/cdk/observers';
|
||||
|
||||
@NgModule({
|
||||
imports: [FilterGroupModule, SelectedFilterOptionsModule],
|
||||
imports: [FilterGroupModule, SelectedFilterOptionsModule, ObserversModule],
|
||||
exports: [],
|
||||
declarations: [],
|
||||
providers: [],
|
||||
|
||||
@@ -1,29 +1,23 @@
|
||||
<div class="container" *ngIf="value">
|
||||
<div class="clear-all">
|
||||
<span class="filter-name">Alle Filter Entfernen</span>
|
||||
<lib-icon (click)="clearAllFilters()" name="close-branch"></lib-icon>
|
||||
<button class="isa-btn isa-btn-primary-link remove-all isa-pt-0 isa-pb-0 isa-pl-15 isa-pr-15"
|
||||
(click)="clearAllFilters()">
|
||||
Alle Filter Entfernen
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<ng-container *ngFor="let filter of value">
|
||||
<ng-container
|
||||
*ngTemplateOutlet="optionTmpl; context: { $implicit: filter }"
|
||||
></ng-container>
|
||||
<ng-container *ngTemplateOutlet="optionTmpl; context: { $implicit: filter }"></ng-container>
|
||||
</ng-container>
|
||||
|
||||
<ng-container
|
||||
[ngSwitch]="isCollapsed"
|
||||
*ngIf="items && items.length > collapsedStateNumberOfSelectedFiltersShown"
|
||||
>
|
||||
<div class="expand" *ngSwitchCase="true" (click)="updateCollapsed(false)">
|
||||
<ng-container [ngSwitch]="isCollapsed" *ngIf="items && items.length > collapsedStateNumberOfSelectedFiltersShown">
|
||||
<div class="expand c-pointer" *ngSwitchCase="true" (click)="updateCollapsed(false)">
|
||||
<span>Mehr</span>
|
||||
<lib-icon name="Arrow_More_Grey"></lib-icon>
|
||||
<lib-icon name="Arrow_More_Grey" class="isa-mt-0"></lib-icon>
|
||||
</div>
|
||||
<div class="collapse" *ngSwitchDefault (click)="updateCollapsed(true)">
|
||||
<lib-icon
|
||||
[rotateBackwards]="true"
|
||||
style="margin-left: 12px;"
|
||||
name="Arrow_More_Grey"
|
||||
></lib-icon>
|
||||
<div class=" collapse c-pointer" *ngSwitchDefault (click)="updateCollapsed(true)">
|
||||
<lib-icon [rotateBackwards]="true" class="isa-mt-0 isa-ml-12" name="Arrow_More_Grey">
|
||||
</lib-icon>
|
||||
<span>Weniger</span>
|
||||
</div>
|
||||
</ng-container>
|
||||
@@ -32,17 +26,10 @@
|
||||
<ng-template #optionTmpl let-filter>
|
||||
<div *ngIf="filter.selected" class="selected-filter" #filteredItem>
|
||||
<span class="filter-name">{{ filter.name }}</span>
|
||||
<lib-icon
|
||||
(click)="clearFilter(filter); (false)"
|
||||
name="close-branch"
|
||||
></lib-icon>
|
||||
<lib-icon (click)="clearFilter(filter); (false)" name="close-branch"></lib-icon>
|
||||
</div>
|
||||
|
||||
<ng-container
|
||||
*ngFor="let option of filter.options | checkAllChildOptionsSelected"
|
||||
>
|
||||
<ng-container
|
||||
*ngTemplateOutlet="optionTmpl; context: { $implicit: option }"
|
||||
></ng-container>
|
||||
<ng-container *ngFor="let option of filter.options | checkAllChildOptionsSelected">
|
||||
<ng-container *ngTemplateOutlet="optionTmpl; context: { $implicit: option }"></ng-container>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
@@ -1,6 +1,7 @@
|
||||
$color-grey: #596470;
|
||||
$filter-padding-right: 13px;
|
||||
$icon-margin: 6px;
|
||||
$font-size: 18px;
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
@@ -35,11 +36,20 @@ $icon-margin: 6px;
|
||||
|
||||
.filter-name {
|
||||
font-weight: bold;
|
||||
font-size: $font-size;
|
||||
line-height: 2;
|
||||
display: flex;
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
.remove-all {
|
||||
font-weight: bold;
|
||||
line-height: 2;
|
||||
font-size: $font-size;
|
||||
cursor: pointer;
|
||||
color: #f70400;
|
||||
}
|
||||
|
||||
.selected-filter {
|
||||
margin-right: $filter-padding-right;
|
||||
.filter-name {
|
||||
@@ -56,6 +66,7 @@ $icon-margin: 6px;
|
||||
.collapse {
|
||||
margin-left: $icon-margin;
|
||||
font-weight: bold;
|
||||
font-size: $font-size;
|
||||
color: $color-grey;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -11,10 +11,8 @@ import {
|
||||
Renderer2,
|
||||
ElementRef,
|
||||
} from '@angular/core';
|
||||
import { Filter, SelectFilterOption, SelectFilter } from '../../models';
|
||||
import { Filter, SelectFilterOption } from '../../models';
|
||||
import { cloneFilter } from '../../utils';
|
||||
import { isNullOrUndefined } from 'util';
|
||||
import { DeleteSelectedFiltersForProcess } from 'apps/sales/src/app/core/store/actions/filter.actions';
|
||||
|
||||
@Component({
|
||||
selector: 'app-selected-filter-options',
|
||||
@@ -33,7 +31,7 @@ export class SelectedFilterOptionsComponent implements AfterViewInit {
|
||||
collapsedStateNumberOfSelectedFiltersShown = 3;
|
||||
isCollapsed = true;
|
||||
|
||||
constructor(private cdr: ChangeDetectorRef, private renderer: Renderer2) { }
|
||||
constructor(private cdr: ChangeDetectorRef, private renderer: Renderer2) {}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.items.changes.subscribe((_) => {
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
|
||||
@Pipe({
|
||||
name: 'getAuthorsPipe',
|
||||
})
|
||||
export class GetAuthorsPipe implements PipeTransform {
|
||||
transform(author: string): string[] {
|
||||
if (author) {
|
||||
const authors = author.split(';');
|
||||
return authors.map((a) => a.trim());
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -9,13 +9,24 @@
|
||||
</div>
|
||||
<div class="content-container">
|
||||
<div class="author align-left">
|
||||
<ng-container *ngFor="let author of authors; let i = index; let last = last">
|
||||
<span [id]="'author' + i" (click)="filterByAuthor(author)">{{ author }}</span>
|
||||
<ng-container
|
||||
*ngFor="
|
||||
let author of product.author | getAuthorsPipe;
|
||||
let i = index;
|
||||
let last = last
|
||||
"
|
||||
>
|
||||
<span [id]="'author' + i" (click)="filterByAuthor(author)">{{
|
||||
author
|
||||
}}</span>
|
||||
<span *ngIf="!last">{{ '; ' }}</span>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div class="title-price">
|
||||
<div class="title align-left" [ngClass]="{ 'title-grid': product.recommandation }">
|
||||
<div
|
||||
class="title align-left"
|
||||
[ngClass]="{ 'title-grid': product.recommandation }"
|
||||
>
|
||||
<div class="rec-icon-container" *ngIf="product.recommandation">
|
||||
<lib-icon name="Empfehlungen_Icon"></lib-icon>
|
||||
</div>
|
||||
@@ -32,30 +43,45 @@
|
||||
<div class="type-stock">
|
||||
<div class="type align-left">
|
||||
<div class="type-icon-container align-left">
|
||||
<lib-icon *ngIf="!!product.typeIcon && product.typeIcon != '--'" name="Icon_{{ product.typeIcon }}"></lib-icon>
|
||||
<lib-icon
|
||||
*ngIf="!!product.typeIcon && product.typeIcon != '--'"
|
||||
name="Icon_{{ product.typeIcon }}"
|
||||
></lib-icon>
|
||||
</div>
|
||||
<div class="type-text align-left">
|
||||
<span>{{ product.type }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stock-container align-right">
|
||||
<div class="available-stock" [ngClass]="{ 'no-quantity-container': product.itemsInStock === 0 }">
|
||||
<div class="stock-icon-wraper" *ngIf="product.itemsInStock && product.itemsInStock > 0">
|
||||
<div
|
||||
class="available-stock"
|
||||
[ngClass]="{ 'no-quantity-container': product.itemsInStock === 0 }"
|
||||
>
|
||||
<div
|
||||
class="stock-icon-wraper"
|
||||
*ngIf="product.itemsInStock && product.itemsInStock > 0"
|
||||
>
|
||||
<lib-icon
|
||||
class="stock-icon"
|
||||
name="{{ product.recommandation ? 'Icon_House_recommended' : 'Icon_House' }}"
|
||||
name="{{
|
||||
product.recommandation ? 'Icon_House_recommended' : 'Icon_House'
|
||||
}}"
|
||||
height="19px"
|
||||
width="16px"
|
||||
></lib-icon>
|
||||
</div>
|
||||
<div class="stock" *ngIf="product.itemsInStock && product.itemsInStock > 0">
|
||||
<span *ngIf="product.itemsInStock">{{ product.itemsInStock }}x</span>
|
||||
<div
|
||||
class="stock"
|
||||
*ngIf="product.itemsInStock && product.itemsInStock > 0"
|
||||
>
|
||||
<span *ngIf="product.itemsInStock"
|
||||
>{{ product.itemsInStock }}x</span
|
||||
>
|
||||
<span *ngIf="!product.itemsInStock">0x</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="store-availability">
|
||||
<div class="availability-image">
|
||||
<!-- <lib-icon name="Package_Icon_blue" height="18px" *ngIf="product.typeIcon !== 'EB' && product.storeStatusCode > 1"></lib-icon> -->
|
||||
<lib-icon
|
||||
name="download_product"
|
||||
mr="3px"
|
||||
@@ -80,16 +106,31 @@
|
||||
</div>
|
||||
<div class="volume-edition">
|
||||
<span
|
||||
>{{ product.edition }}<span *ngIf="product.edition && product.volume">|</span>{{ product.volume ? product.volume : '' }}
|
||||
<span *ngIf="(product.edition || product.volume) && product.publicationDate">|</span> {{ publicationDate }}</span>
|
||||
>{{ product.edition
|
||||
}}<span *ngIf="product.edition && product.volume">|</span
|
||||
>{{ product.volume ? product.volume : '' }}
|
||||
<span
|
||||
*ngIf="
|
||||
(product.edition || product.volume) && product.publicationDate
|
||||
"
|
||||
>|</span
|
||||
>
|
||||
{{ publicationDate }}</span
|
||||
>
|
||||
<span class="order">{{ product.category }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="avalability-text">
|
||||
<span *ngIf="product.typeIcon !== 'EB' && product.typeIcon !== 'DL'">{{
|
||||
product.storeStatusCode === 999 ? 'Lieferbar' : product.sscText
|
||||
}}</span>
|
||||
<span *ngIf="product.typeIcon === 'EB' || product.typeIcon === 'DL'">Download</span>
|
||||
<div *ngIf="product.typeIcon !== 'EB' && product.typeIcon !== 'DL'">
|
||||
<span *ngIf="product.ssc">{{ product.ssc }}</span
|
||||
><span *ngIf="product.ssc && product.sscText"> - </span>
|
||||
<span *ngIf="product.sscText">{{
|
||||
product.storeStatusCode === 999 ? 'Lieferbar' : product.sscText
|
||||
}}</span>
|
||||
</div>
|
||||
<span *ngIf="product.typeIcon === 'EB' || product.typeIcon === 'DL'"
|
||||
>Download</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
margin-top: 10px;
|
||||
padding: 15px 20px;
|
||||
// Virtual Scroll Item Height is set to 220px based on maring/paddings => 220 - (10 + 2 * 15) = 180
|
||||
min-height: 180px;
|
||||
min-height: 140px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
}
|
||||
|
||||
.content-container {
|
||||
display: grid;
|
||||
// display: grid;
|
||||
grid-template-columns: auto;
|
||||
}
|
||||
|
||||
@@ -59,11 +59,12 @@
|
||||
font-size: 26px;
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
margin-bottom: 2px;
|
||||
|
||||
&.multi-line {
|
||||
font-size: 22px;
|
||||
transition: font-size .05s ease-out;
|
||||
margin-bottom: 14px;
|
||||
transition: font-size 0.05s ease-out;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,18 +91,17 @@
|
||||
display: grid;
|
||||
grid-template-columns: auto max-content;
|
||||
// margin-top: 10px;
|
||||
align-items: end;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.avalability-text {
|
||||
font-family: 'Open Sans';
|
||||
font-size: 18px;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
line-height: 21px;
|
||||
color: #000000;
|
||||
word-break: break-word;
|
||||
max-width: 220px;
|
||||
text-align: end;
|
||||
margin-top: -1px;
|
||||
}
|
||||
|
||||
.type {
|
||||
@@ -160,14 +160,14 @@
|
||||
.publisher-order {
|
||||
display: grid;
|
||||
grid-template-columns: auto max-content;
|
||||
margin-top: 10px;
|
||||
// margin-top: 5px;
|
||||
}
|
||||
|
||||
.publisher-serial {
|
||||
display: grid;
|
||||
grid-template-columns: max-content min-content auto;
|
||||
|
||||
grid-gap: 1vh;
|
||||
// grid-gap: 1vh;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@@ -187,6 +187,11 @@
|
||||
|
||||
.publisher {
|
||||
max-width: 210px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.serial {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.no-quantity-container {
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
ElementRef,
|
||||
Renderer2,
|
||||
AfterViewChecked,
|
||||
ViewChild
|
||||
ViewChild,
|
||||
} from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { Store } from '@ngxs/store';
|
||||
@@ -22,9 +22,10 @@ import { AddBreadcrumb } from 'apps/sales/src/app/core/store/actions/breadcrumb.
|
||||
@Component({
|
||||
selector: 'app-product-card',
|
||||
templateUrl: './product-card.component.html',
|
||||
styleUrls: ['./product-card.component.scss']
|
||||
styleUrls: ['./product-card.component.scss'],
|
||||
})
|
||||
export class ProductCardComponent implements OnInit, OnDestroy, AfterViewChecked {
|
||||
export class ProductCardComponent
|
||||
implements OnInit, OnDestroy, AfterViewChecked {
|
||||
private _product: Product;
|
||||
@Input() index: number;
|
||||
|
||||
@@ -41,7 +42,6 @@ export class ProductCardComponent implements OnInit, OnDestroy, AfterViewChecked
|
||||
} else {
|
||||
this.publicationDate = undefined;
|
||||
}
|
||||
this.splitAuthors(val.author);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,9 +64,9 @@ export class ProductCardComponent implements OnInit, OnDestroy, AfterViewChecked
|
||||
private router: Router,
|
||||
private store: Store,
|
||||
private renderer: Renderer2
|
||||
) { }
|
||||
) {}
|
||||
|
||||
ngOnInit() { }
|
||||
ngOnInit() {}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.destroy$.next();
|
||||
@@ -97,11 +97,15 @@ export class ProductCardComponent implements OnInit, OnDestroy, AfterViewChecked
|
||||
}
|
||||
this.saveProductPosition.emit(this.index);
|
||||
// TODO: this is temporary solution for the incostency of product detail API
|
||||
const products = this.store.selectSnapshot(SharedSelectors.getSearchedProducts);
|
||||
const products = this.store.selectSnapshot(
|
||||
SharedSelectors.getSearchedProducts
|
||||
);
|
||||
if (products && products.itemsDTO) {
|
||||
const productDetailsData = products.itemsDTO[product.id];
|
||||
if (productDetailsData) {
|
||||
this.store.dispatch(new AddSelectedProduct(productDetailsData, this.index));
|
||||
this.store.dispatch(
|
||||
new AddSelectedProduct(productDetailsData, this.index)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,8 +113,11 @@ export class ProductCardComponent implements OnInit, OnDestroy, AfterViewChecked
|
||||
this.store.dispatch(
|
||||
new AddBreadcrumb(
|
||||
{
|
||||
name: product.title ? product.title.substring(0, 12) + (product.title.length > 12 ? '...' : '') : '',
|
||||
path: '/product/details/' + product.id
|
||||
name: product.title
|
||||
? product.title.substring(0, 12) +
|
||||
(product.title.length > 12 ? '...' : '')
|
||||
: '',
|
||||
path: '/product/details/' + product.id,
|
||||
},
|
||||
'product'
|
||||
)
|
||||
@@ -142,11 +149,4 @@ export class ProductCardComponent implements OnInit, OnDestroy, AfterViewChecked
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
splitAuthors(author: string) {
|
||||
if (author) {
|
||||
const _authors = author.split(';');
|
||||
this.authors = _authors.map(a => a.trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,18 @@
|
||||
<div
|
||||
id="product-detail-container"
|
||||
class="product-detail-container"
|
||||
#productDetailContainer
|
||||
*ngIf="product"
|
||||
[@shrinkMainCard]="expanded"
|
||||
(scroll)="scrollHandler($event)"
|
||||
>
|
||||
<div id="product-detail-container" class="product-detail-container" #productDetailContainer *ngIf="product"
|
||||
[@shrinkMainCard]="expanded" (scroll)="scrollHandler($event)">
|
||||
<div class="feature-icons" *ngIf="showFeatures">
|
||||
<lib-icon name="subscription_tag" alt="subscription icon" type="png" height="48px" *ngIf="isSubscription"></lib-icon>
|
||||
<lib-icon name="subscription_tag" alt="subscription icon" type="png" height="48px" *ngIf="isSubscription">
|
||||
</lib-icon>
|
||||
<lib-icon name="gift_tag" alt="gift icon" type="png" height="48px" *ngIf="isGiftable"></lib-icon>
|
||||
<lib-icon name="recommendation_tag" alt="recommendation icon" type="png" height="48px" *ngIf="isRecommendation"></lib-icon>
|
||||
<lib-icon name="recommendation_tag" alt="recommendation icon" type="png" height="48px" *ngIf="isRecommendation">
|
||||
</lib-icon>
|
||||
</div>
|
||||
|
||||
<div [@shrinkTitle]="!expanded" (click)="expand()">{{ product.title }}</div>
|
||||
<div class="general-details" *ngIf="expanded">
|
||||
<div class="product-image-container">
|
||||
<img [src]="product.imageId | bookImageUrl: 469:575 | async" class="product-image" alt="book image" (click)="openGallery()" />
|
||||
<img [src]="product.imageId | bookImageUrl: 469:575 | async" class="product-image" alt="book image"
|
||||
(click)="openGallery()" />
|
||||
<div class="img-magnifying-glass" (click)="openGallery()">
|
||||
<lib-icon name="magnifying-glass" width="27px"></lib-icon>
|
||||
</div>
|
||||
@@ -45,7 +42,9 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="volume-edition">
|
||||
<span>{{ product.volume }} <span *ngIf="product.volume && product.edition">|</span> {{ product.edition }}</span>
|
||||
<span>{{ product.volume }}
|
||||
<span *ngIf="product.volume && product.edition">|</span>
|
||||
{{ product.edition }}</span>
|
||||
</div>
|
||||
<div class="publication">
|
||||
<span>{{ product.publicationDate }}</span>
|
||||
@@ -65,11 +64,8 @@
|
||||
<span>{{ product.publisher }}</span>
|
||||
</div>
|
||||
<div class="quantity-category">
|
||||
<div
|
||||
class="quantity-wrapper align-right"
|
||||
[ngClass]="{ 'stock-loading': !stockInfoLoaded }"
|
||||
*ngIf="product.formatIcon !== 'EB' && product.formatIcon !== 'DL'"
|
||||
>
|
||||
<div class="quantity-wrapper align-right" [ngClass]="{ 'stock-loading': !stockInfoLoaded }"
|
||||
*ngIf="product.formatIcon !== 'EB' && product.formatIcon !== 'DL'">
|
||||
<div class="home-icon" *ngIf="stockInfoLoaded">
|
||||
<lib-icon name="House_Icon_grey" height="22px"></lib-icon>
|
||||
</div>
|
||||
@@ -97,10 +93,13 @@
|
||||
<div class="lang-ean-age-availability-status">
|
||||
<div class="lang-ean-age">
|
||||
<div class="languages standart-text">
|
||||
<span
|
||||
>{{ product.genre.length > 20 ? product.genre.substring(0, 20) + '...' : product.genre }}
|
||||
<span *ngIf="product.genre && product.locale">|</span> {{ product.locale }}</span
|
||||
>
|
||||
<span>{{
|
||||
product.genre.length > 20
|
||||
? product.genre.substring(0, 20) + '...'
|
||||
: product.genre
|
||||
}}
|
||||
<span *ngIf="product.genre && product.locale">|</span>
|
||||
{{ product.locale }}</span>
|
||||
</div>
|
||||
<div class="ean standart-text">
|
||||
<span>{{ product.eanTag }}</span>
|
||||
@@ -111,39 +110,43 @@
|
||||
</div>
|
||||
<div class="avilability-status" *ngIf="!(showAvLoading | async)">
|
||||
<div class="status-icons" *ngIf="!(isDownload | async)">
|
||||
<div class="align-right ship-icon align-right" *ngIf="(shippingAvailable | async) && !(deliveryError$ |
|
||||
async)">
|
||||
<div class="align-right ship-icon align-right"
|
||||
*ngIf="(shippingAvailable | async) && !(deliveryError$ | async)">
|
||||
<lib-icon name="Truck_Icon_grey" height="17px"></lib-icon>
|
||||
</div>
|
||||
<div class="align-right ship-icon align-right" *ngIf="(deliveryError$ | async)">
|
||||
<lib-icon name="Icon_Truck_Questionmark" mt="-4px" height="22px"
|
||||
tooltip="{{(deliveryError$ | async)}}" placement="top" delay="200"></lib-icon>
|
||||
<div class="align-right ship-icon align-right" *ngIf="deliveryError$ | async">
|
||||
<lib-icon name="Icon_Truck_Questionmark" mt="-4px" height="22px" tooltip="{{ deliveryError$ | async }}"
|
||||
placement="top" delay="200"></lib-icon>
|
||||
</div>
|
||||
<div class="align-right send-icon" *ngIf="(storeAvailable | async) && !(storeError$ | async)">
|
||||
<lib-icon name="Package_Icon_grey" height="18px"></lib-icon>
|
||||
</div>
|
||||
<div class="align-right send-icon" *ngIf="(storeError$ | async)">
|
||||
<lib-icon name="Icon_Pickup_Questionmark" mt="-4px" height="22px"
|
||||
tooltip="{{(storeError$ | async)}}" placement="top" delay="200"></lib-icon>
|
||||
<div class="align-right send-icon" *ngIf="storeError$ | async">
|
||||
<lib-icon name="Icon_Pickup_Questionmark" mt="-4px" height="22px" tooltip="{{ storeError$ | async }}"
|
||||
placement="top" delay="200"></lib-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="availability-status-text align-right">
|
||||
<span>{{ availabilityStatusText }}</span>
|
||||
<span *ngIf="availabilityStatus">{{ availabilityStatus }}</span><span
|
||||
*ngIf="availabilityStatus && availabilityStatus"> - </span>
|
||||
<span *ngIf="availabilityStatusText">{{
|
||||
availabilityStatusText
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="showAvLoading | async">
|
||||
<app-availability-loading></app-availability-loading>
|
||||
</div>
|
||||
<ng-container>
|
||||
<div *ngIf="showAvLoading | async">
|
||||
<app-availability-loading></app-availability-loading>
|
||||
</div>
|
||||
</ng-container>
|
||||
<div></div>
|
||||
<div class="category align-right" *ngIf="!(isDownload | async)">
|
||||
<span>{{ product.assortment }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="product-staus-info"
|
||||
[ngClass]="{ 'partly-loaded': !fullyLoaded }"
|
||||
*ngIf="(productReview$ | async)?.employeeReviews || (fullStars | async)"
|
||||
>
|
||||
<div class="product-staus-info" [ngClass]="{ 'partly-loaded': !fullyLoaded }" *ngIf="
|
||||
(productReview$ | async)?.employeeReviews || (fullStars | async)
|
||||
">
|
||||
<div class="stars-wrapper">
|
||||
<ng-container *ngFor="let star of fullStars | async">
|
||||
<lib-icon name="Star_full" width="12px" type="png"></lib-icon>
|
||||
@@ -165,19 +168,24 @@
|
||||
</div>
|
||||
<div class="separator" *ngIf="expanded"></div>
|
||||
<div class="product-details" *ngIf="expanded">
|
||||
<ng-container *ngIf="product.fullDescription && product.fullDescription.length > 0 && showNoDescription === false; else noDescription">
|
||||
<ng-container *ngIf="
|
||||
product.fullDescription &&
|
||||
product.fullDescription.length > 0 &&
|
||||
showNoDescription === false;
|
||||
else noDescription
|
||||
">
|
||||
<ng-container *ngIf="product.fullDescription.length > 100">
|
||||
<div class="details" id="details-container" *ngIf="!moreBtn">
|
||||
<span id="details-text">{{ product.fullDescription | descriptionText }}</span>
|
||||
<span class="more-btn" (click)="toggleMore('more')"
|
||||
>Mehr
|
||||
<span id="details-text">{{
|
||||
product.fullDescription | descriptionText
|
||||
}}</span>
|
||||
<span class="more-btn" (click)="toggleMore('more')">Mehr
|
||||
<lib-icon name="Arrow_Next-with-body" pl="8px" alt="more"></lib-icon>
|
||||
</span>
|
||||
</div>
|
||||
<div class="details-full" id="details-container" *ngIf="moreBtn">
|
||||
<span id="details-text" [innerHTML]="product.fullDescription"></span>
|
||||
<span class="more-btn opened" (click)="toggleMore('less')"
|
||||
>Weniger
|
||||
<span class="more-btn opened" (click)="toggleMore('less')">Weniger
|
||||
<lib-icon name="Arrow_back" pl="8px" alt="less"></lib-icon>
|
||||
</span>
|
||||
</div>
|
||||
@@ -195,33 +203,16 @@
|
||||
</div>
|
||||
</ng-template>
|
||||
<div class="actions align-right" [ngClass]="{ 'action-partly-loaded': !fullyLoaded }">
|
||||
<app-button (action)="openBranchesAvailabilityModal()" *ngIf="product.formatIcon !== 'EB' && product.formatIcon !== 'DL'"
|
||||
>weitere Verfügbarkeiten</app-button
|
||||
>
|
||||
<app-button
|
||||
[primary]="true"
|
||||
[load]="true"
|
||||
[stayOnPage]="true"
|
||||
[disabled]="inTheCartButtonDisabled"
|
||||
(loaded)="contentLoaded()"
|
||||
(action)="openModal()"
|
||||
#addtocart
|
||||
>In den Warenkorb</app-button
|
||||
>
|
||||
<app-button (action)="openBranchesAvailabilityModal()"
|
||||
*ngIf="product.formatIcon !== 'EB' && product.formatIcon !== 'DL'">weitere Verfügbarkeiten</app-button>
|
||||
<app-button [primary]="true" [load]="true" [stayOnPage]="true" [disabled]="inTheCartButtonDisabled"
|
||||
(loaded)="contentLoaded()" (action)="openModal()" #addtocart>In den Warenkorb</app-button>
|
||||
</div>
|
||||
</div>
|
||||
<app-product-other-formats
|
||||
[productFormats]="product.otherFormats"
|
||||
[detailsExpanded]="detailsExpanded"
|
||||
#otherformats
|
||||
></app-product-other-formats>
|
||||
<lib-photo-gallery
|
||||
*ngIf="item"
|
||||
#photoGallery
|
||||
[productId]="item.id"
|
||||
[images]="images$ | async"
|
||||
[title]="product.title"
|
||||
></lib-photo-gallery>
|
||||
<app-product-other-formats [productFormats]="product.otherFormats" [detailsExpanded]="detailsExpanded" #otherformats>
|
||||
</app-product-other-formats>
|
||||
<lib-photo-gallery *ngIf="item" #photoGallery [productId]="item.id" [images]="images$ | async"
|
||||
[title]="product.title"></lib-photo-gallery>
|
||||
</div>
|
||||
<div loading="true" *ngIf="!product && !loadingError">
|
||||
<app-product-details-loading></app-product-details-loading>
|
||||
@@ -237,43 +228,35 @@
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-container
|
||||
*ngIf="
|
||||
<ng-container *ngIf="
|
||||
item &&
|
||||
product &&
|
||||
(((storeAvLoaded === true && shippingAvLoaded === true) || downloadLoaded === true) &&
|
||||
((takeNowAvailable | async) || (storeAvailable | async) || (shippingAvailable | async) || (downloadAvailable | async)))
|
||||
"
|
||||
>
|
||||
<app-checkout
|
||||
#checkout
|
||||
(closed)="cartActionCompleted($event)"
|
||||
[currentPickUpDate]="currentPickUpDate"
|
||||
[currentDeliveryDate]="currentDeliveryDate"
|
||||
[book]="item"
|
||||
[availability]="availability"
|
||||
[avpickupprice]="pickUpPrice"
|
||||
[avdeliveryprice]="deliveryPrice"
|
||||
[avdownloadprice]="downloadPrice"
|
||||
[isDownload]="product.formatIcon === 'EB' || product.formatIcon === 'DL'"
|
||||
></app-checkout>
|
||||
((storeAvLoaded === true && shippingAvLoaded === true) ||
|
||||
downloadLoaded === true) &&
|
||||
((takeNowAvailable | async) ||
|
||||
(storeAvailable | async) ||
|
||||
(shippingAvailable | async) ||
|
||||
(downloadAvailable | async))
|
||||
">
|
||||
<app-checkout #checkout (closed)="cartActionCompleted($event)" [currentPickUpDate]="currentPickUpDate"
|
||||
[currentDeliveryDate]="currentDeliveryDate" [book]="item" [availability]="availability"
|
||||
[avpickupprice]="pickUpPrice" [avdeliveryprice]="deliveryPrice" [avdownloadprice]="downloadPrice"
|
||||
[isDownload]="product.formatIcon === 'EB' || product.formatIcon === 'DL'"></app-checkout>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="item && loadBranchesInfoComponent">
|
||||
<app-branches-avalability-overview
|
||||
(destroymodal)="destroyAvailabilityModal($event)"
|
||||
[item]="item"
|
||||
#branchesAvailabilityInfo
|
||||
></app-branches-avalability-overview>
|
||||
<app-branches-avalability-overview (destroymodal)="destroyAvailabilityModal($event)" [item]="item"
|
||||
#branchesAvailabilityInfo></app-branches-avalability-overview>
|
||||
</ng-container>
|
||||
|
||||
<app-printer-selection #printModal (print)="print($event)"></app-printer-selection>
|
||||
|
||||
<div class="recommendations" [@shrinkSecondary]="!expanded" *ngIf="item && !loadingError">
|
||||
<div class="header" *ngIf="expanded" (click)="expand()">
|
||||
<lib-icon name="recommendation_tag" alt="recommendation icon" type="png" height="48px" class="recommendation-card-icon"></lib-icon>
|
||||
<lib-icon name="recommendation_tag" alt="recommendation icon" type="png" height="48px"
|
||||
class="recommendation-card-icon"></lib-icon>
|
||||
<span>Empfehlungen</span>
|
||||
</div>
|
||||
|
||||
<app-recommendations [book]="item" #recommendations></app-recommendations>
|
||||
</div>
|
||||
</div>
|
||||
@@ -338,7 +338,7 @@
|
||||
|
||||
.lang-ean-age-availability-status {
|
||||
display: grid;
|
||||
grid-template-columns: auto 173px;
|
||||
grid-template-columns: auto 250px;
|
||||
grid-gap: 5px;
|
||||
|
||||
.avilability-status {
|
||||
@@ -352,9 +352,11 @@
|
||||
}
|
||||
|
||||
.availability-status-text {
|
||||
width: 173px;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
line-height: 21px;
|
||||
text-align: end;
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -375,7 +377,7 @@
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
##Device = Big Desktops
|
||||
*/
|
||||
@media (min-width: 1281px) {
|
||||
@@ -393,7 +395,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
##Device = Laptops, Desktops, Ipad pro
|
||||
*/
|
||||
@media (min-width: 1025px) and (max-width: 1280px) {
|
||||
@@ -411,7 +413,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
##Device = Tablets, Ipads
|
||||
*/
|
||||
@media (min-width: 768px) and (max-width: 1024px) {
|
||||
@@ -420,13 +422,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
##Device = Low Resolution Tablets, Mobiles (Landscape)
|
||||
*/
|
||||
@media (min-width: 481px) and (max-width: 767px) {
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
##Device = Most of the Smartphones Mobiles (Portrait)
|
||||
*/
|
||||
@media (min-width: 320px) and (max-width: 480px) {
|
||||
|
||||
@@ -68,6 +68,7 @@ export class ProductDetailsComponent implements OnInit, OnDestroy {
|
||||
_halfStars = 0;
|
||||
_emptyStars = 0;
|
||||
loadBranchesInfoComponent = false;
|
||||
availabilityStatus: string;
|
||||
availabilityStatusText: string;
|
||||
currentPickUpDate = '';
|
||||
currentDeliveryDate = '';
|
||||
@@ -632,7 +633,7 @@ export class ProductDetailsComponent implements OnInit, OnDestroy {
|
||||
.getShippingAvailabilityWithCheck(item, ean, branch.id)
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe((response) => {
|
||||
if ((response as { error: boolean; message: string; type: CheckoutType }).error) {
|
||||
if ((response as { error: boolean; message: string; type: CheckoutType; }).error) {
|
||||
this.downloadError = true;
|
||||
this.downloadLoaded = true;
|
||||
if (this.addToCartBtn) {
|
||||
@@ -647,6 +648,7 @@ export class ProductDetailsComponent implements OnInit, OnDestroy {
|
||||
if (successfulResponse) {
|
||||
const preferredAvailability = successfulResponse.av.find((t) => t.preferred === 1 && t.supplier === 'DIG');
|
||||
if (preferredAvailability) {
|
||||
this.availabilityStatus = preferredAvailability.ssc;
|
||||
this.availabilityStatusText = preferredAvailability.sscText;
|
||||
if (preferredAvailability.price && preferredAvailability.price.value && preferredAvailability.price.value.value) {
|
||||
this.downloadPrice = preferredAvailability.price.value.value;
|
||||
@@ -684,7 +686,7 @@ export class ProductDetailsComponent implements OnInit, OnDestroy {
|
||||
|
||||
async loadAvailability(it: ItemDTO, ean: string): Promise<boolean> {
|
||||
const availabilityObservables: Observable<
|
||||
{ branchId: number; type: CheckoutType; av: AvailabilityDTO[] } | { error: boolean; message: string; type: CheckoutType }
|
||||
{ branchId: number; type: CheckoutType; av: AvailabilityDTO[]; } | { error: boolean; message: string; type: CheckoutType; }
|
||||
>[] = [];
|
||||
const userBranchNumber = this.store.selectSnapshot(BranchSelectors.getUserBranch);
|
||||
const branch = this.store.selectSnapshot(BranchSelectors.getBranchesIterable).find((t) => t.branchNumber === userBranchNumber);
|
||||
@@ -713,7 +715,7 @@ export class ProductDetailsComponent implements OnInit, OnDestroy {
|
||||
type: CheckoutType;
|
||||
av: AvailabilityDTO[];
|
||||
}
|
||||
| { error: boolean; message: string; type: CheckoutType }
|
||||
| { error: boolean; message: string; type: CheckoutType; }
|
||||
) => {
|
||||
if (
|
||||
(response as {
|
||||
@@ -751,6 +753,7 @@ export class ProductDetailsComponent implements OnInit, OnDestroy {
|
||||
return;
|
||||
}
|
||||
if (item.type === CheckoutType.store) {
|
||||
this.availabilityStatus = preferredAvailability.ssc;
|
||||
this.availabilityStatusText = preferredAvailability.sscText;
|
||||
this.currentPickUpDate = preferredAvailability.at
|
||||
? this.datePipe.transform(new Date(preferredAvailability.at), 'dd.MM.yy')
|
||||
@@ -790,6 +793,8 @@ export class ProductDetailsComponent implements OnInit, OnDestroy {
|
||||
if (this.addToCartBtn) {
|
||||
this.addToCartBtn.stopLoading();
|
||||
}
|
||||
|
||||
this.cdrf.detectChanges();
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ export class ProductResultsComponent implements OnInit, OnDestroy, AfterViewInit
|
||||
products: Product[];
|
||||
skip = 0;
|
||||
processCount = 0;
|
||||
@ViewChild('scroller', { static: true }) scroller: CdkVirtualScrollViewport;
|
||||
@ViewChild('scroller', { static: false }) scroller: CdkVirtualScrollViewport;
|
||||
@ViewChildren('productCard') productCardList: QueryList<ProductCardComponent>;
|
||||
loading = true;
|
||||
filters: Filter[];
|
||||
|
||||
@@ -7,9 +7,21 @@ import { ProductCardComponent } from './components/product-card/product-card.com
|
||||
import { ScrollingModule } from '@angular/cdk/scrolling';
|
||||
import { ScrollingModule as ExperimentalScrollingModule } from '@angular/cdk-experimental/scrolling';
|
||||
import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
|
||||
import { LoadingModule, PhotoGalleryModule, CardModule, SearchInputModule, DeleteDropdownModule } from '@libs/ui';
|
||||
import {
|
||||
LoadingModule,
|
||||
PhotoGalleryModule,
|
||||
CardModule,
|
||||
SearchInputModule,
|
||||
DeleteDropdownModule,
|
||||
} from '@libs/ui';
|
||||
import { SharedModule } from './../../shared/shared.module';
|
||||
import { ModalModule, ButtonModule, InputModule, DropdownModule, IconModule } from '@libs/ui';
|
||||
import {
|
||||
ModalModule,
|
||||
ButtonModule,
|
||||
InputModule,
|
||||
DropdownModule,
|
||||
IconModule,
|
||||
} from '@libs/ui';
|
||||
import { ProductDetailsComponent } from './pages/product-details/product-details.component';
|
||||
import { RecommendationsdModule } from './../recommendations/recommendations.module';
|
||||
import { ProductReviewComponent } from './components/product-review/product-review.component';
|
||||
@@ -37,6 +49,7 @@ import { UiSwitchModule } from 'ngx-toggle-switch';
|
||||
import { InteractiveFiltersComponent } from './components/interactive-filters/interactive-filters.component';
|
||||
import { StockInfoLoadingComponent } from './components/stock-info-loading/stock-info-loading.component';
|
||||
import { SmallDoubleChoiceSwitchModule } from '@libs/ui/lib/small-double-choice-switch';
|
||||
import { GetAuthorsPipe } from './components/product-card/get-authors.pipe';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@@ -64,7 +77,7 @@ import { SmallDoubleChoiceSwitchModule } from '@libs/ui/lib/small-double-choice-
|
||||
SearchDropdownModule,
|
||||
PrinterSelectionModule,
|
||||
UiSwitchModule,
|
||||
SmallDoubleChoiceSwitchModule
|
||||
SmallDoubleChoiceSwitchModule,
|
||||
],
|
||||
declarations: [
|
||||
ProductResultsComponent,
|
||||
@@ -86,9 +99,10 @@ import { SmallDoubleChoiceSwitchModule } from '@libs/ui/lib/small-double-choice-
|
||||
ProductDetailsLoadingComponent,
|
||||
ProductOtherFormatsComponent,
|
||||
InteractiveFiltersComponent,
|
||||
StockInfoLoadingComponent
|
||||
StockInfoLoadingComponent,
|
||||
GetAuthorsPipe,
|
||||
],
|
||||
exports: [],
|
||||
providers: [BookImagePipe, BookThumbnailPipe, DescriptionTextPipe]
|
||||
providers: [BookImagePipe, BookThumbnailPipe, DescriptionTextPipe],
|
||||
})
|
||||
export class ProductModule {}
|
||||
|
||||
@@ -1,18 +1,27 @@
|
||||
import {
|
||||
trigger,
|
||||
transition,
|
||||
stagger,
|
||||
animate,
|
||||
style,
|
||||
query,
|
||||
state
|
||||
state,
|
||||
} from '@angular/animations';
|
||||
|
||||
export const fadeInAnimation = trigger('fadeIn', [
|
||||
state('true', style({ opacity: 1, transform: 'translateY(0)' })),
|
||||
transition(':enter', [
|
||||
style({ opacity: 0, transform: 'translateY(-50px)' }),
|
||||
animate('200ms ease-in')
|
||||
animate('200ms ease-in'),
|
||||
]),
|
||||
transition(':leave', animate(200, style({ opacity: 0 })))
|
||||
transition(':leave', animate(200, style({ opacity: 0 }))),
|
||||
]);
|
||||
|
||||
export function fadeInWithDelay(delayInSec: number) {
|
||||
return trigger('fadeInWithDelay', [
|
||||
state('true', style({ opacity: 1, transform: 'translateY(0)' })),
|
||||
transition(':enter', [
|
||||
style({ opacity: 0, transform: 'translateY(-50px)' }),
|
||||
animate(`200ms ${delayInSec}s ease-in`),
|
||||
]),
|
||||
transition(':leave', animate(200, style({ opacity: 0 }))),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -94,11 +94,20 @@
|
||||
name="Delete-white"
|
||||
></lib-icon>
|
||||
</div>
|
||||
<div class="feature-tooltip-arrow-down" [style.marginLeft.px]="tooltipArrowMl" *ngIf="toolTipOpened"></div>
|
||||
<div class="features item" *ngIf="isArray(product.features); else emptyFeatures">
|
||||
<span *ngFor="let feature of product.features; let index = index" (click)="openTooltip(feature.name, index)">{{
|
||||
feature.key
|
||||
}}</span>
|
||||
<div
|
||||
class="feature-tooltip-arrow-down"
|
||||
[style.marginLeft.px]="tooltipArrowMl"
|
||||
*ngIf="toolTipOpened"
|
||||
></div>
|
||||
<div
|
||||
class="features item"
|
||||
*ngIf="isArray(product.features); else emptyFeatures"
|
||||
>
|
||||
<span
|
||||
*ngFor="let feature of product.features; let index = index"
|
||||
(click)="openTooltip(feature.name, index)"
|
||||
>{{ feature.key }}</span
|
||||
>
|
||||
</div>
|
||||
<ng-template #emptyFeatures>
|
||||
<div class="empty-feature"></div>
|
||||
@@ -130,7 +139,11 @@
|
||||
</div>
|
||||
<hr class="spacer branch" />
|
||||
<div class="actions">
|
||||
<app-button primary="true" (action)="addProdcutToList()"
|
||||
<app-button
|
||||
[load]="true"
|
||||
primary="true"
|
||||
(action)="addProdcutToList()"
|
||||
#addProductButton
|
||||
>Hinzufügen</app-button
|
||||
>
|
||||
</div>
|
||||
|
||||
@@ -1,17 +1,30 @@
|
||||
import { Component, OnInit, Input, EventEmitter, Output, OnDestroy } from '@angular/core';
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
Input,
|
||||
EventEmitter,
|
||||
Output,
|
||||
OnDestroy,
|
||||
ViewChild,
|
||||
} from '@angular/core';
|
||||
import { RemissionProduct, RemissionService } from '@isa/remission';
|
||||
import { ModalService } from '@libs/ui';
|
||||
import { filter, take } from 'rxjs/operators';
|
||||
import { ModalService, ButtonComponent } from '@libs/ui';
|
||||
import { filter, take, catchError, debounceTime, delay } from 'rxjs/operators';
|
||||
import { isNullOrUndefined } from 'util';
|
||||
import { NO_RESMISSION_REASON_SELECTED } from '../../constants/remission.constants';
|
||||
import { RemissionHelperService } from '../../services/remission-helper.service';
|
||||
import { Store } from '@ngxs/store';
|
||||
import { SetRemissionIsLoading } from 'apps/sales/src/app/core/store/actions/remission.actions';
|
||||
|
||||
@Component({
|
||||
selector: 'app-add-product-to-remission-dialog',
|
||||
templateUrl: './add-product-to-remission-dialog.component.html',
|
||||
styleUrls: ['./add-product-to-remission-dialog.component.scss']
|
||||
styleUrls: ['./add-product-to-remission-dialog.component.scss'],
|
||||
})
|
||||
export class AddProductToRemissionDialogComponent implements OnInit, OnDestroy {
|
||||
@ViewChild('addProductButton', { static: false })
|
||||
addProductButton: ButtonComponent;
|
||||
|
||||
id = 'remission-add-product-to-remission-modal';
|
||||
@Input() product: RemissionProduct;
|
||||
@Output() add = new EventEmitter<RemissionProduct>();
|
||||
@@ -34,7 +47,8 @@ export class AddProductToRemissionDialogComponent implements OnInit, OnDestroy {
|
||||
constructor(
|
||||
private modalService: ModalService,
|
||||
private remissionService: RemissionService,
|
||||
private remissionHelper: RemissionHelperService
|
||||
private remissionHelper: RemissionHelperService,
|
||||
private store: Store
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
@@ -47,10 +61,10 @@ export class AddProductToRemissionDialogComponent implements OnInit, OnDestroy {
|
||||
this.remissionService
|
||||
.getRemissionReasons()
|
||||
.pipe(
|
||||
filter(data => !isNullOrUndefined(data)),
|
||||
filter((data) => !isNullOrUndefined(data)),
|
||||
take(1)
|
||||
)
|
||||
.subscribe(reasons => {
|
||||
.subscribe((reasons) => {
|
||||
this.reasons = reasons;
|
||||
this.reason = NO_RESMISSION_REASON_SELECTED;
|
||||
});
|
||||
@@ -108,14 +122,31 @@ export class AddProductToRemissionDialogComponent implements OnInit, OnDestroy {
|
||||
this.reasonError = true;
|
||||
return;
|
||||
}
|
||||
|
||||
this.store.dispatch(new SetRemissionIsLoading(true));
|
||||
this.addProductButton.startLoading();
|
||||
|
||||
this.remissionService
|
||||
.addProductToRemit({ product: this.product, remissionReason: this.reason, remissionQuantity: this.quantity })
|
||||
.pipe(take(1))
|
||||
.subscribe(response => {
|
||||
.addProductToRemit({
|
||||
product: this.product,
|
||||
remissionReason: this.reason,
|
||||
remissionQuantity: this.quantity,
|
||||
})
|
||||
.pipe(
|
||||
catchError((err) => {
|
||||
this.store.dispatch(new SetRemissionIsLoading(false));
|
||||
this.addProductButton.stopLoading();
|
||||
return [err];
|
||||
}),
|
||||
debounceTime(250),
|
||||
take(1),
|
||||
delay(2000)
|
||||
)
|
||||
.subscribe((product: RemissionProduct) => {
|
||||
this.reasonError = false;
|
||||
this.remissionHelper.addRemissionListItem(response);
|
||||
this.remissionHelper.addRemissionListItem(product);
|
||||
this.modalService.close(this.id);
|
||||
this.add.emit(this.product);
|
||||
this.add.emit(product);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,31 +6,48 @@ import { isNullOrUndefined } from 'util';
|
||||
|
||||
@Component({
|
||||
selector: 'app-remission-add-product-to-shipping-document-partially-dialog',
|
||||
templateUrl: './remission-add-product-to-shipping-document-partially-dialog.component.html',
|
||||
styleUrls: ['./remission-add-product-to-shipping-document-partially-dialog.component.scss']
|
||||
templateUrl:
|
||||
'./remission-add-product-to-shipping-document-partially-dialog.component.html',
|
||||
styleUrls: [
|
||||
'./remission-add-product-to-shipping-document-partially-dialog.component.scss',
|
||||
],
|
||||
})
|
||||
export class RemissionAddProductToShippingDocumentPartiallyDialogComponent implements OnInit {
|
||||
export class RemissionAddProductToShippingDocumentPartiallyDialogComponent
|
||||
implements OnInit {
|
||||
id = 'remission-add-product-to-shipping-document-partially-modal';
|
||||
@Input() quantity: number;
|
||||
@Input() placementType: RemissionPlacementType;
|
||||
@Output() add = new EventEmitter();
|
||||
@Output() closed = new EventEmitter<{ quantity: number; placementType: string }>();
|
||||
@Output() closed = new EventEmitter<{
|
||||
quantity: number;
|
||||
placementType: string;
|
||||
}>();
|
||||
|
||||
placementTypes: RemissionPlacementType[];
|
||||
loading = false;
|
||||
predefinedRemissionQuantity: number;
|
||||
|
||||
constructor(private modalService: ModalService, private remissionService: RemissionService) {}
|
||||
constructor(
|
||||
private modalService: ModalService,
|
||||
private remissionService: RemissionService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.remissionService
|
||||
.getPlacementTypes()
|
||||
.pipe(
|
||||
filter(data => !isNullOrUndefined(data) && data.length > 0),
|
||||
filter((data) => !isNullOrUndefined(data) && data.length > 0),
|
||||
take(1)
|
||||
)
|
||||
.subscribe(placementTypes => {
|
||||
.subscribe((placementTypes) => {
|
||||
this.placementTypes = placementTypes;
|
||||
});
|
||||
|
||||
this.setPredefinedRemissionQuantity(this.quantity);
|
||||
}
|
||||
|
||||
setPredefinedRemissionQuantity(totalRemissionQuantity: number) {
|
||||
this.predefinedRemissionQuantity = totalRemissionQuantity;
|
||||
}
|
||||
|
||||
public openDialog() {
|
||||
@@ -48,7 +65,11 @@ export class RemissionAddProductToShippingDocumentPartiallyDialogComponent imple
|
||||
if (isNullOrUndefined(this.quantity) || this.quantity < 0) {
|
||||
return;
|
||||
}
|
||||
this.add.emit({ quantity: this.quantity, placementType: this.placementType });
|
||||
this.add.emit({
|
||||
quantity: this.quantity,
|
||||
placementType: this.placementType,
|
||||
predefinedRemissionQuantity: this.predefinedRemissionQuantity,
|
||||
});
|
||||
}
|
||||
|
||||
placementUpdated(placementType: RemissionPlacementType) {
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
margin-bottom: 16px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #557596;
|
||||
color: #596470;
|
||||
padding: 0 10%;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,23 +13,10 @@
|
||||
<app-remission-open-shipping-documents-widget></app-remission-open-shipping-documents-widget>
|
||||
</div>
|
||||
</div>
|
||||
<div class="start-remission">
|
||||
<app-button [primary]="true" (action)="startRemission.emit()"
|
||||
>Remission starten</app-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="actions" *ngIf="status === 'started'">
|
||||
<div class="remit-all">
|
||||
<span>Wanne befüllen</span>
|
||||
</div>
|
||||
<div class="complete-remission">
|
||||
<app-button
|
||||
[primary]="true"
|
||||
[disabled]="!enableCompleteShippingDocument"
|
||||
(action)="completeShippingDocument.emit()"
|
||||
>Wanne abschließen</app-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,16 +3,11 @@
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
.start-remission,
|
||||
.add-article {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.start-remission {
|
||||
padding-top: 22px;
|
||||
}
|
||||
|
||||
.secondary-actions {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
// start:ng42.barrel
|
||||
export * from './remission-list-start-action.component';
|
||||
// end:ng42.barrel
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<div class="start-remission">
|
||||
<button
|
||||
class="isa-btn isa-btn-l isa-btn-primary isa-btn-shadow isa-btn-pill"
|
||||
(click)="startRemission.emit()"
|
||||
>
|
||||
Remission starten
|
||||
</button>
|
||||
</div>
|
||||
@@ -0,0 +1,5 @@
|
||||
.start-remission {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding-top: 22px;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import {
|
||||
Component,
|
||||
ChangeDetectionStrategy,
|
||||
EventEmitter,
|
||||
Output,
|
||||
} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-remission-list-start-action',
|
||||
templateUrl: 'remission-list-start-action.component.html',
|
||||
styleUrls: ['./remission-list-start-action.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class RemissionListStartActionComponent {
|
||||
@Output() startRemission = new EventEmitter<void>();
|
||||
|
||||
constructor() {}
|
||||
}
|
||||
@@ -60,9 +60,9 @@ export class RemissionListCardStartedComponent implements OnInit {
|
||||
private remissionHelper: RemissionHelperService,
|
||||
private cdr: ChangeDetectorRef,
|
||||
private errorService: ErrorService
|
||||
) { }
|
||||
) {}
|
||||
|
||||
ngOnInit() { }
|
||||
ngOnInit() {}
|
||||
|
||||
openTooltip(index: number) {
|
||||
this.toolTipOpened = this.toolTipOpened.map(
|
||||
@@ -136,6 +136,7 @@ export class RemissionListCardStartedComponent implements OnInit {
|
||||
remitPartially(partialRemission: {
|
||||
quantity: number;
|
||||
placementType: string;
|
||||
predefinedRemissionQuantity: number;
|
||||
}) {
|
||||
if (this.product.remissionQuantity < 1) {
|
||||
setTimeout(() => {
|
||||
@@ -146,6 +147,7 @@ export class RemissionListCardStartedComponent implements OnInit {
|
||||
const currentShippingDocument = this.store.selectSnapshot(
|
||||
RemissionSelectors.getRemissionShippingDocument
|
||||
);
|
||||
|
||||
this.remissionService
|
||||
.addProductToShippingDocument({
|
||||
remissionProcessId: this.remissionProcess.id,
|
||||
@@ -154,6 +156,8 @@ export class RemissionListCardStartedComponent implements OnInit {
|
||||
quantity: partialRemission.quantity,
|
||||
placementType: partialRemission.placementType,
|
||||
inStock: this.product.inStock,
|
||||
predefinedRemissionQuantity:
|
||||
partialRemission.predefinedRemissionQuantity,
|
||||
})
|
||||
.toPromise()
|
||||
.then((err) => {
|
||||
|
||||
@@ -1,37 +1,26 @@
|
||||
<div
|
||||
class="search-results"
|
||||
[style.height]="containerHeight === 0 ? '100%' : containerHeight + 'px'"
|
||||
infinite-scroll
|
||||
[infiniteScrollDistance]="scrollDistance"
|
||||
[infiniteScrollUpDistance]="scrollUpDistance"
|
||||
[fromRoot]="true"
|
||||
[infiniteScrollContainer]="'.remission-list-container'"
|
||||
(scrolled)="onScrollDown()"
|
||||
(scrolledUp)="onUp()"
|
||||
#remissionListContainer
|
||||
>
|
||||
<ng-container *ngIf="!(isLoading$ | async); else spinner">
|
||||
<ng-container *ngFor="let product of products; let last = last">
|
||||
<ng-container *ngIf="product != null">
|
||||
<app-remission-list-card
|
||||
*ngIf="!started"
|
||||
[last]="last"
|
||||
[product]="product"
|
||||
(deleteProduct)="deleteProduct(product)"
|
||||
></app-remission-list-card>
|
||||
<app-remission-list-card-started
|
||||
[remissionProcess]="remissionProcess"
|
||||
(updateShippingDocument)="updateShippingDocument.emit()"
|
||||
*ngIf="started"
|
||||
[product]="product"
|
||||
></app-remission-list-card-started>
|
||||
</ng-container>
|
||||
<ng-template #loadingComponent>
|
||||
<app-remission-list-card-loading></app-remission-list-card-loading>
|
||||
</ng-template>
|
||||
<div class="search-results" [style.height]="containerHeight === 0 ? '100%' : containerHeight + 'px'" infinite-scroll
|
||||
[infiniteScrollDistance]="scrollDistance" [infiniteScrollUpDistance]="scrollUpDistance" [fromRoot]="true"
|
||||
[infiniteScrollContainer]="'.remission-list-container'" (scrolled)="onScrollDown()" (scrolledUp)="onUp()"
|
||||
#remissionListContainer>
|
||||
<ng-container *ngIf="!(isLoading$ | async); else spinner"></ng-container>
|
||||
<ng-container *ngFor="let product of products; let last = last">
|
||||
<ng-container *ngIf="product != null">
|
||||
<app-remission-list-card *ngIf="!started" [last]="last" [product]="product" [class.loading]="isLoading$ | async"
|
||||
(deleteProduct)="deleteProduct(product)"></app-remission-list-card>
|
||||
<app-remission-list-card-started [remissionProcess]="remissionProcess"
|
||||
(updateShippingDocument)="updateShippingDocument.emit()" *ngIf="started" [product]="product"
|
||||
[class.loading]="isLoading$ | async">
|
||||
</app-remission-list-card-started>
|
||||
</ng-container>
|
||||
<ng-template #loadingComponent>
|
||||
<app-remission-list-card-loading></app-remission-list-card-loading>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
<ng-template #spinner>
|
||||
<div class="spinner"></div>
|
||||
</ng-template>
|
||||
</div>
|
||||
|
||||
<ng-template #spinner>
|
||||
<div class="spinner"></div>
|
||||
</ng-template>
|
||||
@@ -44,3 +44,7 @@
|
||||
cdk-virtual-scroll-viewport {
|
||||
-webkit-overflow-scrolling: auto !important;
|
||||
}
|
||||
|
||||
.loading {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,35 @@
|
||||
import { Component, OnInit, Input, OnDestroy, ViewChild, Output, EventEmitter, ElementRef, ChangeDetectorRef } from '@angular/core';
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
Input,
|
||||
OnDestroy,
|
||||
ViewChild,
|
||||
Output,
|
||||
EventEmitter,
|
||||
ElementRef,
|
||||
ChangeDetectorRef,
|
||||
} from '@angular/core';
|
||||
import { RemissionListDataSource } from './remission-list.datasource';
|
||||
import { RemissionService, RemissionProcess, RemissionProduct, RemissionFilter } from '@isa/remission';
|
||||
import {
|
||||
RemissionService,
|
||||
RemissionProcess,
|
||||
RemissionProduct,
|
||||
RemissionFilter,
|
||||
} from '@isa/remission';
|
||||
import { RemissionHelperService } from '../../services/remission-helper.service';
|
||||
import { Subject, of, Subscription, Observable } from 'rxjs';
|
||||
import { takeUntil, filter, take, catchError, share, debounceTime, first, map, switchMap, tap } from 'rxjs/operators';
|
||||
import {
|
||||
takeUntil,
|
||||
filter,
|
||||
take,
|
||||
catchError,
|
||||
share,
|
||||
debounceTime,
|
||||
first,
|
||||
map,
|
||||
switchMap,
|
||||
tap,
|
||||
} from 'rxjs/operators';
|
||||
import { isNullOrUndefined } from 'util';
|
||||
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
|
||||
import { Select, Store } from '@ngxs/store';
|
||||
@@ -17,7 +43,8 @@ import { SetRemissionIsLoading } from 'apps/sales/src/app/core/store/actions/rem
|
||||
})
|
||||
export class RemissionListComponent implements OnInit, OnDestroy {
|
||||
@ViewChild('scroller', { static: false }) scroller: CdkVirtualScrollViewport;
|
||||
@ViewChild('remissionListContainer', { static: false }) remissionListContainer: ElementRef;
|
||||
@ViewChild('remissionListContainer', { static: false })
|
||||
remissionListContainer: ElementRef;
|
||||
@Input() started = false;
|
||||
@Input() hits: number;
|
||||
@Input() remissionProcess: RemissionProcess;
|
||||
@@ -28,9 +55,12 @@ export class RemissionListComponent implements OnInit, OnDestroy {
|
||||
@Output() fullListLoaded = new EventEmitter();
|
||||
@Output() scroll = new EventEmitter<void>();
|
||||
@Output() updateShippingDocument = new EventEmitter<void>();
|
||||
@Select(RemissionSelectors.getRemissionProcess) remissionProcess$: Observable<RemissionProcess>;
|
||||
@Select(RemissionSelectors.getRemissionProcess) remissionProcess$: Observable<
|
||||
RemissionProcess
|
||||
>;
|
||||
@Select(RemissionSelectors.getRemissionActiveFilters)
|
||||
selectedFilters$: Observable<{ [key: string]: [] }>;
|
||||
|
||||
ds: RemissionListDataSource;
|
||||
destroy$ = new Subject();
|
||||
subscribedToProducts = false;
|
||||
topScrolledOffset = 0;
|
||||
@@ -60,7 +90,7 @@ export class RemissionListComponent implements OnInit, OnDestroy {
|
||||
private remissionHelper: RemissionHelperService,
|
||||
private store: Store,
|
||||
private cdr: ChangeDetectorRef
|
||||
) { }
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.fetchProductPaged(this.page)
|
||||
@@ -70,19 +100,44 @@ export class RemissionListComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
this.subscribeToRefetchProducts();
|
||||
this.subscribeToFetchLastProducts();
|
||||
this.subscribeToResetCurrentPage();
|
||||
this.subscribeToRefetchAllProducts();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.destroy$.next();
|
||||
}
|
||||
|
||||
subscribeToResetCurrentPage() {
|
||||
this.selectedFilters$
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe(() => this.resetPagesFetched());
|
||||
}
|
||||
|
||||
private resetPagesFetched() {
|
||||
this.page = 0;
|
||||
this.isFullListLoaded = false;
|
||||
this.isFullLoad = false;
|
||||
}
|
||||
|
||||
subscribeToRefetchAllProducts() {
|
||||
this.remissionHelper.refetchProducts$
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe(() => {
|
||||
this.refetchLoadedProducts(10);
|
||||
});
|
||||
}
|
||||
|
||||
subscribeToFetchLastProducts() {
|
||||
this.fetchLastProducts$.pipe(takeUntil(this.destroy$)).subscribe((_) => {
|
||||
// Increase number of takes to show all residual products (currently: 10)
|
||||
const takeParam = this.pageSize;
|
||||
const skipParam = Math.max(0, this.totalHits - takeParam);
|
||||
|
||||
if (this.products.length > this.pageSize + (this.totalHits % this.pageSize)) {
|
||||
if (
|
||||
this.products.length >
|
||||
this.pageSize + (this.totalHits % this.pageSize)
|
||||
) {
|
||||
this.products = this.cleanResidualProducts(this.products);
|
||||
}
|
||||
|
||||
@@ -95,15 +150,21 @@ export class RemissionListComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
private filterUniqueProducts(products: RemissionProduct[]) {
|
||||
return products.filter((product, i, allProducts) => allProducts.findIndex((t) => t.id === product.id) === i);
|
||||
return products.filter(
|
||||
(product, i, allProducts) =>
|
||||
allProducts.findIndex((t) => t.id === product.id) === i
|
||||
);
|
||||
}
|
||||
|
||||
subscribeToRefetchProducts() {
|
||||
this.refetchProducts$.pipe(debounceTime(10), takeUntil(this.destroy$)).subscribe((_) => this.fetchNewProducts());
|
||||
this.refetchProducts$
|
||||
.pipe(debounceTime(10), takeUntil(this.destroy$))
|
||||
.subscribe((_) => this.fetchNewProducts());
|
||||
}
|
||||
|
||||
onScrollDown() {
|
||||
this.scroll.emit();
|
||||
|
||||
if (this.isFullListLoaded || this.isFullLoad) {
|
||||
return;
|
||||
}
|
||||
@@ -112,6 +173,7 @@ export class RemissionListComponent implements OnInit, OnDestroy {
|
||||
|
||||
private fetchNewProducts() {
|
||||
this.page += 1;
|
||||
|
||||
this.fetchProductPaged(this.page).subscribe();
|
||||
}
|
||||
|
||||
@@ -182,46 +244,87 @@ export class RemissionListComponent implements OnInit, OnDestroy {
|
||||
map((process) => process.id),
|
||||
tap((_) => this.store.dispatch(new SetRemissionIsLoading(true))),
|
||||
switchMap((remissionProcessId) =>
|
||||
this.remissionService.getRemissionProducts({ remissionProcessId }).pipe(
|
||||
filter((data) => !isNullOrUndefined(data)),
|
||||
debounceTime(250)
|
||||
)
|
||||
this.remissionService
|
||||
.getRemissionProducts({ remissionProcessId })
|
||||
.pipe(
|
||||
filter((data) => !isNullOrUndefined(data)),
|
||||
debounceTime(250),
|
||||
catchError((_) => {
|
||||
this.store.dispatch(new SetRemissionIsLoading(false));
|
||||
return [
|
||||
{
|
||||
hits: 0,
|
||||
completed: true,
|
||||
},
|
||||
];
|
||||
})
|
||||
)
|
||||
),
|
||||
takeUntil(this.destroy$),
|
||||
tap((_) => this.store.dispatch(new SetRemissionIsLoading(false)))
|
||||
takeUntil(this.destroy$)
|
||||
)
|
||||
.subscribe((result: { skip?: number; take?: number; hits?: number; items: RemissionProduct[]; completed: boolean }) => {
|
||||
console.error('Subscribe [RemissionProducts]', result);
|
||||
.subscribe(
|
||||
(result: {
|
||||
skip?: number;
|
||||
take?: number;
|
||||
hits?: number;
|
||||
items: RemissionProduct[];
|
||||
completed: boolean;
|
||||
}) => {
|
||||
this.totalHits = result.hits;
|
||||
|
||||
this.totalHits = result.hits;
|
||||
this.updateFullListLoaded({
|
||||
totalHits: this.totalHits,
|
||||
numberOfProductsFetched: this.products.length,
|
||||
});
|
||||
|
||||
this.updateFullListLoaded({
|
||||
totalHits: this.totalHits,
|
||||
numberOfProductsFetched: this.products.length,
|
||||
});
|
||||
this.isLoading = false;
|
||||
|
||||
this.isLoading = false;
|
||||
if ((this.page === 0 || result.skip === 0) && !this.isFullLoad) {
|
||||
this.products = [];
|
||||
}
|
||||
this.remissionHelper.setRemissionListHits(result.hits || -1);
|
||||
if (result.items && result.items.length > 0) {
|
||||
if (!this.isItRefetch && !this.isFullLoad) {
|
||||
this.products = this.filterUniqueProducts([...this.products, ...result.items]);
|
||||
} else if (this.isItRefetch && !this.isFullLoad) {
|
||||
this.products.splice(result.skip, result.items.length, ...result.items);
|
||||
this.isItRefetch = false;
|
||||
} else if (this.isFullLoad) {
|
||||
this.products = this.filterUniqueProducts([...this.products, ...result.items]);
|
||||
this.isFullLoad = false;
|
||||
this.isFullListLoaded = true;
|
||||
this.cdr.detectChanges();
|
||||
this.fullListLoaded.emit();
|
||||
if (
|
||||
(this.page === 0 || result.skip === 0) &&
|
||||
result.completed &&
|
||||
result.hits > 0 &&
|
||||
result.items
|
||||
) {
|
||||
if (!result.items.length) {
|
||||
this.products = [...this.products];
|
||||
} else {
|
||||
this.products = [...result.items];
|
||||
}
|
||||
} else {
|
||||
if ((this.page === 0 || result.skip === 0) && !this.isFullLoad) {
|
||||
this.products = [];
|
||||
}
|
||||
this.remissionHelper.setRemissionListHits(result.hits || -1);
|
||||
if (result.items && result.items.length > 0) {
|
||||
if (!this.isItRefetch && !this.isFullLoad) {
|
||||
this.products = this.filterUniqueProducts([
|
||||
...this.products,
|
||||
...result.items,
|
||||
]);
|
||||
} else if (this.isItRefetch && !this.isFullLoad) {
|
||||
this.products.splice(
|
||||
result.skip,
|
||||
result.items.length,
|
||||
...result.items
|
||||
);
|
||||
this.isItRefetch = false;
|
||||
} else if (this.isFullLoad) {
|
||||
this.products = this.filterUniqueProducts([
|
||||
...this.products,
|
||||
...result.items,
|
||||
]);
|
||||
this.isFullLoad = false;
|
||||
this.isFullListLoaded = true;
|
||||
this.cdr.detectChanges();
|
||||
this.fullListLoaded.emit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.cdr.detectChanges();
|
||||
});
|
||||
this.store.dispatch(new SetRemissionIsLoading(false));
|
||||
this.cdr.detectChanges();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public loadFullList() {
|
||||
@@ -257,9 +360,19 @@ export class RemissionListComponent implements OnInit, OnDestroy {
|
||||
this.isFullListLoaded = false;
|
||||
}
|
||||
|
||||
private updateFullListLoaded(params: { totalHits: number; numberOfProductsFetched: number }) {
|
||||
private updateFullListLoaded(params: {
|
||||
totalHits: number;
|
||||
numberOfProductsFetched: number;
|
||||
}) {
|
||||
if (this.page === 0) {
|
||||
return;
|
||||
}
|
||||
this.page = Math.floor(params.numberOfProductsFetched / this.pageSize);
|
||||
|
||||
const allProductsFetched = params.numberOfProductsFetched >= this.totalHits;
|
||||
allProductsFetched ? (this.isFullListLoaded = true) : (this.isFullListLoaded = false);
|
||||
allProductsFetched
|
||||
? (this.isFullListLoaded = true)
|
||||
: (this.isFullListLoaded = false);
|
||||
}
|
||||
|
||||
deleteProduct(product: RemissionProduct) {
|
||||
@@ -276,7 +389,9 @@ export class RemissionListComponent implements OnInit, OnDestroy {
|
||||
)
|
||||
.subscribe((result) => {
|
||||
if (result.result) {
|
||||
this.products = this.products.filter((existingProduct) => existingProduct.id !== product.id);
|
||||
this.products = this.products.filter(
|
||||
(existingProduct) => existingProduct.id !== product.id
|
||||
);
|
||||
this.hits = this.products.length;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
<div
|
||||
class="container"
|
||||
*ngIf="
|
||||
numberOfOpenShippingDocuments$ | async as numberOfOpenShippingDocuments
|
||||
"
|
||||
>
|
||||
<div class="container">
|
||||
<lib-icon class="icon" name="Icon_Warenbegleitschein_Red"></lib-icon>
|
||||
<app-button alignLeft="true" (action)="openOverviewPage()">
|
||||
<span
|
||||
>Warenbegleitscheine ({{ numberOfOpenShippingDocuments$ | async }})</span
|
||||
>Warenbegleitscheine
|
||||
<span
|
||||
*ngIf="
|
||||
numberOfOpenShippingDocuments$
|
||||
| async as numberOfOpenShippingDocuments
|
||||
"
|
||||
>({{ numberOfOpenShippingDocuments }})</span
|
||||
></span
|
||||
>
|
||||
</app-button>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
// start:ng42.barrel
|
||||
export * from './remission-required-capacities-widget.component';
|
||||
export * from './remission-required-capacities-widget.module';
|
||||
// end:ng42.barrel
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
<div class="progress-wrapper" *ngIf="capacities">
|
||||
<ng-container *ngFor="let item of capacities">
|
||||
<div>
|
||||
<lib-progress-bar
|
||||
[progress]="item.utilized / item.available"
|
||||
></lib-progress-bar>
|
||||
</div>
|
||||
<div>
|
||||
<span
|
||||
>{{ item.name }}: {{ item.utilized }} von {{ item.available }}
|
||||
{{ item.label }}</span
|
||||
>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<div class="note" *ngIf="showOverflowMessage">
|
||||
<span class="align-center"
|
||||
>Wählen Sie die Abteilung aus, die Sie remittieren möchten.</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,15 @@
|
||||
import { Component, ChangeDetectionStrategy, Input } from '@angular/core';
|
||||
import { CapacityType } from '@isa/remission';
|
||||
|
||||
@Component({
|
||||
selector: 'app-remission-required-capacities-widget',
|
||||
templateUrl: './remission-required-capacities-widget.component.html',
|
||||
styleUrls: ['./remission-required-capacities-widget.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class RemissionRequiredCapacitiesWidgetComponent {
|
||||
@Input() capacities: CapacityType[];
|
||||
@Input() showOverflowMessage: boolean;
|
||||
|
||||
constructor() {}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RemissionRequiredCapacitiesWidgetComponent } from './remission-required-capacities-widget.component';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ProgressBarModule } from '@libs/ui';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, ProgressBarModule],
|
||||
exports: [RemissionRequiredCapacitiesWidgetComponent],
|
||||
declarations: [RemissionRequiredCapacitiesWidgetComponent],
|
||||
providers: [],
|
||||
})
|
||||
export class RemissionRequiredCapacitiesWidgetModule {}
|
||||
@@ -0,0 +1,4 @@
|
||||
// start:ng42.barrel
|
||||
export * from './remission-scroll-button.component';
|
||||
// end:ng42.barrel
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<div class="button" [class.reverse]="iconPosition === 'right'">
|
||||
<div class="icon">
|
||||
<ng-content select="lib-icon"></ng-content>
|
||||
</div>
|
||||
<div class="text">
|
||||
<span>{{ text }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,34 @@
|
||||
.button {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: white;
|
||||
-moz-box-shadow: 0 0 30px #596470;
|
||||
-webkit-box-shadow: 0 0 30px #596470;
|
||||
box-shadow: 0 0 30px rgba(89, 100, 112, 0.5);
|
||||
border-radius: 50px;
|
||||
padding: 15px 20px;
|
||||
cursor: pointer;
|
||||
|
||||
&.reverse {
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
|
||||
.icon {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
margin-top: 3px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
span {
|
||||
font-family: 'Open Sans';
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #000000;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Component, ChangeDetectionStrategy, Input } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-remission-scroll-button',
|
||||
templateUrl: './remission-scroll-button.component.html',
|
||||
styleUrls: ['./remission-scroll-button.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class RemissionScrollButtonComponent {
|
||||
@Input() text: string;
|
||||
@Input() iconPosition: 'left' | 'right' = 'left';
|
||||
|
||||
constructor() {}
|
||||
}
|
||||
@@ -44,6 +44,7 @@ export class RemissionShippingDocumentCardComponent {
|
||||
.then(() => {
|
||||
this.remissionHelper.addRemissionListItem(this.product);
|
||||
this.updateShippingDocument.emit();
|
||||
this.remissionHelper.requestRefetchProducts();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,8 @@
|
||||
></app-remission-shipping-document-card>
|
||||
<hr class="branch" *ngIf="!last" />
|
||||
</ng-container>
|
||||
|
||||
<ng-content class="bottom"></ng-content>
|
||||
</div>
|
||||
<div class="empty-list" *ngIf="shippingDocument.products.length === 0">
|
||||
<span>Neue Artikel aus der Remi-Liste hinzufügen</span>
|
||||
|
||||
@@ -1,20 +1,14 @@
|
||||
<div @fadeIn class="top" *ngIf="showTop$ | async">
|
||||
<div class="button" (click)="scrollBottom()">
|
||||
<div class="icon">
|
||||
<lib-icon name="Hover_Arrow_Down"></lib-icon>
|
||||
</div>
|
||||
<div class="text">
|
||||
<span>Zum Warenbegleitschein</span>
|
||||
</div>
|
||||
</div>
|
||||
<app-remission-scroll-button
|
||||
text="Zum Warenbegleitschein"
|
||||
iconPosition="right"
|
||||
(click)="scrolltoWarenbegleitschein()"
|
||||
>
|
||||
<lib-icon name="Hover_Arrow_Right"></lib-icon
|
||||
></app-remission-scroll-button>
|
||||
</div>
|
||||
<div @fadeIn class="bottom" *ngIf="showBottom$ | async">
|
||||
<div class="button" (click)="scrollTop()">
|
||||
<div class="icon">
|
||||
<lib-icon name="Hover_Arrow_Up"></lib-icon>
|
||||
</div>
|
||||
<div class="text">
|
||||
<span>Nach oben</span>
|
||||
</div>
|
||||
</div>
|
||||
<app-remission-scroll-button text="Nach oben" (click)="scrollTop()">
|
||||
<lib-icon name="Hover_Arrow_Up"></lib-icon
|
||||
></app-remission-scroll-button>
|
||||
</div>
|
||||
|
||||
@@ -17,28 +17,3 @@
|
||||
top: 200px;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.button {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
background-color: white;
|
||||
-moz-box-shadow: 0 0 30px #596470;
|
||||
-webkit-box-shadow: 0 0 30px #596470;
|
||||
box-shadow: 0 0 30px rgba(89, 100, 112, 0.5);
|
||||
border-radius: 50px;
|
||||
padding: 15px 20px;
|
||||
cursor: pointer;
|
||||
|
||||
.icon {
|
||||
margin-top: 3px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
span {
|
||||
font-family: 'Open Sans';
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #000000;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import { fadeInAnimation } from '../../animations/fadeIn.animation';
|
||||
})
|
||||
export class RemissionToTopToBottomActionsComponent {
|
||||
@Output() navigateToTop = new EventEmitter();
|
||||
@Output() navigateToBottom = new EventEmitter();
|
||||
@Output() navigateToWarenbegleitschein = new EventEmitter();
|
||||
|
||||
@Input() showTop$: BehaviorSubject<boolean>;
|
||||
@Input() showBottom$: BehaviorSubject<boolean>;
|
||||
@@ -21,7 +21,7 @@ export class RemissionToTopToBottomActionsComponent {
|
||||
this.navigateToTop.emit();
|
||||
}
|
||||
|
||||
scrollBottom() {
|
||||
this.navigateToBottom.emit();
|
||||
scrolltoWarenbegleitschein() {
|
||||
this.navigateToWarenbegleitschein.emit();
|
||||
}
|
||||
}
|
||||
|
||||
9
apps/sales/src/app/modules/remission/models/index.ts
Normal file
9
apps/sales/src/app/modules/remission/models/index.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
// start:ng42.barrel
|
||||
export * from './capacity-type-client-wrapper.model';
|
||||
export * from './remission-active-view.model';
|
||||
export * from './remission-finishing-process-status.enum';
|
||||
export * from './remission-resource-type.model';
|
||||
export * from './resource-type-specific-model.model';
|
||||
export * from './update-filter.model';
|
||||
// end:ng42.barrel
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
export enum RemissionActiveView {
|
||||
PRODUCTS = 'Products',
|
||||
WBS = 'Warenbegleitschein',
|
||||
}
|
||||
@@ -5,4 +5,4 @@ import { RemissionListFilterModule } from './remission-list-filter/remission-lis
|
||||
imports: [RemissionListFilterModule],
|
||||
exports: [RemissionListFilterModule],
|
||||
})
|
||||
export class RemissionOverlaysModule {}
|
||||
export class RemissionOverlaysModule { }
|
||||
|
||||
@@ -1,12 +1,94 @@
|
||||
import { trigger, transition, style, animate } from '@angular/animations';
|
||||
import {
|
||||
trigger,
|
||||
transition,
|
||||
style,
|
||||
animate,
|
||||
keyframes,
|
||||
} from '@angular/animations';
|
||||
|
||||
export const topRightInOut = trigger('topRightInOut', [
|
||||
transition(':enter', [
|
||||
style({ width: '0', height: '0', marginLeft: 'auto', overflow: 'hidden', opacity: '0', borderRadius: '100% 0 100% 100%' }),
|
||||
animate(300, style({ width: '100%', height: '100%', opacity: '1', borderRadius: '0%' })),
|
||||
style({
|
||||
width: '0',
|
||||
height: '0',
|
||||
marginLeft: 'auto',
|
||||
overflow: 'hidden',
|
||||
opacity: '0',
|
||||
}),
|
||||
animate(
|
||||
300,
|
||||
style({ width: '100%', height: '100%', opacity: '1', borderRadius: '0%' })
|
||||
),
|
||||
]),
|
||||
transition('* => out', [
|
||||
style({ width: '100%', height: '100%', marginLeft: 'auto', opacity: '1', borderRadius: '0%', overflow: 'hidden' }),
|
||||
animate(300, style({ width: '0', height: '0', overflow: 'hidden', opacity: '0', borderRadius: '100% 0 100% 100%' })),
|
||||
style({
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
marginLeft: 'auto',
|
||||
opacity: '1',
|
||||
borderRadius: '0%',
|
||||
overflow: 'hidden',
|
||||
}),
|
||||
animate(
|
||||
300,
|
||||
style({
|
||||
width: '0',
|
||||
height: '0',
|
||||
overflow: 'hidden',
|
||||
opacity: '0',
|
||||
})
|
||||
),
|
||||
]),
|
||||
]);
|
||||
|
||||
export const slideIn = trigger('slideIn', [
|
||||
transition(':enter', [
|
||||
style({
|
||||
transform: 'translateX(100%)',
|
||||
marginLeft: 'auto',
|
||||
opacity: '0',
|
||||
height: '100%',
|
||||
}),
|
||||
animate(
|
||||
430,
|
||||
keyframes([
|
||||
style({
|
||||
opacity: '1',
|
||||
transform: 'translateX(65%)',
|
||||
offset: 0.35,
|
||||
}),
|
||||
style({
|
||||
transform: 'translateX(0)',
|
||||
height: '100%',
|
||||
opacity: '1',
|
||||
offset: 1,
|
||||
}),
|
||||
])
|
||||
),
|
||||
]),
|
||||
transition('* => out', [
|
||||
style({
|
||||
transform: 'translateX(0)',
|
||||
height: '100%',
|
||||
opacity: '1',
|
||||
marginLeft: 'auto',
|
||||
}),
|
||||
animate(
|
||||
430,
|
||||
keyframes([
|
||||
style({
|
||||
opacity: '1',
|
||||
transform: 'translateX(65%)',
|
||||
offset: 0.65,
|
||||
}),
|
||||
style({
|
||||
width: '0',
|
||||
height: '100%',
|
||||
transform: 'translateX(100%)',
|
||||
opacity: '0',
|
||||
offset: 1,
|
||||
}),
|
||||
])
|
||||
),
|
||||
]),
|
||||
]);
|
||||
|
||||
@@ -1,46 +1,53 @@
|
||||
<div
|
||||
class="bg-branch"
|
||||
[@topRightInOut]="animate"
|
||||
(@topRightInOut.done)="closeAnimationDone()"
|
||||
(headerClicked)="close()"
|
||||
[@slideIn]="animate"
|
||||
(@slideIn.done)="closeAnimationDone()"
|
||||
>
|
||||
<div class="filter-container">
|
||||
<button type="button" class="btn-close" (click)="close()">
|
||||
<lib-icon
|
||||
height="21px"
|
||||
class="close-icon"
|
||||
name="close"
|
||||
name="close-branch"
|
||||
alt="close"
|
||||
></lib-icon>
|
||||
</button>
|
||||
<h2 class="filter-title">Filter</h2>
|
||||
|
||||
<app-remission-uberlauf-capacities
|
||||
*ngIf="(remissionSource$ | async) === 'ueberlauf'"
|
||||
>
|
||||
</app-remission-uberlauf-capacities>
|
||||
|
||||
<div class="selection-container" *ngIf="filtersByGroup$ | async as filters">
|
||||
<app-selected-filter-options
|
||||
[value]="filters"
|
||||
(filterChanged)="updateFilter($event)"
|
||||
<div>
|
||||
<app-remission-required-capacities-widget
|
||||
*ngIf="(remissionSource$ | async) === 'ueberlauf'"
|
||||
[showOverflowMessage]="showOverflowInitialMessage$ | async"
|
||||
[capacities]="capacities$ | async"
|
||||
>
|
||||
</app-selected-filter-options>
|
||||
|
||||
<app-filter-group
|
||||
*ngIf="filters"
|
||||
[value]="filters"
|
||||
[selected]="selectedFilter$ | async"
|
||||
(valueChanged)="onFiltersChange($event)"
|
||||
(valueGroupChanged)="updateLastGroupChanged($event)"
|
||||
></app-filter-group>
|
||||
</app-remission-required-capacities-widget>
|
||||
</div>
|
||||
<ng-container *ngIf="filtersByGroup$ | async as filters">
|
||||
<div>
|
||||
<app-selected-filter-options
|
||||
[value]="filters"
|
||||
(filterChanged)="onFiltersChange($event)"
|
||||
>
|
||||
</app-selected-filter-options>
|
||||
</div>
|
||||
<div class="selection-container" #selectionContainer>
|
||||
<app-filter-group
|
||||
*ngIf="filters"
|
||||
[value]="filters"
|
||||
[selected]="selectedFilter$ | async"
|
||||
(valueChanged)="onFiltersChange($event)"
|
||||
(valueGroupChanged)="updateLastGroupChanged($event)"
|
||||
>
|
||||
</app-filter-group>
|
||||
</div>
|
||||
</ng-container>
|
||||
<div class="apply-filter-wrapper">
|
||||
<button
|
||||
class="isa-btn isa-btn-primary isa-btn-pill btn-apply-filters text-nowrap"
|
||||
(click)="applyFilters()"
|
||||
>
|
||||
Filter anwenden
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
@fadeIn
|
||||
*ngIf="pendingFilters$ | async"
|
||||
class="isa-btn isa-btn-primary isa-btn-medium isa-btn-pill btn-apply-filters"
|
||||
(click)="applyFilters()"
|
||||
>
|
||||
Filter anwenden
|
||||
</button>
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
@import 'variables';
|
||||
@import 'mixins/media';
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
|
||||
.bg-branch {
|
||||
height: 100%;
|
||||
@@ -13,12 +23,21 @@
|
||||
}
|
||||
}
|
||||
|
||||
.selection-container {
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
.filter-container {
|
||||
height: 95%;
|
||||
max-height: 95%;
|
||||
position: relative;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: $max-content-width;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
display: grid;
|
||||
grid-template-rows: auto auto 1fr auto;
|
||||
grid-template-rows: auto auto auto 1fr;
|
||||
}
|
||||
|
||||
.btn-close {
|
||||
@@ -28,6 +47,7 @@
|
||||
background: none;
|
||||
right: 20px;
|
||||
top: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.filter-title {
|
||||
@@ -35,14 +55,30 @@
|
||||
margin-top: 25px;
|
||||
}
|
||||
|
||||
.apply-filter-wrapper {
|
||||
text-align: center;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.btn-apply-filters {
|
||||
position: sticky;
|
||||
left: 50%;
|
||||
bottom: 30px;
|
||||
transform: translateX(-50%);
|
||||
padding: 18px 27px 20px 27px;
|
||||
border-radius: 57px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.text-nowrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
:host ::ng-deep .select-filter-options-content {
|
||||
max-height: 400px;
|
||||
overflow: scroll;
|
||||
|
||||
@include mq-tablet-landscape() {
|
||||
max-height: 200px;
|
||||
}
|
||||
|
||||
@include mq-desktop() {
|
||||
max-height: 300px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,51 +1,129 @@
|
||||
import { Component, ChangeDetectionStrategy, OnInit } from '@angular/core';
|
||||
import {
|
||||
Component,
|
||||
ChangeDetectionStrategy,
|
||||
OnInit,
|
||||
ViewChild,
|
||||
ElementRef,
|
||||
OnDestroy,
|
||||
Renderer2,
|
||||
ChangeDetectorRef,
|
||||
} from '@angular/core';
|
||||
import { IsaOverlayRef } from 'apps/sales/src/app/core/overlay/isa-overlay-ref';
|
||||
import { Select } from '@ngxs/store';
|
||||
import { RemissionSelectors } from 'apps/sales/src/app/core/store/selectors/remission.selectors';
|
||||
import { Observable, BehaviorSubject } from 'rxjs';
|
||||
import { Observable, BehaviorSubject, interval } from 'rxjs';
|
||||
import {
|
||||
RemissionProcess,
|
||||
RemissionSelectedFilters,
|
||||
RemissionService,
|
||||
CapacityType,
|
||||
} from '@isa/remission';
|
||||
import { RemissionFilterService } from '../../services/remission-filter.service';
|
||||
import { fadeInAnimation } from '../../animations/fadeIn.animation';
|
||||
import { Filter, SelectFilter } from '../../../filter';
|
||||
import { Filter, SelectFilter, SelectFilterOption } from '../../../filter';
|
||||
import { RemissionResourceType } from '../../models/remission-resource-type.model';
|
||||
import { trigger, transition, style, animate } from '@angular/animations';
|
||||
import { topRightInOut } from './remission-list-filter.animations';
|
||||
import { tap } from 'rxjs/operators';
|
||||
import { slideIn } from './remission-list-filter.animations';
|
||||
import {
|
||||
withLatestFrom,
|
||||
filter,
|
||||
switchMap,
|
||||
map,
|
||||
throttleTime,
|
||||
startWith,
|
||||
} from 'rxjs/operators';
|
||||
import { isNullOrUndefined } from 'util';
|
||||
|
||||
@Component({
|
||||
selector: 'app-remission-list-filter',
|
||||
templateUrl: 'remission-list-filter.component.html',
|
||||
styleUrls: ['remission-list-filter.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
animations: [fadeInAnimation, topRightInOut],
|
||||
animations: [slideIn],
|
||||
})
|
||||
export class RemissionListFilterComponent implements OnInit {
|
||||
export class RemissionListFilterComponent implements OnInit, OnDestroy {
|
||||
animate = 'in';
|
||||
|
||||
@ViewChild('selectionContainer', { static: false })
|
||||
selectionContainer: ElementRef;
|
||||
|
||||
checkHeightTimerSub = interval(100)
|
||||
.pipe(startWith(0), throttleTime(250))
|
||||
.subscribe(() => this.updateHeight());
|
||||
|
||||
@Select(RemissionSelectors.getRemissionProcess)
|
||||
remissionProcess$: Observable<RemissionProcess>;
|
||||
@Select(RemissionSelectors.getRemissionSource)
|
||||
remissionSource$: Observable<RemissionResourceType>;
|
||||
@Select(RemissionSelectors.getRemissionFilters)
|
||||
remissionFilter$: Observable<RemissionSelectedFilters>;
|
||||
@Select(RemissionSelectors.getRemissionSupplierId)
|
||||
remissionSupplierId$: Observable<number>;
|
||||
@Select(RemissionSelectors.getRemissionActiveFilters)
|
||||
activeFilters$: Observable<{
|
||||
[filterId: string]: string[];
|
||||
}>;
|
||||
|
||||
filter$: Observable<Filter[]>;
|
||||
filtersByGroup$: Observable<Filter[]>;
|
||||
pendingFilters$ = new BehaviorSubject<Filter[]>(null);
|
||||
selectedFilter$: Observable<Filter>;
|
||||
capacities$: Observable<CapacityType[]>;
|
||||
showOverflowInitialMessage$: Observable<boolean>;
|
||||
|
||||
constructor(
|
||||
public overlayRef: IsaOverlayRef,
|
||||
private filterService: RemissionFilterService
|
||||
private filterService: RemissionFilterService,
|
||||
private remissionService: RemissionService,
|
||||
private renderer: Renderer2,
|
||||
private cdr: ChangeDetectorRef
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.getAvailableFilters();
|
||||
this.getSelectedFilter();
|
||||
this.getPendingFilters();
|
||||
|
||||
this.getRequiredCapacities();
|
||||
this.initShowOverflowMessage();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.checkHeightTimerSub.unsubscribe();
|
||||
}
|
||||
|
||||
updateHeight() {
|
||||
if (this.selectionContainer) {
|
||||
const element: HTMLElement = this.selectionContainer.nativeElement;
|
||||
const selectFilterElement: HTMLElement = element.querySelector(
|
||||
'app-select-filter'
|
||||
);
|
||||
|
||||
if (isNullOrUndefined(selectFilterElement)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const selectFilterOptionsContent: HTMLElement = selectFilterElement.querySelector(
|
||||
'.select-filter-options-content'
|
||||
);
|
||||
|
||||
if (isNullOrUndefined(selectFilterOptionsContent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const height =
|
||||
element.clientHeight -
|
||||
(selectFilterElement.clientHeight -
|
||||
selectFilterOptionsContent.clientHeight);
|
||||
|
||||
if (selectFilterOptionsContent.offsetHeight !== height) {
|
||||
this.renderer.setStyle(
|
||||
selectFilterOptionsContent,
|
||||
'max-height',
|
||||
`${height}px`
|
||||
);
|
||||
|
||||
this.cdr.markForCheck();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAvailableFilters() {
|
||||
@@ -57,6 +135,60 @@ export class RemissionListFilterComponent implements OnInit {
|
||||
this.selectedFilter$ = this.filterService.lastFilterGroupChanged$;
|
||||
}
|
||||
|
||||
getPendingFilters() {
|
||||
this.pendingFilters$ = this.filterService.pendingFilters$;
|
||||
}
|
||||
|
||||
getRequiredCapacities() {
|
||||
this.capacities$ = this.pendingFilters$.pipe(
|
||||
withLatestFrom(this.remissionSource$),
|
||||
filter(([_, source]) => source === 'ueberlauf'),
|
||||
map(([pendingFilters]) =>
|
||||
this.filterService.mapToRemissionFilter(pendingFilters || [])
|
||||
),
|
||||
withLatestFrom(this.remissionSupplierId$),
|
||||
switchMap(([selectedFilters, supplierId]) =>
|
||||
this.remissionService.getCapacities({ selectedFilters, supplierId })
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
initShowOverflowMessage() {
|
||||
this.showOverflowInitialMessage$ = this.pendingFilters$.pipe(
|
||||
map((pendingFilters) => [
|
||||
pendingFilters,
|
||||
this.filterService.flattenPendingFilters(pendingFilters),
|
||||
]),
|
||||
map(
|
||||
([filters, flattenedFilters]: [
|
||||
SelectFilterOption[],
|
||||
SelectFilterOption[]
|
||||
]) => {
|
||||
if (
|
||||
isNullOrUndefined(flattenedFilters) ||
|
||||
isNullOrUndefined(filters)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const departmentFilters = filters.find((f) => f.id === 'abteilungen');
|
||||
|
||||
if (!departmentFilters || !departmentFilters.options) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const selectedFilters = flattenedFilters.filter((f) => !!f.selected);
|
||||
|
||||
const result = !selectedFilters.some(
|
||||
(f) => !!departmentFilters.options.find((opt) => opt.id === f.id)
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
updateFilter(updatedFilters: SelectFilter[]) {
|
||||
this.filterService.updateFilters(updatedFilters);
|
||||
}
|
||||
@@ -66,7 +198,9 @@ export class RemissionListFilterComponent implements OnInit {
|
||||
}
|
||||
|
||||
applyFilters() {
|
||||
this.filterService.updateFilters(this.pendingFilters$.value);
|
||||
if (this.pendingFilters$.value) {
|
||||
this.filterService.updateFilters(this.pendingFilters$.value);
|
||||
}
|
||||
this.close();
|
||||
}
|
||||
|
||||
|
||||
@@ -7,15 +7,19 @@ import { SelectedFilterOptionsModule } from '../../../filter';
|
||||
import { FilterGroupModule } from '../../../filter/filter-group/filter-group.module';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { RemissionUeberlaufCapacitiesModule } from '../../pages/remission-list-create/ueberlauf-capacities';
|
||||
import { SharedModule } from 'apps/sales/src/app/shared/shared.module';
|
||||
import { RemissionRequiredCapacitiesWidgetModule } from '../../components/remission-required-capacities-widget';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
SharedModule,
|
||||
IconModule,
|
||||
SelectedFilterOptionsModule,
|
||||
FilterGroupModule,
|
||||
BrowserAnimationsModule,
|
||||
RemissionUeberlaufCapacitiesModule
|
||||
RemissionUeberlaufCapacitiesModule,
|
||||
RemissionRequiredCapacitiesWidgetModule,
|
||||
],
|
||||
exports: [RemissionListFilterComponent],
|
||||
declarations: [RemissionListFilterComponent],
|
||||
|
||||
@@ -1,35 +1,41 @@
|
||||
<div
|
||||
class="list"
|
||||
*ngIf="{
|
||||
productsData: remissionProductsData$ | async,
|
||||
remissionProcess: remissionProcess$ | async,
|
||||
status: remissionProcessStatuses$ | async,
|
||||
isLoading: isLoading$ | async
|
||||
} as data"
|
||||
class="wrapper"
|
||||
[class.slide-left]="_slideOutOfView"
|
||||
[class.remove-from-flow]="_removeFromDocumentFlow"
|
||||
>
|
||||
<ng-container *ngIf="data.productsData && data.productsData.hits">
|
||||
<div #remissionListHitsRef class="hits" *ngIf="!data.isLoading">
|
||||
<span>{{ data.productsData.hits }} Titel</span>
|
||||
</div>
|
||||
<div
|
||||
class="list"
|
||||
*ngIf="{
|
||||
productsData: remissionProductsData$ | async,
|
||||
remissionProcess: remissionProcess$ | async,
|
||||
status: remissionProcessStatuses$ | async,
|
||||
isLoading: isLoading$ | async
|
||||
} as data"
|
||||
>
|
||||
<ng-container *ngIf="data.productsData && data.productsData.hits">
|
||||
<div #remissionListHitsRef class="hits" *ngIf="!data.isLoading">
|
||||
<span>{{ data.productsData.hits }} Titel</span>
|
||||
</div>
|
||||
|
||||
<div class="ng-container" *ngIf="data.remissionProcess && data.status">
|
||||
<app-remission-list
|
||||
[started]="data.status.started"
|
||||
[remissionProcess]="data.remissionProcess"
|
||||
[hits]="data.productsData.hits"
|
||||
[isLoading$]="isLoading$"
|
||||
[refetchProducts$]="refetchRemissionProductsOnScrollUp$"
|
||||
[fetchLastProducts$]="fetchLastProducts$"
|
||||
(updateShippingDocument)="updateShippingDocument()"
|
||||
#remissionList
|
||||
></app-remission-list>
|
||||
<app-remission-to-top-to-bottom-actions
|
||||
(navigateToTop)="navigateToTop()"
|
||||
(navigateToBottom)="navigateToBottom()"
|
||||
[showTop$]="showJumpToTop$"
|
||||
[showBottom$]="showJumpToBottom$"
|
||||
#toTopToBottomActions
|
||||
></app-remission-to-top-to-bottom-actions>
|
||||
</div>
|
||||
</ng-container>
|
||||
<div class="ng-container" *ngIf="data.remissionProcess && data.status">
|
||||
<app-remission-list
|
||||
[started]="data.status.started"
|
||||
[remissionProcess]="data.remissionProcess"
|
||||
[hits]="data.productsData.hits"
|
||||
[isLoading$]="isLoading$"
|
||||
[refetchProducts$]="refetchRemissionProductsOnScrollUp$"
|
||||
[fetchLastProducts$]="fetchLastProducts$"
|
||||
(updateShippingDocument)="updateShippingDocument()"
|
||||
#remissionList
|
||||
></app-remission-list>
|
||||
<app-remission-to-top-to-bottom-actions
|
||||
(navigateToTop)="navigateToTop()"
|
||||
(navigateToWarenbegleitschein)="navigateToWarenbegleitschein()"
|
||||
[showTop$]="showJumpToTop$"
|
||||
[showBottom$]="showJumpToBottom$"
|
||||
#toTopToBottomActions
|
||||
></app-remission-to-top-to-bottom-actions>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -9,5 +9,28 @@
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
.ng-container {
|
||||
margin-bottom: 125px;
|
||||
}
|
||||
|
||||
margin-top: 8px;
|
||||
margin-bottom: 16px;
|
||||
transition: all 1s ease-in-out;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
margin-bottom: 16px;
|
||||
transition: all 1s ease-in-out;
|
||||
|
||||
&.slide-left {
|
||||
position: relative;
|
||||
transform: translateX(-100vw);
|
||||
transition: all 1.5s ease-in-out;
|
||||
|
||||
&.remove-from-flow {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
transition: all 0.6s ease-in-out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,18 +9,46 @@ import {
|
||||
QueryList,
|
||||
ElementRef,
|
||||
AfterViewInit,
|
||||
ChangeDetectorRef,
|
||||
} from '@angular/core';
|
||||
import { Observable, of, Subject, BehaviorSubject, combineLatest, merge } from 'rxjs';
|
||||
import { RemissionProduct, RemissionService, RemissionProcess } from '@isa/remission';
|
||||
import {
|
||||
Observable,
|
||||
of,
|
||||
Subject,
|
||||
BehaviorSubject,
|
||||
combineLatest,
|
||||
merge,
|
||||
} from 'rxjs';
|
||||
import {
|
||||
RemissionProduct,
|
||||
RemissionService,
|
||||
RemissionProcess,
|
||||
} from '@isa/remission';
|
||||
import { Store, Select } from '@ngxs/store';
|
||||
import { RemissionSelectors } from 'apps/sales/src/app/core/store/selectors/remission.selectors';
|
||||
import { map, switchMap, filter, catchError, takeUntil, take, distinctUntilChanged, debounceTime, tap, skip } from 'rxjs/operators';
|
||||
import {
|
||||
map,
|
||||
switchMap,
|
||||
filter,
|
||||
catchError,
|
||||
takeUntil,
|
||||
take,
|
||||
distinctUntilChanged,
|
||||
debounceTime,
|
||||
tap,
|
||||
skip,
|
||||
} from 'rxjs/operators';
|
||||
import { RemissionListComponent } from '../../../components/remission-list';
|
||||
import { RemissionResourceType } from '../../../models/remission-resource-type.model';
|
||||
import { isNullOrUndefined } from 'util';
|
||||
import { RemissionProcessStatuses } from 'apps/sales/src/app/core/models/remission-process-statuses.model';
|
||||
import { RemissionToTopToBottomActionsComponent } from '../../../components/remission-to-top-to-bottom-actions';
|
||||
import { UpdateShippingDocuent } from 'apps/sales/src/app/core/store/actions/remission.actions';
|
||||
import {
|
||||
UpdateShippingDocuent,
|
||||
SetRemissionActiveView,
|
||||
} from 'apps/sales/src/app/core/store/actions/remission.actions';
|
||||
import { RemissionActiveView } from '../../../models';
|
||||
import { RemissionHelperService } from '../../../services';
|
||||
|
||||
@Component({
|
||||
selector: 'app-remission-product-list',
|
||||
@@ -28,26 +56,50 @@ import { UpdateShippingDocuent } from 'apps/sales/src/app/core/store/actions/rem
|
||||
styleUrls: ['./product-list.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class RemissionProductListComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
export class RemissionProductListComponent
|
||||
implements OnInit, OnDestroy, AfterViewInit {
|
||||
@ViewChild('toTopToBottomActions', { static: false })
|
||||
toTopToBottomActions: RemissionToTopToBottomActionsComponent;
|
||||
@ViewChild('remissionList', { static: false }) remissionList: RemissionListComponent;
|
||||
@ViewChild('remissionList', { static: false })
|
||||
remissionList: RemissionListComponent;
|
||||
@ViewChildren('remissionListHitsRef') remissionHitsRef: QueryList<ElementRef>;
|
||||
|
||||
@Input() remissionListContainer: HTMLElement;
|
||||
@Input() shippingDocumentContainer: HTMLElement;
|
||||
@Input() containerScrolled$: Observable<Event> = new Subject<Event>().asObservable();
|
||||
@Input() containerScrolled$: Observable<Event> = new Subject<
|
||||
Event
|
||||
>().asObservable();
|
||||
@Input() shippingDocumentRef: QueryList<ElementRef>;
|
||||
@Input() set slideOutOfView(shouldBeOutOfView: boolean) {
|
||||
if (!shouldBeOutOfView) {
|
||||
this._removeFromDocumentFlow = false;
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
this._removeFromDocumentFlow = true;
|
||||
this.cdr.detectChanges();
|
||||
}, 400);
|
||||
}
|
||||
|
||||
this._slideOutOfView = shouldBeOutOfView;
|
||||
}
|
||||
_slideOutOfView: boolean;
|
||||
_removeFromDocumentFlow = false;
|
||||
|
||||
@Select(RemissionSelectors.getIsLoading) isLoading$: Observable<boolean>;
|
||||
@Select(RemissionSelectors.getRemissionProcess) remissionProcess$: Observable<RemissionProcess>;
|
||||
@Select(RemissionSelectors.getRemissionSource) source$: Observable<RemissionResourceType>;
|
||||
@Select(RemissionSelectors.getRemissionProcess) remissionProcess$: Observable<
|
||||
RemissionProcess
|
||||
>;
|
||||
@Select(RemissionSelectors.getRemissionSource) source$: Observable<
|
||||
RemissionResourceType
|
||||
>;
|
||||
@Select(RemissionSelectors.getRemissionProcessStatuses)
|
||||
remissionProcessStatuses$: Observable<RemissionProcessStatuses>;
|
||||
@Select(RemissionSelectors.getRemissiontarget)
|
||||
remissionTarget$: Observable<string>;
|
||||
@Select(RemissionSelectors.getRemissionSource)
|
||||
remissionSource$: Observable<RemissionResourceType>;
|
||||
@Select(RemissionSelectors.getActiveView)
|
||||
activeView$: Observable<RemissionActiveView>;
|
||||
|
||||
refetchRemissionProductsOnScrollUp$ = new Subject<void>();
|
||||
destroy$ = new Subject<void>();
|
||||
@@ -86,11 +138,17 @@ export class RemissionProductListComponent implements OnInit, OnDestroy, AfterVi
|
||||
// Feature Flag: Indicates whether to load entire remission list on navigate to bottom
|
||||
loadAllProductsOnNavigateToBottom = false;
|
||||
|
||||
constructor(private store: Store, private remissionService: RemissionService) { }
|
||||
constructor(
|
||||
private store: Store,
|
||||
private remissionService: RemissionService,
|
||||
private remissionHelperService: RemissionHelperService,
|
||||
private cdr: ChangeDetectorRef
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.initProducts();
|
||||
this.initScrollHandler();
|
||||
this.initScrollRestoration();
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
@@ -104,9 +162,20 @@ export class RemissionProductListComponent implements OnInit, OnDestroy, AfterVi
|
||||
this.destroy$.complete();
|
||||
}
|
||||
|
||||
initScrollRestoration() {
|
||||
this.activeView$.pipe(takeUntil(this.destroy$)).subscribe((activeView) => {
|
||||
if (activeView === 'Warenbegleitschein') {
|
||||
this.resetRemissionListScrollPosition(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
initProducts() {
|
||||
this.remissionProductsData$ = this.remissionProcess$.pipe(
|
||||
filter((process) => !isNullOrUndefined(process) && !isNullOrUndefined(process.id)),
|
||||
filter(
|
||||
(process) =>
|
||||
!isNullOrUndefined(process) && !isNullOrUndefined(process.id)
|
||||
),
|
||||
take(1),
|
||||
map((process) => process.id),
|
||||
switchMap((remissionProcessId) =>
|
||||
@@ -114,7 +183,10 @@ export class RemissionProductListComponent implements OnInit, OnDestroy, AfterVi
|
||||
filter((data) => !isNullOrUndefined(data)),
|
||||
map((data) => {
|
||||
if (!data.hits) {
|
||||
data.hits = (this.latestRemissionProductsData$.value && this.latestRemissionProductsData$.value.hits) || 0;
|
||||
data.hits =
|
||||
(this.latestRemissionProductsData$.value &&
|
||||
this.latestRemissionProductsData$.value.hits) ||
|
||||
0;
|
||||
}
|
||||
|
||||
return data;
|
||||
@@ -154,7 +226,10 @@ export class RemissionProductListComponent implements OnInit, OnDestroy, AfterVi
|
||||
map((process) => process.id),
|
||||
take(1)
|
||||
)
|
||||
.subscribe((remissionProcessId) => this.store.dispatch(new UpdateShippingDocuent(remissionProcessId)));
|
||||
.subscribe((remissionProcessId) => {
|
||||
this.remissionHelperService.requestRefetchProducts();
|
||||
this.store.dispatch(new UpdateShippingDocuent(remissionProcessId));
|
||||
});
|
||||
}
|
||||
|
||||
navigateToTop() {
|
||||
@@ -164,38 +239,16 @@ export class RemissionProductListComponent implements OnInit, OnDestroy, AfterVi
|
||||
});
|
||||
}
|
||||
|
||||
navigateToBottom() {
|
||||
this.remissionList.isFullLoad = true;
|
||||
|
||||
if (this.loadAllProductsOnNavigateToBottom) {
|
||||
return this.remissionList.loadFullList().subscribe();
|
||||
}
|
||||
|
||||
this.navigateToWarenbegleitschein();
|
||||
}
|
||||
|
||||
private navigateToWarenbegleitschein() {
|
||||
this.fetchLastProducts$.next();
|
||||
|
||||
setTimeout(
|
||||
() =>
|
||||
this.shippingDocumentContainer.scrollIntoView({
|
||||
block: 'end',
|
||||
behavior: 'smooth',
|
||||
}),
|
||||
1000
|
||||
);
|
||||
setTimeout(() => {
|
||||
this.viewShippingDocumentFromJumpToBottom = true;
|
||||
this.remissionList.isFullLoad = false;
|
||||
}, 4000);
|
||||
navigateToWarenbegleitschein() {
|
||||
this.store.dispatch(new SetRemissionActiveView(RemissionActiveView.WBS));
|
||||
}
|
||||
|
||||
scrollHandler(target: HTMLElement) {
|
||||
const { clientHeight, scrollHeight, scrollTop } = target;
|
||||
|
||||
if (this.refetchProductsOnScroll()) {
|
||||
const shippingContainerHeight = this.shippingDocumentContainer.clientHeight;
|
||||
const shippingContainerHeight = this.shippingDocumentContainer
|
||||
.clientHeight;
|
||||
if (
|
||||
this.shouldRefreshProducts({
|
||||
clientHeight,
|
||||
@@ -233,7 +286,8 @@ export class RemissionProductListComponent implements OnInit, OnDestroy, AfterVi
|
||||
const pixelsScrolledUpToTriggerRefresh = 150;
|
||||
|
||||
return (
|
||||
scrollTop + clientHeight - scrollHeight < pixelsScrolledUpToTriggerRefresh - shippingContainerHeight &&
|
||||
scrollTop + clientHeight - scrollHeight <
|
||||
pixelsScrolledUpToTriggerRefresh - shippingContainerHeight &&
|
||||
!!this.viewShippingDocumentFromJumpToBottom
|
||||
);
|
||||
}
|
||||
@@ -244,7 +298,10 @@ export class RemissionProductListComponent implements OnInit, OnDestroy, AfterVi
|
||||
this.refetchRemissionProductsOnScrollUp$.next();
|
||||
}
|
||||
|
||||
private setScrollToTopBotomVisibility(topValue: boolean, bottomValue: boolean) {
|
||||
private setScrollToTopBotomVisibility(
|
||||
topValue: boolean,
|
||||
bottomValue: boolean
|
||||
) {
|
||||
this.showJumpToTop$.next(topValue);
|
||||
this.showJumpToBottom$.next(bottomValue);
|
||||
}
|
||||
@@ -255,21 +312,25 @@ export class RemissionProductListComponent implements OnInit, OnDestroy, AfterVi
|
||||
}
|
||||
|
||||
private setUpIntersectionObservers() {
|
||||
this.remissionHitsRef.changes.pipe(this.returnFirstElement).subscribe((element) => {
|
||||
this.subscribeToIntersectionSub({
|
||||
element,
|
||||
sub: this.isNumberOfHitsVisible$,
|
||||
type: 'top',
|
||||
this.remissionHitsRef.changes
|
||||
.pipe(this.returnFirstElement)
|
||||
.subscribe((element) => {
|
||||
this.subscribeToIntersectionSub({
|
||||
element,
|
||||
sub: this.isNumberOfHitsVisible$,
|
||||
type: 'top',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
this.shippingDocumentRef.changes.pipe(this.returnFirstElement).subscribe((element) => {
|
||||
this.subscribeToIntersectionSub({
|
||||
element,
|
||||
sub: this.isShippingDocumentVisible$,
|
||||
type: 'bottom',
|
||||
this.shippingDocumentRef.changes
|
||||
.pipe(this.returnFirstElement)
|
||||
.subscribe((element) => {
|
||||
this.subscribeToIntersectionSub({
|
||||
element,
|
||||
sub: this.isShippingDocumentVisible$,
|
||||
type: 'bottom',
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private returnFirstElement(el: Observable<any>): Observable<any> {
|
||||
@@ -279,7 +340,11 @@ export class RemissionProductListComponent implements OnInit, OnDestroy, AfterVi
|
||||
);
|
||||
}
|
||||
|
||||
private subscribeToIntersectionSub(params: { element: Element; sub: BehaviorSubject<boolean>; type: 'top' | 'bottom' }) {
|
||||
private subscribeToIntersectionSub(params: {
|
||||
element: Element;
|
||||
sub: BehaviorSubject<boolean>;
|
||||
type: 'top' | 'bottom';
|
||||
}) {
|
||||
const { element, sub, type } = params;
|
||||
|
||||
const unsubscribe = this.intersectionObserverSubs.get(type);
|
||||
@@ -287,7 +352,10 @@ export class RemissionProductListComponent implements OnInit, OnDestroy, AfterVi
|
||||
unsubscribe();
|
||||
}
|
||||
|
||||
this.intersectionObserverSubs.set(type, this.initIntersectionObserver(element, type));
|
||||
this.intersectionObserverSubs.set(
|
||||
type,
|
||||
this.initIntersectionObserver(element, type)
|
||||
);
|
||||
|
||||
this.intersectionObserver$
|
||||
.pipe(
|
||||
@@ -306,11 +374,13 @@ export class RemissionProductListComponent implements OnInit, OnDestroy, AfterVi
|
||||
}
|
||||
|
||||
private initIntersectionObserver(element: Element, type: 'top' | 'bottom') {
|
||||
const intersectionObserver = new IntersectionObserver((entries, observer) => {
|
||||
entries.forEach((entry) => {
|
||||
this.intersectionObserver$.next({ entry, observer, type });
|
||||
});
|
||||
});
|
||||
const intersectionObserver = new IntersectionObserver(
|
||||
(entries, observer) => {
|
||||
entries.forEach((entry) => {
|
||||
this.intersectionObserver$.next({ entry, observer, type });
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
intersectionObserver.observe(element);
|
||||
|
||||
@@ -345,22 +415,47 @@ export class RemissionProductListComponent implements OnInit, OnDestroy, AfterVi
|
||||
this.isLoading$
|
||||
)
|
||||
.pipe(
|
||||
map(([isNumberOfHitsVisible, isShippingDocumentVisible, hits, isLoading]) => {
|
||||
if (!hits || hits === -1 || !!isLoading) {
|
||||
map(
|
||||
([
|
||||
isNumberOfHitsVisible,
|
||||
isShippingDocumentVisible,
|
||||
hits,
|
||||
isLoading,
|
||||
]) => {
|
||||
if (!hits || hits === -1 || !!isLoading) {
|
||||
return {
|
||||
showTop: false,
|
||||
showBottom: false,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
showTop: false,
|
||||
showBottom: false,
|
||||
showTop: !isShippingDocumentVisible,
|
||||
showBottom: !isNumberOfHitsVisible,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
showTop: !isShippingDocumentVisible,
|
||||
showBottom: !isNumberOfHitsVisible,
|
||||
};
|
||||
}),
|
||||
),
|
||||
takeUntil(this.destroy$),
|
||||
debounceTime(350)
|
||||
)
|
||||
.subscribe(({ showTop, showBottom }) => this.setScrollToTopBotomVisibility(showTop, showBottom));
|
||||
.subscribe(({ showTop, showBottom }) =>
|
||||
this.setScrollToTopBotomVisibility(showTop, showBottom)
|
||||
);
|
||||
}
|
||||
|
||||
private resetRemissionListScrollPosition(
|
||||
waitForAnimationToComplete: boolean = false
|
||||
) {
|
||||
if (!this.remissionHitsRef || !this.remissionHitsRef.first) {
|
||||
return;
|
||||
}
|
||||
|
||||
const container = this.remissionHitsRef.first.nativeElement;
|
||||
|
||||
if (waitForAnimationToComplete) {
|
||||
setTimeout(() => {
|
||||
container.scrollTop = 0;
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,21 +27,26 @@
|
||||
<app-remission-suppliers-filter></app-remission-suppliers-filter>
|
||||
</div>
|
||||
|
||||
<div *ngIf="data.selectedSource === 'ueberlauf'; else callToActions">
|
||||
<div class="pb-16" *ngIf="data.selectedSource === 'ueberlauf'">
|
||||
<app-remission-uberlauf-capacities></app-remission-uberlauf-capacities>
|
||||
</div>
|
||||
|
||||
<ng-template #callToActions>
|
||||
<ng-container *ngIf="showCtAs$ | async">
|
||||
<app-remission-list-actions
|
||||
(startRemission)="openStartRemissionDialog()"
|
||||
(addProduct)="openAddProductToRemission()"
|
||||
(scanProduct)="searchProductScannedResult($event)"
|
||||
[isNative]="isNative"
|
||||
[isSafari]="isSafari"
|
||||
status="created"
|
||||
*ngIf="data.remissionListHits > 0"
|
||||
></app-remission-list-actions>
|
||||
</ng-template>
|
||||
|
||||
<app-remission-list-start-action
|
||||
@fadeInWithDelay
|
||||
*ngIf="!data.isLoading"
|
||||
class="bottom"
|
||||
(startRemission)="openStartRemissionDialog()"
|
||||
></app-remission-list-start-action>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
<ng-template #loadFilters>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user