Merged PR 1263: Cleanup

This commit is contained in:
Lorenz Hilpert
2022-05-31 12:56:41 +00:00
committed by Nino Righi
parent 0e5c35fae4
commit 24a008b20d
1623 changed files with 25 additions and 69073 deletions

View File

@@ -3,256 +3,6 @@
"version": 1,
"newProjectRoot": "apps",
"projects": {
"ui": {
"root": "libs/ui",
"sourceRoot": "libs/ui",
"projectType": "library",
"prefix": "lib",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:ng-packagr",
"options": {
"tsConfig": "libs/ui/tsconfig.lib.json",
"project": "libs/ui/ng-package.json"
},
"configurations": {
"production": {
"tsConfig": "libs/ui/tsconfig.lib.prod.json"
}
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "libs/ui/src/test.ts",
"tsConfig": "libs/ui/tsconfig.spec.json",
"karmaConfig": "libs/ui/karma.conf.js"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"libs/ui/tsconfig.lib.json",
"libs/ui/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
},
"sales": {
"root": "apps/sales/",
"sourceRoot": "apps/sales/src",
"projectType": "application",
"prefix": "app",
"schematics": {},
"architect": {
"build": {
"builder": "@angular-builders/custom-webpack:browser",
"options": {
"aot": true,
"outputPath": "dist/sales",
"index": "apps/sales/src/index.html",
"main": "apps/sales/src/main.ts",
"polyfills": "apps/sales/src/polyfills.ts",
"tsConfig": "apps/sales/tsconfig.app.json",
"assets": [
"apps/sales/src/favicon.ico",
"apps/sales/src/assets",
"apps/sales/src/manifest.webmanifest",
"apps/sales/src/browserconfig.xml",
"apps/sales/src/silent-refresh.html"
],
"styles": [
"apps/sales/src/styles.scss"
],
"stylePreprocessorOptions": {
"includePaths": [
"apps/sales/src/scss"
]
},
"scripts": [],
"customWebpackConfig": {
"path": "apps/sales/webpack.config.js"
}
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "apps/sales/src/environments/environment.ts",
"with": "apps/sales/src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "25kb"
}
],
"serviceWorker": true
},
"development": {
"budgets": [
{
"type": "anyComponentStyle",
"maximumWarning": "25kb"
}
]
}
}
},
"serve": {
"builder": "@angular-builders/custom-webpack:dev-server",
"options": {
"browserTarget": "sales:build"
},
"configurations": {
"test": {
"browserTarget": "sales:build:test"
},
"integration": {
"browserTarget": "sales:build:integration"
},
"staging": {
"browserTarget": "sales:build:staging"
},
"production": {
"browserTarget": "sales:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "sales:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "apps/sales/src/test.ts",
"polyfills": "apps/sales/src/polyfills.ts",
"tsConfig": "apps/sales/tsconfig.spec.json",
"karmaConfig": "apps/sales/karma.conf.js",
"styles": [
"apps/sales/src/styles.scss"
],
"stylePreprocessorOptions": {
"includePaths": [
"apps/sales/src/scss"
]
},
"scripts": [],
"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"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
},
"sales-e2e": {
"root": "apps/sales-e2e/",
"projectType": "application",
"prefix": "",
"architect": {
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "apps/sales-e2e/protractor.conf.js",
"devServerTarget": "sales:serve"
},
"configurations": {
"integration": {
"devServerTarget": "sales:serve:integration"
},
"production": {
"devServerTarget": "sales:serve:production"
}
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": "apps/sales-e2e/tsconfig.e2e.json",
"exclude": [
"**/node_modules/**"
]
}
}
}
},
"sso": {
"root": "libs/sso",
"sourceRoot": "libs/sso/src",
"projectType": "library",
"prefix": "lib",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:ng-packagr",
"options": {
"tsConfig": "libs/sso/tsconfig.lib.json",
"project": "libs/sso/ng-package.json"
},
"configurations": {
"production": {
"tsConfig": "libs/sso/tsconfig.lib.prod.json"
}
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "libs/sso/src/test.ts",
"tsConfig": "libs/sso/tsconfig.spec.json",
"karmaConfig": "libs/sso/karma.conf.js"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"libs/sso/tsconfig.lib.json",
"libs/sso/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
},
"@swagger/availability": {
"root": "apps/swagger/availability",
"sourceRoot": "apps/swagger/availability/src",
@@ -3310,6 +3060,11 @@
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"allowedCommonJsDependencies": [
"lodash",
"pdfjs-dist/es5/build/pdf",
"pdfjs-dist/es5/web/pdf_viewer"
],
"outputPath": "dist/isa-app",
"index": "apps/isa-app/src/index.html",
"main": "apps/isa-app/src/main.ts",
@@ -3875,6 +3630,6 @@
}
}
}
},
},
"defaultProject": "isa-app"
}

View File

@@ -28,7 +28,7 @@
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
*/
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/**
* By default, zone.js will patch all possible macroTask and DomEvents
@@ -62,3 +62,4 @@ import 'zone.js'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/
import 'hammerjs';

View File

@@ -1,9 +1,9 @@
import { Component, Input, OnInit } from '@angular/core';
import { ProductImageService } from '@cdn/product-image';
import { HistoryComponent } from '@modal/history';
import { ImageService } from '@sales/core-services';
import { OrderDTO, OrderItemDTO, OrderItemSubsetDTO } from '@swagger/oms';
import { UiModalService } from '@ui/modal';
import { Observable } from 'rxjs';
import { Observable, of } from 'rxjs';
@Component({
selector: 'page-customer-order-item-card',
@@ -41,13 +41,21 @@ export class CustomerOrderItemCardComponent implements OnInit {
}
}
constructor(private imageService: ImageService, private _modal: UiModalService) {}
constructor(private imageService: ProductImageService, private _modal: UiModalService) {}
ngOnInit() {
this.imageUrl = this.imageService.getImageUrl(this.orderItem.product.ean, {
width: 60,
height: 100,
});
// this.imageUrl = this.imageService.getImageUrl(this.orderItem.product.ean, {
// width: 60,
// height: 100,
// });
this.imageUrl = of(
this.imageService.getImageUrl({
imageId: this.orderItem.product.ean,
width: 60,
height: 100,
})
);
}
openHistory() {

View File

@@ -1,26 +0,0 @@
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter } = require('jasmine-spec-reporter');
exports.config = {
allScriptsTimeout: 11000,
specs: ['./src/**/*.e2e-spec.ts'],
capabilities: {
browserName: 'chrome',
},
directConnect: true,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function () {},
},
onPrepare() {
require('ts-node').register({
project: require('path').join(__dirname, './tsconfig.e2e.json'),
});
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
},
};

View File

@@ -1,14 +0,0 @@
import { AppPage } from './app.po';
describe('workspace-project App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', () => {
page.navigateTo();
expect(page.getTitleText()).toEqual('Welcome to sales!');
});
});

View File

@@ -1,11 +0,0 @@
import { browser, by, element } from 'protractor';
export class AppPage {
navigateTo() {
return browser.get('/');
}
getTitleText() {
return element(by.css('app-root h1')).getText();
}
}

View File

@@ -1,13 +0,0 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/app",
"module": "commonjs",
"target": "es5",
"types": [
"jasmine",
"jasminewd2",
"node"
]
}
}

View File

@@ -1,11 +0,0 @@
# This file is currently used by autoprefixer to adjust CSS to support the below specified browsers
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
#
# For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed
> 0.5%
last 2 versions
Firefox ESR
not dead
not IE 9-11

View File

@@ -1,32 +0,0 @@
// 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'),
reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true,
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['ChromeHeadless'],
singleRun: false,
restartOnFileChange: true,
});
};

View File

@@ -1,23 +0,0 @@
{
"index": "/index.html",
"assetGroups": [
{
"name": "sales",
"installMode": "prefetch",
"resources": {
"files": ["/favicon.ico", "/*.webmanifest", "/*.css", "/*.js"]
}
},
{
"name": "assets",
"installMode": "lazy",
"updateMode": "prefetch",
"resources": {
"files": [
"assets/**",
"/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)"
]
}
}
]
}

View File

@@ -1,22 +0,0 @@
import { Injectable } from '@angular/core';
import { SignalRHubOptions } from '@core/signalr';
import { AuthConfig } from 'angular-oauth2-oidc';
@Injectable()
export class AppConfiguration {
includeGoogleAnalytics = false;
title?: string;
cdn: { [key: string]: string };
sso: AuthConfig;
hubs: { [key: string]: SignalRHubOptions };
swagger: { [key: string]: { rootUrl: string } };
remissionModuleOptions: {
useMock: false;
endpoints: { [key: string]: string };
};
}

View File

@@ -1,94 +0,0 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes, PreloadAllModules } from '@angular/router';
import { LogInComponent } from './components/log-in/log-in.component';
import { DashboardComponent } from './modules/dashboard/pages/dashboard.component';
import { AuthenticationGuard } from './resolvers/authentication.guard';
import { ProcessIdResolver } from './resolvers/process-id.resolver';
import { BranchSectionResolver, CustomerSectionResolver } from './resolvers/section.resolver';
const routes: Routes = [
{
path: 'login',
resolve: { section: CustomerSectionResolver },
children: [
{ path: '', component: LogInComponent, pathMatch: 'full' },
{ path: ':token', component: LogInComponent },
],
},
{ path: 'dashboard', resolve: { section: CustomerSectionResolver }, component: DashboardComponent, canActivate: [AuthenticationGuard] },
{
path: 'product',
resolve: { processId: ProcessIdResolver, section: CustomerSectionResolver },
loadChildren: () => import('@page/catalog').then((m) => m.PageCatalogModule),
canActivate: [AuthenticationGuard],
},
{
path: 'customer',
resolve: { processId: ProcessIdResolver, section: CustomerSectionResolver },
loadChildren: () => import('@page/customer').then((m) => m.PageCustomerModule),
canActivate: [AuthenticationGuard],
},
{
path: 'cart',
resolve: { processId: ProcessIdResolver, section: CustomerSectionResolver },
loadChildren: () => import('@page/checkout').then((m) => m.PageCheckoutModule),
canActivate: [AuthenticationGuard],
},
{
path: 'branch',
resolve: { section: BranchSectionResolver },
loadChildren: () => import('./modules/branch/branch.module').then((m) => m.BranchModule),
canActivate: [AuthenticationGuard],
},
{
path: 'debug',
loadChildren: () => import('./modules/debug/debug.module').then((m) => m.DebugModule),
canActivate: [AuthenticationGuard],
},
{
path: 'goods',
children: [
{
path: 'in',
resolve: { section: BranchSectionResolver },
loadChildren: () => import('@page/goods-in').then((m) => m.GoodsInModule),
},
{
path: 'out',
resolve: { section: CustomerSectionResolver },
loadChildren: () => import('@page/goods-out').then((m) => m.GoodsOutModule),
},
],
canActivate: [AuthenticationGuard],
},
{
path: 'remission',
resolve: { section: BranchSectionResolver },
loadChildren: () => import('./modules/remission/remission-client.module').then((m) => m.RemissionClientModule),
canActivate: [AuthenticationGuard],
},
{
path: 'task-calendar',
resolve: { section: BranchSectionResolver },
loadChildren: () => import('@page/task-calendar').then((m) => m.PageTaskCalendarModule),
canActivate: [AuthenticationGuard],
},
{
path: 'shelf',
loadChildren: () => import('./modules/shelf/shelf.module').then((m) => m.ShelfModule),
canActivate: [AuthenticationGuard],
},
{ path: '**', redirectTo: 'dashboard' },
];
@NgModule({
imports: [
RouterModule.forRoot(routes, {
preloadingStrategy: PreloadAllModules,
scrollPositionRestoration: 'enabled',
relativeLinkResolution: 'legacy',
}),
],
exports: [RouterModule],
})
export class AppRoutingModule {}

View File

@@ -1,40 +0,0 @@
import { NgModule } from '@angular/core';
import { StoreModule, MetaReducer, ActionReducer, INIT } from '@ngrx/store';
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
import { storeFreeze } from 'ngrx-store-freeze';
import { environment } from '../environments/environment';
import { RootState } from './store/root.state';
import { rootReducer } from './store/root.reducer';
import { EffectsModule } from '@ngrx/effects';
import { SearchEffects } from './store/customer';
import { HistoryEffects } from '@shelf-store/history';
import { DetailsEffects } from '@shelf-store/details';
import packageInfo from 'package';
// TODO: In Service Speichern
export function storeInLocalStorage(reducer: ActionReducer<any>): ActionReducer<any> {
const lsKey = 'ISA_NGRX_STATE_1';
return function (state, action) {
if (action.type === INIT) {
const storedState = JSON.parse(localStorage.getItem(lsKey));
if (storedState?.version === packageInfo.version) {
return reducer(storedState, action);
}
}
const nextState = reducer(state, action);
localStorage.setItem(lsKey, JSON.stringify({ ...nextState, version: packageInfo.version }));
return nextState;
};
}
export const metaReducers: MetaReducer<RootState>[] = !environment.production ? [storeFreeze, storeInLocalStorage] : [storeInLocalStorage];
@NgModule({
imports: [
StoreModule.forRoot(rootReducer, { metaReducers }),
EffectsModule.forRoot([SearchEffects, HistoryEffects, DetailsEffects]),
StoreDevtoolsModule.instrument({ name: 'ISA Ngrx Store' }),
],
})
export class AppStoreModule {}

View File

@@ -1,64 +0,0 @@
import { NgModule } from '@angular/core';
import { PrintConfiguration } from '@swagger/print';
import { OmsConfiguration } from '@swagger/oms';
import { IsaConfiguration } from '@swagger/isa';
import { CrmConfiguration } from '@swagger/crm';
import { CheckoutConfiguration } from '@swagger/checkout';
import { AvConfiguration } from '@swagger/availability';
import { CatConfiguration } from '@swagger/cat';
import { EisConfiguration } from '@swagger/eis';
import { RemiConfiguration } from '@swagger/remi';
import { AppConfiguration } from './app-configuration';
export function catConfigurationFactory(config: AppConfiguration) {
return config.swagger.api;
}
export function avConfigurationFactory(config: AppConfiguration) {
return config.swagger.av;
}
export function checkoutConfigurationFactory(config: AppConfiguration) {
return config.swagger.checkout;
}
export function crmConfigurationFactory(config: AppConfiguration) {
return config.swagger.crm;
}
export function isaConfigurationFactory(config: AppConfiguration) {
return config.swagger.isa;
}
export function omsConfigurationFactory(config: AppConfiguration) {
return config.swagger.oms;
}
export function printConfigurationFactory(config: AppConfiguration) {
return config.swagger.print;
}
export function eisConfigurationFactory(config: AppConfiguration) {
return config.swagger.eis;
}
export function remiConfigurationFactory(config: AppConfiguration) {
return config.swagger.remi;
}
@NgModule({
providers: [
{ provide: CatConfiguration, useFactory: catConfigurationFactory, deps: [AppConfiguration] },
{ provide: AvConfiguration, useFactory: avConfigurationFactory, deps: [AppConfiguration] },
{ provide: CheckoutConfiguration, useFactory: checkoutConfigurationFactory, deps: [AppConfiguration] },
{ provide: CrmConfiguration, useFactory: crmConfigurationFactory, deps: [AppConfiguration] },
{ provide: IsaConfiguration, useFactory: isaConfigurationFactory, deps: [AppConfiguration] },
{ provide: OmsConfiguration, useFactory: omsConfigurationFactory, deps: [AppConfiguration] },
{ provide: PrintConfiguration, useFactory: printConfigurationFactory, deps: [AppConfiguration] },
{ provide: EisConfiguration, useFactory: eisConfigurationFactory, deps: [AppConfiguration] },
{ provide: RemiConfiguration, useFactory: remiConfigurationFactory, deps: [AppConfiguration] },
],
})
export class AppSwaggerModule {}

View File

@@ -1,7 +0,0 @@
<lib-offline-overlay>
<!-- lib offline depends on these three elements to be present inside of it -->
<app-header [ngClass]="{ loading: loading$ | async }" *ngIf="authenticated"></app-header>
<app-content [ngClass]="{ loading: loading$ | async }"></app-content>
<app-menu [ngClass]="{ loading: loading$ | async }" *ngIf="authenticated"></app-menu>
<img *ngIf="loading$ | async" src="/assets/images/Icon_Loading.svg" class="app-loader" />
</lib-offline-overlay>

View File

@@ -1,36 +0,0 @@
h1 {
color: #369;
font-family: Arial, Helvetica, sans-serif;
font-size: 250%;
}
.app-loader {
position: absolute;
top: 50%;
left: 50%;
margin: -20px 0 0 -20px;
-webkit-animation: spin 1.5s linear infinite;
-moz-animation: spin 1.5s linear infinite;
animation: spin 1.5s linear infinite;
}
.loading {
opacity: 0;
}
@-moz-keyframes spin {
100% {
-moz-transform: rotate(360deg);
}
}
@-webkit-keyframes spin {
100% {
-webkit-transform: rotate(360deg);
}
}
@keyframes spin {
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}

View File

@@ -1,14 +0,0 @@
import { TestBed, waitForAsync } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(
waitForAsync(() => {
TestBed.configureTestingModule({
imports: [RouterTestingModule],
declarations: [AppComponent],
}).compileComponents();
})
);
});

View File

@@ -1,132 +0,0 @@
import { ChangeDetectorRef, Component, Inject, OnDestroy, OnInit, Renderer2 } from '@angular/core';
import { Router, NavigationEnd } from '@angular/router';
import { Store } from '@ngxs/store';
import { BehaviorSubject, Subject } from 'rxjs';
import { take, filter, takeUntil } from 'rxjs/operators';
import { SsoService } from 'sso';
import { AppService } from './core/services/app.service';
import { AppState } from './core/store/state/app.state';
import { AppConfiguration } from './app-configuration';
import { NativeContainerService } from 'shared/lib/barcode-scanner';
import { DOCUMENT } from '@angular/common';
import { ApplicationService } from '@core/application';
import { DomainCheckoutService } from '@domain/checkout';
import { Actions as NgxsActions, ofActionDispatched } from '@ngxs/store';
import { DeleteProcess } from './core/store/actions/process.actions';
import { DeviceDetectorService } from 'ngx-device-detector';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
})
export class AppComponent implements OnInit, OnDestroy {
title = 'Hugendubel InstoreApp';
loading$ = new BehaviorSubject(true);
includeGoogleAnalytics = this.config.includeGoogleAnalytics;
destroy$ = new Subject();
get authenticated() {
return this.ssoService.isAuthenticated();
}
constructor(
private config: AppConfiguration,
private ssoService: SsoService,
private appService: AppService,
private router: Router,
private store: Store,
private nativeContainer: NativeContainerService,
@Inject(DOCUMENT) private document: Document,
private renderer: Renderer2,
private applicationService: ApplicationService,
private domainCheckoutService: DomainCheckoutService,
private ngxsActions$: NgxsActions,
private deviceDetectorService: DeviceDetectorService
) {
this.appService.loader$.subscribe(() => {
this.loading$.next(false);
});
this.appService.loader$.pipe(take(1)).subscribe(() => {
this.containerNotificationMessage();
});
// intialisations done only when app loads
this.appService.appLoadInitialisations();
// this.router.events.subscribe((event) => {
// if (event instanceof NavigationStart) {
// try {
// throw new Error(event.toString());
// } catch (error) {
// console.error(error);
// }
// }
// });
this.router.events.pipe(filter((e): e is NavigationEnd => e instanceof NavigationEnd)).subscribe(() => {
if (!this.router.url.includes('login')) {
// send router navigation events
this.appService.registerNavigationEvents();
}
});
this.applicationService.section$.pipe(takeUntil(this.destroy$)).subscribe((section) => {
document.body.classList.remove('branch', 'customer');
document.body.classList.add(section);
});
this.store
.select(AppState.getCurrentProcessId)
.pipe(takeUntil(this.destroy$))
.subscribe((processId) => this.applicationService.setActivatedProcessId(processId));
this.ngxsActions$.pipe(ofActionDispatched(DeleteProcess)).subscribe((action: DeleteProcess) => {
this.domainCheckoutService.removeProcess({ processId: action.payload.id });
this.applicationService.removeProcess(action.payload.id);
});
}
ngOnInit() {
if (this.includeGoogleAnalytics) {
this.initGoogleAnalytics();
}
this.renderer.addClass(this.document.body, this.deviceDetectorService.deviceType);
}
ngOnDestroy() {
this.destroy$.next();
}
private containerNotificationMessage() {
// Notify the container app if the user has initialized app loggeed in or not
// For the purpposes of removing the container Header element
if (this.nativeContainer.isUiWebview()) {
this.nativeContainer.sendMessage({ userAuthenticated: this.ssoService.isAuthenticated() });
}
}
private initGoogleAnalytics() {
const htmlScript: HTMLScriptElement = this.renderer.createElement('script');
htmlScript.text = `
(function (i, s, o, g, r, a, m) {
i['GoogleAnalyticsObject'] = r;
(i[r] =
i[r] ||
function () {
(i[r].q = i[r].q || []).push(arguments);
}),
(i[r].l = 1 * new Date());
(a = s.createElement(o)), (m = s.getElementsByTagName(o)[0]);
a.async = 1;
a.src = g;
m.parentNode.insertBefore(a, m);
})(window, document, 'script', 'https://www.google-analytics.com/analytics.js', 'ga');
ga('create', 'UA-76423009-5', 'auto');
`;
this.renderer.appendChild(this.document.body, htmlScript);
}
}

View File

@@ -1,218 +0,0 @@
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NgModule, ErrorHandler, LOCALE_ID, APP_INITIALIZER } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { ComponentsModule } from './modules/components.module';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { NgxsModule } from '@ngxs/store';
import { NgxsReduxDevtoolsPluginModule } from '@ngxs/devtools-plugin';
import { NgxsLoggerPluginModule } from '@ngxs/logger-plugin';
import { ProcessState } from './core/store/state/process.state';
import { BreadcrumbsState } from './core/store/state/breadcrumbs.state';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { SharedModule } from './shared/shared.module';
import { ScrollingModule } from '@angular/cdk/scrolling';
import { NotifierState } from './core/store/state/notifier.state';
import { environment } from '../environments/environment';
import { ModalModule, IconModule, OfflineOverlayModule } from '@libs/ui';
import { ProductState } from './core/store/state/product.state';
import { AppState } from './core/store/state/app.state';
import { BranchState } from './core/store/state/branches.state';
import { SsoModule, SsoInterface, SsoService } from 'sso';
import { SsoAuthorizationInterceptor, HttpErrorHandlerInterceptor } from './core/interceptors';
import { DatePipe } from '@angular/common';
import { HimaSalesErrorHandler } from './core/error/hima-sales.error-handler';
import { CollectingShelfState } from './core/store/state/collecting-shelf.state';
import 'apps/sales/src/app/core/utils/app.prototypes';
import { VatState } from './core/store/state/vat.state';
import { SupplierState } from './core/store/state/supplier.state';
import { GoodsInState } from './core/store/state/goods-in.state';
import { BranchProcessState } from './core/store/state/branch-process.state';
import { RemissionModule, RemissionModuleOptions } from '@isa/remission';
import { RemissionState } from './core/store/state/remission.state';
import { NgIdleKeepaliveModule } from '@ng-idle/keepalive';
import { AppSwaggerModule } from './app-swagger.module';
import { AppConfiguration } from './app-configuration';
import { FormsState } from './core/store/state/forms.state';
import localeDe from '@angular/common/locales/de';
import localeDeExtra from '@angular/common/locales/extra/de';
import { registerLocaleData } from '@angular/common';
import { RemissionStateHandler } from './core/store/handlers/remission.handlers';
import { SsoConfigurationService } from './core/services/sso-configuration.service';
import { OverlayModule } from '@angular/cdk/overlay';
import { ModalDialogueModule } from './core/overlay/component';
import { OverlaysModule } from './core/overlay/overlays.module';
import { CoreBreadcrumbModule } from '@core/breadcrumb';
import { ApplicationService, CoreApplicationModule, ProcessService } from '@core/application';
import { UiModalModule } from '@ui/modal';
import { ProcessRefactImp } from './refact-imp/process.refact-imp';
import { DomainCheckoutModule } from '@domain/checkout';
import { CDN_PRODUCT_PICTURES } from './tokens';
import { DateAdapter } from '@ui/common';
import { ApplicationRefactImp } from './refact-imp/application.refact-imp';
import { CDN_PRODUCT_IMAGE } from 'apps/cdn/product-image/src/lib/tokens';
import { DomainCatalogModule } from '@domain/catalog';
import { AppStoreModule } from './app-store.module';
import { DomainOmsModule } from '@domain/oms';
import { DomainAvailabilityModule } from '@domain/availability';
import { CoreCommandModule } from '@core/command';
import { NotificationsHubModule, NOTIFICATIONS_HUB_OPTIONS } from '@hub/notifications';
import { SignalRHubOptions } from '@core/signalr';
import { UiIconModule } from '@ui/icon';
registerLocaleData(localeDe, localeDeExtra);
registerLocaleData(localeDe, 'de', localeDeExtra);
const states = [
AppState,
ProcessState,
BreadcrumbsState,
NotifierState,
ProductState,
BranchState,
CollectingShelfState,
VatState,
SupplierState,
BranchProcessState,
GoodsInState,
RemissionState,
FormsState,
];
export function noop() {
return function () {};
}
export function remissionModuleOptionsFactory(config: AppConfiguration): RemissionModuleOptions {
return config.remissionModuleOptions;
}
export function cdnProdutctPictures(config: AppConfiguration): string {
return config.cdn.productPictures;
}
export function _notificationsHubOptionsFactory(config: AppConfiguration, sso: SsoService): SignalRHubOptions {
const options = { ...config.hubs.notifications };
options.httpOptions.accessTokenFactory = () => sso.getToken();
return options;
}
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
BrowserAnimationsModule,
AppRoutingModule,
AppSwaggerModule,
AppStoreModule,
ComponentsModule,
HttpClientModule,
NgxsModule.forRoot(states, { developmentMode: !environment.production }),
NgxsReduxDevtoolsPluginModule.forRoot({ name: 'ISA NGXS Store' }),
NgxsLoggerPluginModule,
FormsModule,
ReactiveFormsModule,
SharedModule,
ScrollingModule,
IconModule,
ModalModule.forRoot(),
SsoModule.forRoot(environment.production),
OfflineOverlayModule,
NgIdleKeepaliveModule.forRoot(),
RemissionModule.forRoot(undefined),
OverlayModule,
OverlaysModule,
ModalDialogueModule,
/**
* @core Modules
*/
CoreBreadcrumbModule.forRoot(),
CoreApplicationModule.forRoot(),
CoreCommandModule.forRoot([]),
/**
* @domain Modules
*/
DomainAvailabilityModule.forRoot(),
DomainCheckoutModule.forRoot(),
DomainCatalogModule.forRoot(),
DomainOmsModule.forRoot(),
/**
* @ui Modules
*/
UiModalModule.forRoot(),
UiIconModule,
/**
* @hub Modules
*/
NotificationsHubModule.forRoot(),
],
providers: [
{
provide: APP_INITIALIZER,
useFactory: noop,
deps: [RemissionStateHandler],
multi: true,
},
{
provide: HTTP_INTERCEPTORS,
useClass: SsoAuthorizationInterceptor,
multi: true,
},
{
provide: HTTP_INTERCEPTORS,
useClass: HttpErrorHandlerInterceptor,
multi: true,
},
{
provide: RemissionModuleOptions,
useFactory: remissionModuleOptionsFactory,
deps: [AppConfiguration],
},
DatePipe,
{
provide: ErrorHandler,
useClass: HimaSalesErrorHandler,
},
{ provide: LOCALE_ID, useValue: 'de-DE' },
{
provide: SsoInterface,
useClass: SsoConfigurationService,
},
DateAdapter,
{
provide: ProcessService,
useClass: ProcessRefactImp,
},
{
provide: ApplicationService,
useClass: ApplicationRefactImp,
},
{
provide: CDN_PRODUCT_PICTURES,
useFactory: cdnProdutctPictures,
deps: [AppConfiguration],
},
{
provide: CDN_PRODUCT_IMAGE,
useFactory: cdnProdutctPictures,
deps: [AppConfiguration],
},
{
provide: NOTIFICATIONS_HUB_OPTIONS,
useFactory: _notificationsHubOptionsFactory,
deps: [AppConfiguration, SsoService],
},
],
bootstrap: [AppComponent],
})
export class AppModule {}

View File

@@ -1,40 +0,0 @@
<div class="breadacrumb-grid" [ngClass]="{ 'grid-with-arrow': !showBack, 'breadcumb-mb-5': lowerMargin }" *ngIf="breadcrumbs">
<app-back-arrow *ngIf="showBack" (back)="goBack(breadcrumbs[breadcrumbs.length - 2])" class="align-right back-arrow"></app-back-arrow>
<div
class="layer-fade-start"
*ngIf="showBackNavigationArrow"
(mouseover)="showBackNavArrow()"
(mouseleave)="hideBackNavArrow($event)"
></div>
<div class="icon-start" *ngIf="showBackNavigationArrow && showBackArrow" (click)="navigateBack()" (mouseleave)="hideBackNavArrowIcon()">
<lib-icon class="icon-start" width="40px" name="tab_Arrow_3" type="png"></lib-icon>
</div>
<div class="breadcrumb-start-layer-fade" *ngIf="!showBackNavigationArrow"></div>
<div class="align-center breadcrumb-container" #container>
<span
*ngFor="let breadcrumb of breadcrumbs; let i = index; let last = last"
class="breadcrumb show"
(click)="selectBreadcrumb(breadcrumb)"
[ngClass]="{ selected: last, branch: module === 1 }"
>
<lib-icon *ngIf="breadcrumbs.indexOf(breadcrumb) > 0 && i != 0 && i !== last" class="next" name="Arrow_Next" alt="next"></lib-icon>
<span>{{ breadcrumb ? breadcrumb.name : '' }}</span>
</span>
<span class="breadcrumb last" *ngIf="showBack"></span>
</div>
<div class="breadcrumb-end-layer-fade" *ngIf="!showForwardNavigationalArrow"></div>
<div
class="layer-fade-end"
*ngIf="showForwardNavigationalArrow"
(mouseover)="showForwardNavArrow()"
(mouseleave)="hideForwardNavArrow($event)"
></div>
<div
class="icon-end"
*ngIf="showForwardNavigationalArrow && showForrwardArrow"
(click)="navigateForward()"
(mouseleave)="hideForwardNavArrowIcon()"
>
<lib-icon class="icon-end" width="40px" name="tab_Arrow_2" type="png"></lib-icon>
</div>
</div>

View File

@@ -1,232 +0,0 @@
@import 'variables';
.breadacrumb-grid {
display: grid;
grid-template-columns: min-content auto;
grid-gap: 15px;
height: 40px;
position: relative;
}
.breadcumb-mb-5 {
margin-bottom: 5px;
}
.grid-with-arrow {
grid-template-columns: auto;
}
.breadcrumb-container {
// display: flex;
align-items: center;
// justify-content: center;
white-space: nowrap;
overflow-y: hidden;
scroll-behavior: smooth;
overflow-x: scroll;
height: 45px;
padding-top: 10px;
// width: calc(100% - 100px);
}
:only-child {
.breadcrumb-end-layer-fade {
position: absolute;
min-height: 45px;
min-width: 5px;
z-index: 20;
opacity: 0.7;
background-color: #e6eff9;
box-shadow: -8px 0px 14px 3px #e6eff9;
border-radius: 5px;
}
}
.breadcrumb-start-layer-fade {
position: absolute;
min-height: 45px;
min-width: 5px;
z-index: 20;
opacity: 0.7;
background-color: #e6eff9;
box-shadow: 8px 0px 14px 3px #e6eff9;
border-radius: 5px;
}
.breadcrumb {
outline: none;
font-size: 16px;
color: #1f466c;
line-height: 21px;
padding: 10px 0;
padding-bottom: 0px;
cursor: pointer;
&.branch {
color: #596470;
}
}
.hide {
display: none;
opacity: 0;
&:nth-last-child(4) {
/*declarations*/
display: block;
overflow: hidden;
opacity: 0;
flex: 0.0001;
animation: fadeSlide 400ms;
}
&:nth-last-child(-n + 3) {
/*declarations*/
opacity: 1;
display: block;
}
}
.last {
padding: 10px 50px;
}
.back-arrow-container {
width: 118px;
height: 50px;
display: flex;
justify-content: center;
align-items: flex-start;
position: absolute;
z-index: 50;
transform: rotate(180deg);
cursor: pointer;
}
.foward-arrow-container {
width: 118px;
height: 50px;
display: flex;
justify-content: center;
align-items: flex-start;
position: absolute;
z-index: 50;
cursor: pointer;
}
@keyframes fadeSlide {
0% {
opacity: 1;
transform: translateX(80px);
flex: 0.5;
overflow: hidden;
}
50% {
opacity: 0;
}
100% {
transform: translateX(0);
flex: 0.0001;
}
}
.back-arrow {
padding: 10px 0;
}
.next {
padding: 0 7px;
}
.selected {
font-weight: bold;
}
.icon-start {
position: absolute;
left: 50px;
margin-top: 1px;
z-index: 30;
opacity: 1;
cursor: pointer;
}
.layer-fade-start {
position: absolute;
min-height: 40px;
min-width: 35px;
z-index: 20;
left: 100px;
opacity: 0.4;
background-color: white;
box-shadow: 3px 0 14px 8px white;
border-radius: 5px;
}
.icon-end {
position: absolute;
right: 5px;
margin-top: 1px;
z-index: 30;
opacity: 1;
cursor: pointer;
}
.layer-fade-end {
position: absolute;
min-height: 40px;
min-width: 35px;
z-index: 20;
right: 15px;
opacity: 0.4;
background-color: white;
box-shadow: 3px 0 14px 8px white;
border-radius: 5px;
}
/*
##Device = Big Desktops
*/
@media (min-width: 1281px) {
.breadcrumb-end-layer-fade {
right: 0px;
}
.breadcrumb-start-layer-fade {
left: 100px;
}
}
/*
##Device = Laptops, Desktops, Ipad pro
*/
@media (min-width: 1025px) and (max-width: 1280px) {
.breadcrumb-end-layer-fade {
right: 0px;
}
.breadcrumb-start-layer-fade {
left: 100px;
}
}
/*
##Device = Tablets, Ipads
*/
@media (min-width: 768px) and (max-width: 1024px) {
.breadcrumb-end-layer-fade {
right: 15px;
}
.breadcrumb-start-layer-fade {
left: 112px;
}
}
/*
##Device = Low Resolution Tablets, Mobiles (Landscape)
*/
@media (min-width: 481px) and (max-width: 767px) {
.breadcrumb-end-layer-fade {
right: 15px;
}
}

View File

@@ -1,13 +0,0 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { BreadcrumbsComponent } from './breadcrumbs.component';
describe('BreadcrumbsComponent', () => {
beforeEach(
waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [BreadcrumbsComponent],
}).compileComponents();
})
);
});

View File

@@ -1,286 +0,0 @@
import {
Component,
OnInit,
OnDestroy,
ChangeDetectorRef,
ChangeDetectionStrategy,
ViewChild,
ElementRef,
AfterViewInit,
} from '@angular/core';
import { Subject, Observable } from 'rxjs';
import { Breadcrumb } from '../../core/models/breadcrumb.model';
import { Process } from '../../core/models/process.model';
import { Store, Select } from '@ngxs/store';
import { ChangeCurrentRoute, ResetProcessProductFilters } from '../../core/store/actions/process.actions';
import { Router } from '@angular/router';
import { takeUntil, switchMap, distinctUntilChanged } from 'rxjs/operators';
import { SharedSelectors } from '../../core/store/selectors/shared.selectors';
import { ResetFilters } from '../../core/store/actions/filter.actions';
import { AppService } from '../../core/services/app.service';
import { ModuleSwitcher } from '../../core/models/app-switcher.enum';
import { BranchProcess } from '../../core/models/branch-process.model';
import { SetBranchProcessCurrentPath } from '../../core/store/actions/branch-process.actions';
import { AppState } from '../../core/store/state/app.state';
import { FILIALE_LANDING_PAGE } from '../../core/utils/app.constants';
@Component({
selector: 'app-breadcrumbs',
templateUrl: './breadcrumbs.component.html',
styleUrls: ['./breadcrumbs.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class BreadcrumbsComponent implements OnInit, OnDestroy, AfterViewInit {
@ViewChild('container', { read: ElementRef })
public container: ElementRef<any>;
@Select(SharedSelectors.getBreadcrumbs) breadcrumbs$: Observable<Breadcrumb[]>;
@Select(SharedSelectors.getCurrentProcess) currentProcess$: Observable<Process>;
module: ModuleSwitcher;
destroy$ = new Subject();
breadcrumbs: Breadcrumb[] = [];
currentRoute = '';
start: number;
end: number;
breadsCount: number;
showBack = true;
showBackNavigationArrow = false;
showBackArrow = false;
showForwardNavigationalArrow = false;
showForrwardArrow = false;
isIPad = false;
get firstVisibleItem() {
if (this.start) {
return this.start;
}
return 0;
}
get lastVisibleItem() {
if (this.end) {
return this.end;
}
return this.breadcrumbs ? this.breadcrumbs.length - 1 : 0;
}
get backArrow() {
return this.router.url.substring(0, 16) === '/product/details' && this.breadsCount > 1;
}
get selectedBreadCrumbIndex() {
if (!this.currentRoute) {
return 0;
}
let route = this.currentRoute;
const hasParams = this.currentRoute.includes('?');
if (hasParams) {
const endOfRouteWithOutParams = this.currentRoute.indexOf('?');
route = this.currentRoute.substring(0, endOfRouteWithOutParams);
}
return this.breadcrumbs.findIndex((t) => t && t.path.indexOf(route) >= 0);
}
get lowerMargin() {
if (this.router.url === '/customer/results') {
return true;
}
if (this.router.url.substring(0, 14) === '/customer/edit') {
return true;
}
if (this.router.url.substring(0, 16) === '/product/details') {
return true;
}
return false;
}
constructor(private store: Store, private router: Router, private cdrf: ChangeDetectorRef, private appService: AppService) {}
getBreadcrumbsFromCurentProcess() {
this.currentProcess$
.pipe(
switchMap((process: Process | BranchProcess) => {
if (process) {
this.currentRoute = `${process.currentRoute}`;
this.cdrf.detectChanges();
}
setTimeout(() => {
this.container.nativeElement.scrollTo({
left: this.container.nativeElement.scrollWidth,
behavior: 'smooth',
});
}, 400);
return this.breadcrumbs$;
}),
takeUntil(this.destroy$),
distinctUntilChanged()
)
.subscribe((breadcrumbs: Breadcrumb[]) => {
this.showForwardNavigationalArrow = false;
this.showBackNavigationArrow = false;
if (breadcrumbs) {
const breadName =
breadcrumbs.length > 0 ? (breadcrumbs[breadcrumbs.length - 1] ? breadcrumbs[breadcrumbs.length - 1].name : '') : '';
this.showBack = !(breadName === 'Bestellbestätigung' || breadName === 'Remission');
this.breadcrumbs = breadcrumbs;
this.breadsCount = Object.keys(this.breadcrumbs) ? Object.keys(this.breadcrumbs).length : 0;
this.cdrf.detectChanges();
if (!this.isIPad) {
setTimeout(() => {
try {
this.initNavigationalArrow();
} catch (error) {}
}, 1000);
}
}
});
}
selectBreadcrumb(breadcrumb: Breadcrumb) {
if (breadcrumb && breadcrumb.path) {
if (breadcrumb.path === '/product/search') {
this.store.dispatch(new ResetFilters());
this.store.dispatch(new ResetProcessProductFilters());
}
if (this.module === ModuleSwitcher.Customer) {
this.store.dispatch(new ChangeCurrentRoute(breadcrumb.path, true));
} else {
this.store.dispatch(new SetBranchProcessCurrentPath(breadcrumb.path, true));
}
this.router.navigate([breadcrumb.path], {
queryParams: breadcrumb.queryParams ? breadcrumb.queryParams : {},
});
}
}
goBack(breadcrumb: Breadcrumb) {
if (breadcrumb && breadcrumb.path) {
if (this.module === ModuleSwitcher.Customer) {
this.store.dispatch(new ChangeCurrentRoute(breadcrumb.path, false));
this.router.navigate(['/product/results']);
} else {
this.store.dispatch(new SetBranchProcessCurrentPath(breadcrumb.path, false));
this.router.navigate([FILIALE_LANDING_PAGE]);
}
}
}
ngOnInit() {
this.isIPad = this.appService.isIPadEnv();
this.getBreadcrumbsFromCurentProcess();
this.store
.select(AppState.activeModule)
.pipe(takeUntil(this.destroy$))
.subscribe((v: ModuleSwitcher) => {
this.module = v;
this.cdrf.detectChanges();
});
}
ngAfterViewInit() {
if (!this.isIPad) {
setTimeout(() => {
try {
this.initNavigationalArrow();
} catch (error) {}
}, 100);
}
}
ngOnDestroy(): void {
this.destroy$.next();
}
addOne() {
this.breadcrumbs.push({
name: 'test' + (this.breadcrumbs ? this.breadcrumbs.length : 0),
path: './i',
});
}
initNavigationalArrow() {
if (this.container) {
const containerWidth = this.container.nativeElement.offsetWidth;
let childrenWidth = 60;
let elementProcessed = 0;
if (this.container.nativeElement.children) {
const breadcrumbs = this.container.nativeElement.children.length - 1;
for (let i = 0; i < breadcrumbs; i++) {
elementProcessed++;
childrenWidth += this.container.nativeElement.children[i].offsetWidth;
}
if (breadcrumbs === elementProcessed && containerWidth <= childrenWidth) {
this.showBackNavigationArrow = true;
this.cdrf.detectChanges();
} else {
this.showBackNavigationArrow = false;
this.showForwardNavigationalArrow = false;
}
}
}
}
showBackNavArrow() {
this.showBackArrow = true;
}
hideBackNavArrow(event) {
if (event) {
const e = event.toElement || event.relatedTarget;
if (e && e.classList && (e.classList[0] === 'icon' || e.classList[0] === 'ng-star-inserted')) {
return;
}
this.showBackArrow = false;
}
}
hideBackNavArrowIcon() {
this.showBackArrow = false;
}
showForwardNavArrow() {
this.showForrwardArrow = true;
}
hideForwardNavArrow(event) {
if (event) {
const e = event.toElement || event.relatedTarget;
if (e && e.classList && (e.classList[0] === 'icon' || e.classList[0] === 'ng-star-inserted')) {
return;
}
this.showForrwardArrow = false;
}
}
hideForwardNavArrowIcon() {
this.showForrwardArrow = false;
}
navigateBack() {
const scrollLeft = this.container.nativeElement.scrollLeft;
if (scrollLeft <= 121) {
this.showBackNavigationArrow = false;
}
this.container.nativeElement.scrollTo({
left: scrollLeft - 120,
behavior: 'smooth',
});
this.showForwardNavigationalArrow = true;
this.cdrf.detectChanges();
}
navigateForward() {
const scrollLeft = this.container.nativeElement.scrollLeft;
const containerWidth = this.container.nativeElement.offsetWidth;
const scrollWidth = this.container.nativeElement.scrollWidth;
if (scrollLeft + 119 + containerWidth <= scrollWidth) {
this.showForwardNavigationalArrow = false;
}
this.container.nativeElement.scrollTo({
left: scrollLeft + 120,
behavior: 'smooth',
});
this.showBackNavigationArrow = true;
this.cdrf.detectChanges();
}
}

View File

@@ -1,9 +0,0 @@
<div class="container">
<app-breadcrumbs *ngIf="showBreadCrumbs$ | async"> </app-breadcrumbs>
<app-filter-button
[active]="isFilterActive$ | async"
[module]="activeModule$ | async"
*ngIf="showFilter$ | async"
(toggleFilter)="toggleFilter()"
></app-filter-button>
</div>

View File

@@ -1,31 +0,0 @@
$filter-width: 106px;
.container {
display: flex;
align-items: center;
margin-top: 8px;
margin-bottom: 16px;
app-breadcrumbs {
flex-grow: 1;
margin-left: $filter-width;
::ng-deep app-back-arrow {
margin-right: $filter-width;
margin-left: -$filter-width;
& ~ .breadcrumb-container {
margin-left: 0;
}
}
&:only-child {
margin-left: 0;
::ng-deep app-back-arrow {
margin-right: 0;
margin-left: 0;
}
}
}
}

View File

@@ -1,25 +0,0 @@
import { TestBed, ComponentFixture } from '@angular/core/testing';
import { ComponentsModule } from '../../modules/components.module';
import { ContentHeaderComponent } from './content-header.component';
describe('ContentHeaderComponent', () => {
let fixture: ComponentFixture<ContentHeaderComponent>;
let component: ContentHeaderComponent;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ComponentsModule],
});
fixture = TestBed.createComponent(ContentHeaderComponent);
component = fixture.componentInstance;
});
it('should create the component', () => {
expect(component).toBeTruthy();
});
it('should show breadcrumbs', () => {});
it('should show filter button', () => {});
});

View File

@@ -1,34 +0,0 @@
import { Component, ChangeDetectionStrategy, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { RemissionSelectors } from '../../core/store/selectors/remission.selectors';
import { Select } from '@ngxs/store';
import { ContentHeaderService } from '../../core/services/content-header.service';
@Component({
selector: 'app-content-header',
templateUrl: 'content-header.component.html',
styleUrls: ['./content-header.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ContentHeaderComponent implements OnInit {
@Select(RemissionSelectors.getRemissionActiveFilters)
remissionFilters$: Observable<string[]>;
showFilter$: Observable<boolean>;
showBreadCrumbs$: Observable<boolean>;
isFilterActive$: Observable<boolean>;
activeModule$: Observable<'Customer' | 'Branch'>;
constructor(private contentHeaderService: ContentHeaderService) {}
ngOnInit() {
this.showFilter$ = this.contentHeaderService.showFilter$;
this.showBreadCrumbs$ = this.contentHeaderService.showBreadcrumbs$;
this.activeModule$ = this.contentHeaderService.module$;
this.isFilterActive$ = this.contentHeaderService.isFilterActive$;
}
toggleFilter() {
this.contentHeaderService.toggleFilter();
}
}

View File

@@ -1,3 +0,0 @@
// start:ng42.barrel
export * from './content-header.component';
// end:ng42.barrel

View File

@@ -1,52 +0,0 @@
<div class="header-wrapper" [ngClass]="{ 'branch-header-wrapper': module === 1 }">
<div class="app-header px-16">
<div class="three-col-grid-container">
<div class="align-left">
<a *ngIf="module === 0" (click)="goToDashboard()" class="nav-link">
<lib-icon width="190px" name="Logo-Refined_2-3x" type="png"></lib-icon>
</a>
<a *ngIf="module === 1">
<lib-icon width="190px" name="Logo-Refined_2-3x" type="png"></lib-icon>
</a>
</div>
<div class="align-center">
<div class="icons-grid pt-5">
<div class="header-item align-center">
<button class="header-icon dashboard" type="button" [routerLink]="['/dashboard']" routerLinkActive="active">
<ui-icon icon="dashboard" size="26px"></ui-icon>
</button>
</div>
<div class="header-item align-center">
<button
class="header-icon notification"
type="button"
[class.active]="notificationCount$ | async"
[disabled]="(notificationCount$ | async) === 0"
(click)="openNotifications()"
>
<div class="notification-counter" *ngIf="notificationCount$ | async; let count">{{ count }}</div>
<ui-icon icon="notification" size="26px"></ui-icon>
</button>
</div>
<div class="header-item align-center">
<span class="current-branch" *ngIf="currentBranch$ | async; let currentBranch" [title]="currentBranch.name">
{{ currentBranch.key | uppercase }}
</span>
<button class="header-icon logout" type="button" (click)="logoff()">
<ui-icon icon="logout" size="26px"></ui-icon>
</button>
</div>
</div>
</div>
<div class="align-right">
<div class="switch-wrapper">
<div></div>
<lib-double-choice-switch [model]="doubleChoiceSwitch" (change)="siteChange($event)"></lib-double-choice-switch>
</div>
</div>
</div>
<app-process-header></app-process-header>
</div>
</div>
<app-log-out #logOut></app-log-out>

View File

@@ -1,177 +0,0 @@
@import 'variables';
button.header-icon {
@apply border-none outline-none bg-transparent;
}
::ng-deep .customer app-header {
button.header-icon {
ui-icon {
@apply text-wild-blue-yonder;
}
&.active ui-icon {
@apply text-inactive-customer;
}
}
.current-branch {
@apply text-wild-blue-yonder;
}
}
::ng-deep .branch app-header {
button.header-icon {
ui-icon {
@apply text-cool-grey;
}
&.active ui-icon {
@apply text-active-branch;
}
}
.current-branch {
@apply text-cool-grey;
}
}
button.notification {
@apply relative;
}
.notification-counter {
@apply absolute flex items-center justify-center -top-2 -right-1 bg-brand text-white text-sm rounded-full w-6 h-6 font-semibold;
z-index: 10;
}
.header-item {
@apply flex;
.current-branch {
@apply flex items-center font-bold;
}
}
.header-wrapper {
background-color: white;
position: fixed;
top: 0;
height: 105px;
width: 100%;
z-index: 150;
padding-top: 30px;
box-shadow: 0px 2px 6px 0px #dde5ec;
display: flex;
justify-content: center;
align-items: center;
}
.branch-header-wrapper {
box-shadow: 0px 2px 6px 0px #e9ebee;
}
.logout {
cursor: pointer;
}
.app-header {
position: fixed;
top: 0;
width: 737px;
height: 105px;
padding-top: 30px;
z-index: 100;
display: flex;
flex-direction: column;
justify-content: flex-start;
}
.three-col-grid-container {
display: grid;
grid-template-columns: auto auto auto;
}
.three-col-grid-container-fixed {
display: grid;
grid-template-columns: auto max-content 40px;
grid-column-gap: 1vh;
}
.profile-name {
font-weight: bold;
color: $color-active;
font-size: 16px;
}
.two-col-grid-container {
display: grid;
grid-template-columns: auto auto;
grid-column-gap: 2vh;
}
.icons-grid {
display: grid;
grid-template-columns: auto auto auto;
grid-column-gap: 1vh;
}
.grid-item {
background-color: rgba(255, 255, 255, 0.8);
}
.switch-wrapper {
display: grid;
grid-template-columns: auto min-content;
}
.nav-link {
cursor: pointer;
}
/*
##Device = Big Desktops
*/
@media (min-width: 1281px) {
.app-header {
width: 916px;
}
}
/*
##Device = Laptops, Desktops, Ipad pro
*/
@media (min-width: 1025px) and (max-width: 1280px) {
.app-header {
width: 916px;
}
}
/*
##Device = Tablets, Ipads
*/
@media (min-width: 768px) and (max-width: 1024px) {
.app-header {
width: 737px;
}
}
/*
##Device = Low Resolution Tablets, Mobiles (Landscape)
*/
@media (min-width: 481px) and (max-width: 767px) {
.app-header {
width: 94%;
}
}
/*
##Device = Most of the Smartphones Mobiles (Portrait)
*/
@media (min-width: 320px) and (max-width: 480px) {
.app-header {
width: 94%;
}
}

View File

@@ -1,22 +0,0 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { HeaderComponent } from './header.component';
describe('HeaderComponent', () => {
let component: HeaderComponent;
let fixture: ComponentFixture<HeaderComponent>;
beforeEach(
waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [HeaderComponent],
}).compileComponents();
})
);
beforeEach(() => {
fixture = TestBed.createComponent(HeaderComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
});

View File

@@ -1,112 +0,0 @@
import { Component, OnInit, ViewChild, OnDestroy, ChangeDetectorRef } from '@angular/core';
import { LogOutComponent } from '../log-out/log-out.component';
import { DoubleChoiceSwitch, DoubleChoiceSwitchColor } from '@libs/ui';
import { ModuleSwitcher } from '../../core/models/app-switcher.enum';
import { Subject } from 'rxjs';
import { distinctUntilChanged, first, map, takeUntil } from 'rxjs/operators';
import { Store } from '@ngxs/store';
import { ChangeCurrentRoute } from '../../core/store/actions/process.actions';
import { Router } from '@angular/router';
import { BreadcrumbService } from '@core/breadcrumb';
import { ApplicationService } from '@core/application';
import { NotificationsHub } from '@hub/notifications';
import { UiModalService } from '@ui/modal';
import { ModalNotificationsComponent } from 'apps/modal/notifications/src/public-api';
import { DomainAvailabilityService } from '@domain/availability';
@Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.scss'],
})
export class HeaderComponent implements OnInit, OnDestroy {
@ViewChild('logOut') logOutDialog: LogOutComponent;
doubleChoiceSwitch: DoubleChoiceSwitch;
module: ModuleSwitcher = ModuleSwitcher.Customer;
destroy$ = new Subject();
notifications$ = this._notificationsHub.notifications$;
notificationCount$ = this.notifications$.pipe(map((message) => message?.data?.length));
currentBranch$ = this._availabilityService.getCurrentBranch();
constructor(
private store: Store,
private router: Router,
private breadcrumb: BreadcrumbService,
private applicationService: ApplicationService,
private cdr: ChangeDetectorRef,
private _notificationsHub: NotificationsHub,
private _modal: UiModalService,
private _availabilityService: DomainAvailabilityService
) {}
customer = 'Kunden';
ngOnInit() {
this.doubleChoiceSwitch = <DoubleChoiceSwitch>{
firstChoice: 'Kunden',
secondChoice: 'Filiale',
firstChoiceColor: <DoubleChoiceSwitchColor>{
selectedBackground: '#1F466C',
selectedText: '#ffffff',
unSelectedbackground: '#edeff0',
unSelectedText: '#000000',
},
secondChoiceColor: <DoubleChoiceSwitchColor>{
selectedBackground: '#596470',
selectedText: '#ffffff',
unSelectedbackground: '#E6EFF9',
unSelectedText: '#000000',
},
isFirstSwitchedOn: true,
};
this.applicationService.section$.pipe(distinctUntilChanged(), takeUntil(this.destroy$)).subscribe((section) => {
this.module = section === 'branch' ? ModuleSwitcher.Branch : ModuleSwitcher.Customer;
this.doubleChoiceSwitch.isFirstSwitchedOn = section === 'customer';
this.cdr.markForCheck();
});
}
ngOnDestroy() {
this.destroy$.next();
}
logoff() {
this.logOutDialog.openDialog();
}
async siteChange(model: DoubleChoiceSwitch) {
const section = model.isFirstSwitchedOn ? 'customer' : 'branch';
const crumb = await this.breadcrumb.getLatestBreadcrumbForSection(section).pipe(first()).toPromise();
if (crumb) {
return this.router.navigate([crumb.path], { queryParams: crumb.params });
}
this.router.navigate([section === 'customer' ? '/dashboard' : '/task-calendar']);
}
goToDashboard() {
this.store.dispatch(new ChangeCurrentRoute('/dashboard'));
this.router.navigate(['/dashboard']);
}
notificationAction() {
// navigations needed for development purpouse
// this.router.navigate(['/remission/finish']);
}
async openNotifications() {
const notifications = await this.notifications$.pipe(first()).toPromise();
this._modal.open({
content: ModalNotificationsComponent,
data: notifications,
config: {
showScrollbarY: false,
},
});
}
}

View File

@@ -1,21 +0,0 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { SsoService } from 'sso';
@Component({
selector: 'app-log-in',
templateUrl: './log-in.component.html',
styleUrls: ['./log-in.component.scss'],
})
export class LogInComponent implements OnInit {
constructor(private route: ActivatedRoute, private ssoService: SsoService) {}
ngOnInit() {
const token = this.route.snapshot.paramMap.get('token');
this.logIn(token);
}
private logIn(token: string) {
this.ssoService.keyCardLogin(token);
}
}

View File

@@ -1,17 +0,0 @@
<app-modal id="logout-modal">
<div class="logout-modal">
<div class="header">
<h1>Möchten Sie sich wirklich ausloggen?</h1>
<lib-icon (click)="closeModal()" height="21px" class="close-icon" name="close" alt="close"></lib-icon>
</div>
<div class="body"></div>
<div class="actions">
<div class="align-right">
<app-button (action)="closeModal()">Abbrechen</app-button>
</div>
<div class="align-right">
<app-button [primary]="true" (action)="logoff()">Ausloggen</app-button>
</div>
</div>
</div>
</app-modal>

View File

@@ -1,36 +0,0 @@
.logout-modal {
font-family: 'Open Sans';
line-height: 21px;
margin: 16px 0;
display: flex;
align-items: center;
flex-direction: column;
min-height: 185px;
h1 {
font-size: 20px;
font-weight: bold;
text-align: center;
margin-top: 20px;
}
.header {
.close-icon {
position: absolute;
top: 25px;
right: 25px;
height: 21px;
}
.close-icon:hover {
cursor: pointer;
}
}
.actions {
margin-top: 50px;
display: flex;
justify-content: center;
align-items: center;
}
}

View File

@@ -1,22 +0,0 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { LogOutComponent } from './log-out.component';
describe('LogOutComponent', () => {
let component: LogOutComponent;
let fixture: ComponentFixture<LogOutComponent>;
beforeEach(
waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [LogOutComponent],
}).compileComponents();
})
);
beforeEach(() => {
fixture = TestBed.createComponent(LogOutComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
});

View File

@@ -1,28 +0,0 @@
import { Component, OnInit } from '@angular/core';
import { SsoService } from 'sso';
import { ModalService } from '@libs/ui';
import { NativeContainerService } from 'shared/lib/barcode-scanner';
@Component({
selector: 'app-log-out',
templateUrl: './log-out.component.html',
styleUrls: ['./log-out.component.scss'],
})
export class LogOutComponent implements OnInit {
id = 'logout-modal';
constructor(private ssoService: SsoService, private modalService: ModalService, private nativeContainer: NativeContainerService) {}
ngOnInit() {}
logoff() {
this.ssoService.logoff();
}
openDialog() {
this.modalService.open(this.id);
}
closeModal() {
this.modalService.close(this.id);
}
}

View File

@@ -1,145 +0,0 @@
<!-- Customer module menus -->
<div class="menu" *ngIf="module === 0">
<div class="menu-grid">
<div class="menu-item-grid align-center active" (click)="routeToMenu('/product/search', 'productsearch')">
<div>
<lib-icon
class="menu-icon"
name="{{
router.url === '/product/search' || router.url.startsWith('/product/results') || router.url.startsWith('/product/details')
? 'Icon_Artikelsuche'
: 'Icon_Artikelsuche_inactive'
}}"
width="34px"
height="24px"
></lib-icon>
</div>
<span
*ngIf="
router.url === '/product/search' || router.url.startsWith('/product/results') || router.url.startsWith('/product/details');
else articleSearchLabelElse
"
class="menu-item selected"
>Artikelsuche</span
>
<ng-template #articleSearchLabelElse>
<span class="menu-item">Artikelsuche</span>
</ng-template>
</div>
<div
class="menu-item-grid align-center active"
(click)="routeToMenu('/customer/search', 'customersearch', { customertype: 'store;loyalty;webshop' })"
>
<div>
<lib-icon
class="menu-icon"
name="{{
router.url === '/customer/search' ||
router.url === '/customer/results' ||
router.url.startsWith('/customer/edit') ||
router.url.startsWith('/customer')
? 'Icon_Kundensuche'
: 'Icon_Kundensuche_inactive'
}}"
width="34px"
height="24px"
></lib-icon>
</div>
<span
*ngIf="
router.url === '/customer/search' ||
router.url === '/customer/results' ||
router.url.startsWith('/customer/edit') ||
router.url.startsWith('/customer');
else customerSearchLabelElse
"
class="menu-item selected"
>Kundensuche</span
>
<ng-template #customerSearchLabelElse>
<span class="menu-item">Kundensuche</span>
</ng-template>
</div>
<div class="menu-item-grid align-center active" (click)="routeToMenu('/goods/out', 'shelfsearch')">
<!-- <div class="menu-item-grid align-center"> -->
<div>
<lib-icon
class="menu-icon"
name="{{ router.url === '/goods/out' || router.url.startsWith('/goods/out') ? 'Icon_Abholfach' : 'Icon_Abholfach_inactive' }}"
width="34px"
height="24px"
></lib-icon>
</div>
<span
*ngIf="router.url === '/shelf/search' || router.url.startsWith('/goods/out'); else shelfSearchLabelElse"
class="menu-item selected"
>Warenausgabe</span
>
<ng-template #shelfSearchLabelElse>
<span class="menu-item">Warenausgabe</span>
</ng-template>
</div>
</div>
</div>
<!-- Branch module menus -->
<div class="branch-menu" *ngIf="module === 1">
<div class="menu-grid">
<!-- <div class="menu-item-grid align-center disabled"></div> -->
<div class="menu-item-grid align-center active" (click)="routeToMenu('/task-calendar/calendar', 'calendar')">
<div>
<lib-icon
class="menu-icon"
name="{{
router.url === '/task-calendar/calendar' || router.url === '/task-calendar/tasks'
? 'Icon_Tatigkeitskalender'
: 'Icon_Tatigkeitskalender_inactive'
}}"
width="34px"
height="24px"
></lib-icon>
</div>
<span
*ngIf="router.url === '/task-calendar/calendar' || router.url === '/task-calendar/tasks'; else taskCalendarLabelElse"
class="branch-menu-item branch-selected"
>Tätigkeitskalender</span
>
<ng-template #taskCalendarLabelElse>
<span class="branch-menu-item">Tätigkeitskalender</span>
</ng-template>
</div>
<div class="menu-item-grid align-center active" (click)="routeToMenu('/goods/in', 'goods-in')">
<div>
<lib-icon
class="menu-icon"
name="{{ router.url.startsWith('/goods/in') ? 'Icon_Abholfach_B' : 'Icon_Abholfach_B_inactive' }}"
width="34px"
height="24px"
></lib-icon>
</div>
<span *ngIf="router.url.startsWith('/goods/in'); else abholfachLabelElse" class="branch-menu-item branch-selected">Abholfach</span>
<ng-template #abholfachLabelElse>
<span class="branch-menu-item">Abholfach</span>
</ng-template>
</div>
<div class="menu-item-grid align-center active" (click)="routeToMenu('/remission/create', 'remission')">
<div>
<lib-icon
class="menu-icon"
name="{{
router.url === '/remission/create' || router.url === '/remission/started' ? 'Icon_Remission' : 'Icon_Remission_inactive'
}}"
width="34px"
height="24px"
></lib-icon>
</div>
<span
*ngIf="router.url === '/remission/create' || router.url === '/remission/started'; else remissionLabelElse"
class="branch-menu-item branch-selected"
>Remission</span
>
<ng-template #remissionLabelElse>
<span class="branch-menu-item">Remission</span>
</ng-template>
</div>
</div>
</div>

View File

@@ -1,121 +0,0 @@
@import 'variables';
.menu {
position: fixed;
bottom: 0;
width: 100%;
height: 80px;
background-color: white;
z-index: 100;
box-shadow: 0px -2px 6px 0px #dde5ec;
}
.branch-menu {
position: fixed;
bottom: 0;
width: 100%;
height: 80px;
background-color: white;
z-index: 100;
box-shadow: 0px -2px 6px 0px #e9ebee;
}
.menu-grid {
display: grid;
grid-template-columns: 33% 33% 33%;
padding-top: 20px;
margin: auto;
}
.menu-item-grid {
display: grid;
grid-template-columns: auto;
outline: none;
}
.menu-item {
font-size: 15px;
line-height: 21px;
font-weight: 600;
color: $color-inactive;
}
.selected {
color: $color-active;
}
.disabled {
opacity: 0.2;
}
.branch-menu-item {
font-size: 15px;
line-height: 21px;
font-weight: 600;
color: $branch-color-inactive;
&.branch-disabled {
color: #edeff0;
}
}
.branch-selected {
color: $branch-color-active;
}
.missing-menu {
font-family: 'Open Sans';
font-size: 15px;
font-weight: 600;
color: #89949e;
opacity: 0.6;
}
.active {
cursor: pointer;
}
/*
##Device = Big Desktops
*/
@media (min-width: 1281px) {
.menu-grid {
width: 980px;
}
}
/*
##Device = Laptops, Desktops, Ipad pro
*/
@media (min-width: 1025px) and (max-width: 1280px) {
.menu-grid {
width: 980px;
}
}
/*
##Device = Tablets, Ipads
*/
@media (min-width: 768px) and (max-width: 1024px) {
.menu-grid {
width: 768px;
}
}
/*
##Device = Low Resolution Tablets, Mobiles (Landscape)
*/
@media (min-width: 481px) and (max-width: 767px) {
.menu-grid {
width: 94%;
}
}
/*
##Device = Most of the Smartphones Mobiles (Portrait)
*/
@media (min-width: 320px) and (max-width: 480px) {
.menu-grid {
width: 94%;
}
}

View File

@@ -1,22 +0,0 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { MenuComponent } from './menu.component';
describe('MenuComponent', () => {
let component: MenuComponent;
let fixture: ComponentFixture<MenuComponent>;
beforeEach(
waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [MenuComponent],
}).compileComponents();
})
);
beforeEach(() => {
fixture = TestBed.createComponent(MenuComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
});

View File

@@ -1,168 +0,0 @@
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Router } from '@angular/router';
import { Observable, Subject } from 'rxjs';
import { Process } from '../../core/models/process.model';
import { Store, Select } from '@ngxs/store';
import {
AddProcess,
ChangeCurrentRoute,
ResetProcessProductFilters,
SetOnlineCustomerCreationStatus,
} from '../../core/store/actions/process.actions';
import { Breadcrumb } from '../../core/models/breadcrumb.model';
import { ResetBreadcrumbsTo } from '../../core/store/actions/breadcrumb.actions';
import { takeUntil, distinctUntilChanged } from 'rxjs/operators';
import { ProcessSelectors } from '../../core/store/selectors/process.selectors';
import { ResetFilters } from '../../core/store/actions/filter.actions';
import { ModuleSwitcher } from '../../core/models/app-switcher.enum';
import { ModuleSwitcherService } from '../../core/services/module-switcher.service';
import { SetBranchProcessCurrentPath } from '../../core/store/actions/branch-process.actions';
import { InitRemissionState, SetRemissionFinishedProcessStatus, ResetRemissionState } from '../../core/store/actions/remission.actions';
import { RemissionSelectors } from '../../core/store/selectors/remission.selectors';
import { RemissionFinishingProcessStatus } from '../../modules/remission/models/remission-finishing-process-status.enum';
import { DeleteFormState } from '../../core/store/actions/forms.actions';
import { USER_FORM_STATE_KEY, USER_EXTRAS_FORM_STATE_KEY, USER_ERRORS_FORM_STATE_KEY } from '../../core/utils/app.constants';
import { ApplicationService } from '@core/application';
@Component({
selector: 'app-menu',
templateUrl: './menu.component.html',
styleUrls: ['./menu.component.scss'],
})
export class MenuComponent implements OnInit, OnDestroy {
@Select(ProcessSelectors.getProcesses) processes$: Observable<Process[]>;
module: ModuleSwitcher = ModuleSwitcher.Customer;
processes: Process[] = [];
destroy$ = new Subject();
breadCrumbId = 'product';
constructor(
public router: Router,
private store: Store,
private moduleSwitcherService: ModuleSwitcherService,
private applicationService: ApplicationService
) {}
activeMenu = '';
routeToMenu(menuPath: string, menuTag: string, queryParams?: { [key: string]: string }): void {
if (this.processes && this.processes.length < 1 && this.module === ModuleSwitcher.Customer) {
this.createProcess(menuPath);
}
this.activeMenu = menuTag;
this.store.dispatch(
new ResetBreadcrumbsTo(
<Breadcrumb>{
name: this.nameFromPath(menuPath),
path: menuPath,
},
this.breadCrumbId,
true
)
);
if (menuTag === 'productsearch') {
this.store.dispatch(new ResetFilters());
this.store.dispatch(new ResetProcessProductFilters());
}
if (menuTag === 'remission') {
this.store.dispatch(new InitRemissionState());
}
if (menuTag === 'customersearch') {
this.store.dispatch(new DeleteFormState(USER_FORM_STATE_KEY));
this.store.dispatch(new DeleteFormState(USER_EXTRAS_FORM_STATE_KEY));
this.store.dispatch(new DeleteFormState(USER_ERRORS_FORM_STATE_KEY));
this.store.dispatch(new SetOnlineCustomerCreationStatus({ error: false, invalidProperties: null }));
}
if (this.router.url === '/remission/finish' && menuTag === 'remission') {
const processStatus = this.store.selectSnapshot(RemissionSelectors.getRemissionFinishingProcessStatus);
if (processStatus === RemissionFinishingProcessStatus.start) {
this.store.dispatch(new SetRemissionFinishedProcessStatus(RemissionFinishingProcessStatus.notSet));
} else {
this.store.dispatch(new SetRemissionFinishedProcessStatus(RemissionFinishingProcessStatus.notSet));
this.navigateToNewRemissionList();
return;
}
}
if (this.module === ModuleSwitcher.Branch) {
this.store.dispatch(new SetBranchProcessCurrentPath(menuPath));
} else {
this.store.dispatch(new ChangeCurrentRoute(menuPath));
}
this.router.navigate([menuPath], { queryParams });
}
routeToMenuBranch(menuPath: string, menuTag: string): void {
this.activeMenu = menuTag;
this.router.navigate([menuPath]);
}
createProcess(menuPath: string) {
const newProcess = <Process>{
id: 1,
name: 'Vorgang 1',
currentRoute: menuPath,
};
this.store.dispatch(new AddProcess(newProcess));
}
routeFromPath(path: string) {
if (path) {
return path.substring(1, path.length);
}
}
nameFromPath(path: string) {
switch (path) {
case '/product/search':
this.breadCrumbId = 'product';
return 'Artikelsuche';
case '/customer/search':
this.breadCrumbId = 'customer';
return 'Kundensuche';
case '/shelf/search':
this.breadCrumbId = 'shelf';
return 'Warenausgabe';
case '/branch/main':
this.breadCrumbId = 'goodsin';
return 'Abholfach';
case '/remission/create':
this.breadCrumbId = 'remission';
return 'Remission';
case '/task-calendar/calendar':
this.breadCrumbId = 'taskCalendar';
return 'Tätigkeitskalendar';
default:
this.breadCrumbId = 'product';
return 'Artikelsuche';
}
}
ngOnInit() {
this.processes$.pipe(takeUntil(this.destroy$)).subscribe((data: Process[]) => (this.processes = data));
this.applicationService.section$.pipe(takeUntil(this.destroy$), distinctUntilChanged()).subscribe((section) => {
this.module = section === 'branch' ? ModuleSwitcher.Branch : ModuleSwitcher.Customer;
});
}
ngOnDestroy() {
this.destroy$.next();
}
private navigateToNewRemissionList() {
this.store.dispatch(new ResetRemissionState());
const path = '/remission/create';
this.store.dispatch(
new ResetBreadcrumbsTo(
<Breadcrumb>{
name: 'Remission',
path: path,
},
'remission',
true
)
);
this.store.dispatch(new SetBranchProcessCurrentPath(path));
this.router.navigate([path]);
}
}

View File

@@ -1,26 +0,0 @@
<app-modal id="printer-modal">
<div class="printer-modal">
<div class="header">
<h1>Wählen Sie einen Drucker aus</h1>
<lib-icon (click)="closeModal()" height="21px" class="close-icon" name="close" alt="close"></lib-icon>
</div>
<span *ngIf="error && errorMessage" class="error-message isa-font-color-warning">{{ errorMessage }}</span>
<ng-container *ngIf="!error">
<div class="body">
<app-dropdown
[load]="true"
(valueChanges)="printerSelected($event)"
[options]="options"
[selected]="selected"
[loading]="!loaded"
[showFull]="true"
></app-dropdown>
</div>
<div class="actions">
<div>
<app-button [primary]="true" [load]="true" [disabled]="!loaded" (action)="emitPrint()" #printBtn>Drucken </app-button>
</div>
</div>
</ng-container>
</div>
</app-modal>

View File

@@ -1,48 +0,0 @@
.printer-modal {
font-family: 'Open Sans';
line-height: 21px;
margin: 16px 0;
display: flex;
align-items: center;
flex-direction: column;
min-height: 185px;
h1 {
font-size: 20px;
font-weight: bold;
text-align: center;
margin-top: 20px;
}
.header {
.close-icon {
position: absolute;
top: 25px;
right: 25px;
height: 21px;
}
.close-icon:hover {
cursor: pointer;
}
}
.actions {
margin-top: 35px;
margin-bottom: 30px;
display: flex;
justify-content: center;
align-items: center;
}
.error-message {
padding: 1rem 2rem;
text-align: center;
font-size: 16px;
font-weight: 600;
}
}
.body {
padding-top: 30px;
}

View File

@@ -1,22 +0,0 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { PrinterSelectionComponent } from './printer-selection.component';
describe('PrinterSelectionComponent', () => {
let component: PrinterSelectionComponent;
let fixture: ComponentFixture<PrinterSelectionComponent>;
beforeEach(
waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [PrinterSelectionComponent],
}).compileComponents();
})
);
beforeEach(() => {
fixture = TestBed.createComponent(PrinterSelectionComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
});

View File

@@ -1,115 +0,0 @@
import { Component, OnInit, Output, EventEmitter, OnDestroy, ViewChild, ChangeDetectorRef } from '@angular/core';
import { PrinterService } from '../../core/services/printer.service';
import { ModalService, ButtonComponent } from '@libs/ui';
import { Subject } from 'rxjs';
import { takeUntil, map, tap, delay } from 'rxjs/operators';
import { Printer } from '../../core/models/printer.model';
import { PRINT_ERROR_MSG } from './printer-selection.constants';
@Component({
selector: 'app-printer-selection',
templateUrl: './printer-selection.component.html',
styleUrls: ['./printer-selection.component.scss'],
})
export class PrinterSelectionComponent implements OnInit, OnDestroy {
id = 'printer-modal';
options: string[] | number[];
selected: string | number;
selectedPrinterValue: string;
printers: { key: string; text: string; selected: boolean }[] = [];
destroy$ = new Subject();
error = false;
errorMessage = PRINT_ERROR_MSG;
loaded = false;
printingRequested = false;
submited = false;
@Output() print: EventEmitter<string> = new EventEmitter();
@Output() closed = new EventEmitter();
@ViewChild('printBtn') printBtn: ButtonComponent;
constructor(private printerService: PrinterService, private modalService: ModalService, private cdr: ChangeDetectorRef) {}
ngOnInit() {}
printerSelected(value: string | number) {
this.selected = value;
this.selectedPrinterValue = this.printers.find((t) => t.text === this.selected).key;
}
emitPrint() {
if (this.loaded) {
this.print.emit(this.selectedPrinterValue);
this.printBtn.startLoading();
} else {
this.printingRequested = true;
this.printBtn.startLoading();
}
}
loadPrinters() {
this.loaded = false;
this.error = false;
this.printerService
.getAvailablePrinters()
.pipe(takeUntil(this.destroy$))
.subscribe((response) => {
if ((response as { error: string }).error) {
const errorResponse = response as { error: string };
this.error = true;
this.errorMessage = errorResponse.error;
return;
}
const result = response as Printer[];
this.printers = result.map((t) => {
return { key: t.key, text: t.description, selected: t.selected };
});
const selectedPrinter = this.printers.find((printer) => printer.selected);
this.options = this.printers.map((t) => t.text);
this.selectedPrinterValue = selectedPrinter ? selectedPrinter.key : this.printers[0].key;
this.selected = selectedPrinter ? selectedPrinter.text : this.options[0];
this.error = false;
this.loaded = true;
if (this.printBtn) {
this.printBtn.stopLoading();
}
if (!this.selected) {
this.error = true;
this.errorMessage = 'No available printers';
}
this.cdr.markForCheck();
});
}
openDialog() {
this.loadPrinters();
this.modalService.open(this.id);
}
closeModal() {
this.modalService.close(this.id);
if (this.printBtn) {
this.printBtn.stopLoading();
}
this.closed.emit();
}
setError(errorMessage?: string) {
this.error = true;
if (!!errorMessage) {
this.errorMessage = errorMessage;
}
}
ngOnDestroy() {
if (this.printBtn) {
this.printBtn.stopLoading();
}
this.destroy$.next();
}
contentLoaded() {
if (this.printingRequested) {
this.print.emit(this.selectedPrinterValue);
}
}
}

View File

@@ -1 +0,0 @@
export const PRINT_ERROR_MSG = 'Der Druckauftrag konnte nicht ausgeführt werden.';

View File

@@ -1,11 +0,0 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { PrinterSelectionComponent } from './printer-selection.component';
import { IconModule, DropdownModule, ModalModule, ButtonModule } from '@libs/ui';
@NgModule({
declarations: [PrinterSelectionComponent],
imports: [CommonModule, IconModule, DropdownModule, ModalModule, ButtonModule],
exports: [PrinterSelectionComponent],
})
export class PrinterSelectionModule {}

View File

@@ -1,8 +0,0 @@
import { trigger, transition, animate, style } from '@angular/animations';
export const addAnimation = trigger('add', [
transition('void => true', [
style({ opacity: 0, transform: 'translateX(200%)' }),
animate('0.3s ease-out', style({ opacity: 1, transform: 'translateX(0%)' })),
]),
]);

View File

@@ -1,46 +0,0 @@
<div class="grid-container">
<div class="align-left">
<div class="back-arrow-container" *ngIf="showBackContainer && !isIPad" (mouseover)="showBackIcon()" (mouseleave)="hideBackIcon()">
<lib-icon class="icon" width="118px" height="50px" name="tab_Arrow" type="png" (click)="scrollBack()" *ngIf="showBack"></lib-icon>
</div>
<div class="process-grid-container" [ngClass]="{ adding: showAddingPadding }" *ngIf="module === 0" #panel>
<app-process-tab
[id]="process.id"
style="display: inline-block; height: 100%;"
*ngFor="let process of processes; let last = last"
[module]="module"
[last]="last"
[process]="process"
[@add]="process.new"
></app-process-tab>
</div>
<div class="process-grid-container" *ngIf="module === 1" #panel>
<app-process-tab
id="{{ branchProcess.id }}"
style="display: inline-block; height: 100%;"
[module]="module"
[process]="branchProcess"
></app-process-tab>
</div>
<div
class="foward-arrow-container"
*ngIf="showFowardContainer && !isIPad"
(mouseover)="showFowardIcon()"
(mouseleave)="hideFowardIcon()"
>
<lib-icon class="icon" width="118px" height="50px" name="tab_Arrow" type="png" (click)="scrollFoward()" *ngIf="showNext"></lib-icon>
</div>
</div>
<div class="align-right">
<div class="grid-container-fix-width-last-col">
<div class="align-right add-process-label">
<span *ngIf="processes && processes.length === 0 && module === 0" class="process-span" (click)="addProcess()">{{
startProcessLabel
}}</span>
</div>
<app-button *ngIf="module === 0" (action)="addProcess()" class="add-process" [type]="'small'">
<lib-icon width="34px" height="36px" name="add-red"></lib-icon>
</app-button>
</div>
</div>
</div>

View File

@@ -1,113 +0,0 @@
@import 'variables';
.grid-container {
@apply grid grid-flow-col;
grid-template-columns: 1fr auto;
}
.align-left {
@apply grid grid-flow-col;
grid-template-columns: auto 1fr auto;
}
.grid-container-fix-width-last-col {
display: grid;
grid-template-columns: auto;
padding-top: 7px;
}
.process-span {
color: $hima-color-red;
font-weight: bold;
}
.process-grid-container {
white-space: nowrap;
padding-top: 13px;
padding-right: 25px;
overflow-y: scroll;
scroll-behavior: smooth;
display: block;
}
.adding {
padding-right: 200px;
}
.add-process-label {
position: absolute;
top: 99px;
right: 63px;
cursor: pointer;
}
.back-arrow-container {
width: 118px;
height: 50px;
display: flex;
justify-content: center;
align-items: center;
position: absolute;
z-index: 50;
top: 83px;
cursor: pointer;
transform: rotate(180deg);
}
.foward-arrow-container {
width: 118px;
height: 50px;
display: flex;
justify-content: center;
align-items: center;
position: absolute;
z-index: 50;
top: 87px;
cursor: pointer;
}
.placeholder-tab {
width: 300px;
height: 42px;
display: inline-block;
height: 100%;
}
/*
##Device = Big Desktops
*/
@media (min-width: 1281px) {
.grid-container {
grid-template-columns: 93% auto;
}
.foward-arrow-container {
right: 80px;
}
}
/*
##Device = Laptops, Desktops, Ipad pro
*/
@media (min-width: 1025px) and (max-width: 1280px) {
.grid-container {
grid-template-columns: 93% auto;
}
.foward-arrow-container {
right: 80px;
}
}
/*
##Device = Tablets, Ipads
*/
@media (min-width: 768px) and (max-width: 1024px) {
.grid-container {
grid-template-columns: 685px auto;
}
.foward-arrow-container {
right: 68px;
}
}

View File

@@ -1,22 +0,0 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { ProcessHeaderComponent } from './process-header.component';
describe('ProcessHeaderComponent', () => {
let component: ProcessHeaderComponent;
let fixture: ComponentFixture<ProcessHeaderComponent>;
beforeEach(
waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [ProcessHeaderComponent],
}).compileComponents();
})
);
beforeEach(() => {
fixture = TestBed.createComponent(ProcessHeaderComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
});

View File

@@ -1,248 +0,0 @@
import { Component, OnInit, OnDestroy, ElementRef, ViewChild, ChangeDetectorRef, ChangeDetectionStrategy, Input } from '@angular/core';
import { Process } from '../../core/models/process.model';
import { Observable, Subject } from 'rxjs';
import { Breadcrumb } from '../../core/models/breadcrumb.model';
import { Store, Select } from '@ngxs/store';
import { AddProcess } from '../../core/store/actions/process.actions';
import { addAnimation } from './add.animation';
import { Router } from '@angular/router';
import { AddBreadcrumb } from '../../core/store/actions/breadcrumb.actions';
import { takeUntil, switchMap, distinctUntilChanged } from 'rxjs/operators';
import { ProcessSelectors } from '../../core/store/selectors/process.selectors';
import { WindowRef } from '../../core/services/window-ref.service';
import { ModuleSwitcher } from '../../core/models/app-switcher.enum';
import { ApplicationService } from '@core/application';
@Component({
selector: 'app-process-header',
templateUrl: './process-header.component.html',
styleUrls: ['./process-header.component.scss'],
animations: [addAnimation],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ProcessHeaderComponent implements OnInit, OnDestroy {
startProcessLabel = 'VORGANG STARTEN';
@ViewChild('panel', { read: ElementRef }) public panel: ElementRef<any>;
@Select(ProcessSelectors.getProcesses) process$: Observable<Process[]>;
@Select(ProcessSelectors.getCurrentProcess) selectedProcess$: Observable<Process>;
module: ModuleSwitcher;
branchProcess = <Process>{
id: 1,
name: 'Wareneingang',
};
processes: Process[] = [];
destroy$ = new Subject();
showNext = false;
showBack = false;
showFowardContainer = false;
showBackContainer = false;
offset: number;
selectedProcessId: string;
selectedProcess: any;
isIPad = false;
showAddingPadding = false;
private iPadDetected = false;
private iPadEventRecieved = false;
constructor(
private store: Store,
private router: Router,
private cdr: ChangeDetectorRef,
private windowRef: WindowRef,
private applicationService: ApplicationService
) {}
addProcess() {
const itemNo = !this.processes
? 0
: this.processes && this.processes.length === 0
? 1
: this.processes[this.processes.length - 1].id + 1;
const newProcess = <Process>{
id: itemNo,
new: true,
name: `Vorgang ${itemNo}`,
currentRoute: '/product/search',
};
if (itemNo >= 4) {
this.initializeAnimation(itemNo);
}
this.store
.dispatch(new AddProcess(newProcess))
.toPromise()
.then(() => {
this.store.dispatch(
new AddBreadcrumb(
<Breadcrumb>{
name: 'Artikelsuche',
path: '/product/search',
},
'product',
true
)
);
this.router.navigate(['/product/search']);
});
this.applicationService.setActivatedProcessId(newProcess.id);
}
ngOnInit() {
this.isIPad = this.isIPadEnv();
this.selectedProcess$
.pipe(
switchMap((proc: Process) => {
if (proc) {
this.selectedProcessId = proc.id + '';
}
return this.process$;
}),
takeUntil(this.destroy$),
distinctUntilChanged((prev, curr) => this.processComperer(prev, curr))
)
.subscribe(
(data) => {
this.processes = data;
this.cdr.detectChanges();
if (this.panel?.nativeElement?.children.length > 0) {
this.scrollableArrows();
}
},
(err) => console.error(err)
);
this.applicationService.section$.pipe(distinctUntilChanged(), takeUntil(this.destroy$)).subscribe((section) => {
this.module = section === 'branch' ? ModuleSwitcher.Branch : ModuleSwitcher.Customer;
this.cdr.detectChanges();
});
}
private initializeAnimation(processCount: number) {
this.showAddingPadding = true;
this.cdr.detectChanges();
setTimeout(() => {
this.panel.nativeElement.scrollTo({ left: this.panel.nativeElement.scrollLeft + 300 * processCount, behavior: 'smooth' });
this.cdr.detectChanges();
}, 100);
}
processComperer(prev: Process[], curr: Process[]) {
try {
if (!prev || !curr) {
return false;
}
if (prev.length !== curr.length) {
return false;
}
if (prev.length === 0 || curr.length === 0) {
return false;
}
const prevStr = JSON.stringify(
prev.map((t) => {
return { new: t.new, name: t.name };
})
);
const currStr = JSON.stringify(
curr.map((t) => {
return { new: t.new, name: t.name };
})
);
const status = prevStr === currStr;
return status;
} catch (error) {
return false;
}
}
ngOnDestroy() {
this.destroy$.next();
}
isIPadEnv() {
const navigator = this.windowRef.nativeWindow.navigator as Navigator;
const standalone = (navigator as any).standalone,
userAgent = navigator.userAgent.toLowerCase(),
ios = /iphone|ipod|ipad/.test(userAgent);
this.iPadDetected = ios && !standalone;
return this.iPadDetected || this.iPadEventRecieved;
}
scrollableArrows() {
this.offset = Math.ceil(this.panel.nativeElement.clientWidth / 2);
let selected: any;
for (let _i = 0; _i < this.panel.nativeElement.children.length; _i++) {
const proc = this.panel.nativeElement.children[_i];
if (proc.id === this.selectedProcessId) {
selected = proc;
this.selectedProcess = proc;
}
}
const lastProcess = this.panel.nativeElement.children[this.panel.nativeElement.children.length - 1];
if (selected) {
const clientWidth = this.panel.nativeElement.clientWidth;
this.showAddingPadding = false;
this.cdr.detectChanges();
const selecetedPos = selected.offsetLeft + selected.offsetWidth;
const lastPos = lastProcess.offsetLeft + lastProcess.offsetWidth;
this.showFowardContainer =
(selecetedPos + this.offset * 2 > clientWidth || selecetedPos + this.offset < lastPos) &&
lastPos > clientWidth &&
lastPos > this.panel.nativeElement.scrollWidth;
// This number is from testing > 643
this.showBackContainer = selecetedPos > 643;
}
this.cdr.detectChanges();
}
scrollBack() {
const moveCalc = this.panel.nativeElement.scrollLeft - this.offset;
this.panel.nativeElement.scrollTo({ left: moveCalc, behavior: 'smooth' });
this.cdr.detectChanges();
this.showBackContainer = this.panel.nativeElement.scrollLeft - this.offset > 0;
this.showBack = false;
this.showFowardContainer = this.panel.nativeElement.scrollLeft < this.panel.nativeElement.scrollWidth;
this.cdr.detectChanges();
}
scrollFoward() {
const moveCalc = this.panel.nativeElement.scrollLeft + this.offset;
this.panel.nativeElement.scrollTo({ left: moveCalc, behavior: 'smooth' });
this.cdr.detectChanges();
this.showFowardContainer = moveCalc + this.offset * 2 < this.panel.nativeElement.scrollWidth;
this.showNext = false;
// If clicked next, then back button is always there
this.showBackContainer = true;
this.cdr.detectChanges();
}
showFowardIcon() {
this.showNext = true;
}
hideFowardIcon() {
this.showNext = false;
}
showBackIcon() {
this.showBack = true;
}
hideBackIcon() {
this.showBack = false;
}
}

View File

@@ -1,58 +0,0 @@
<!-- Customer module process -->
<div class="grid-item" id="{{ process.id }}" *ngIf="module === 0" [ngClass]="{ last: last }">
<div class="grid-container" [ngClass]="{ 'selected-process': process.id === (currentProcessId$ | async) }">
<div class="process-name-container pt-3" (click)="selectProcess(process)">
<span class="process-name">{{ process.name }}</span>
</div>
<ng-container *ngIf="{ length: cartCount$ | async }; let cartCount">
<div
[class.items-in-cart]="cartCount.length > 0"
class="cart-container"
[ngClass]="{ download: cartBackgroundForDownload }"
(click)="openCart(process)"
>
<lib-icon
mt="12px"
ml="15px"
width="17px"
height="16px"
name="Shopping_Cart"
*ngIf="cartCount.length === 0 && !cartBackgroundForDownload && process.id === (currentProcessId$ | async)"
class="process-cart-icon"
></lib-icon>
<lib-icon
mt="12px"
ml="15px"
width="17px"
height="16px"
name="Shopping_Cart_Inactive"
*ngIf="cartCount.length === 0 && !cartBackgroundForDownload && process.id !== (currentProcessId$ | async)"
class="process-cart-icon"
></lib-icon>
<lib-icon
mt="12px"
ml="15px"
width="17px"
height="16px"
name="shopping_cart_white"
*ngIf="cartCount.length > 0 || cartBackgroundForDownload"
class="process-cart-icon"
></lib-icon>
<div [@cartnumber]="cartanimation" class="pt-3 process-cart-number-container">
<span
[class.items-in-cart]="cartCount.length > 0"
class="process-cart-number"
[ngClass]="{ 'number-download': cartBackgroundForDownload }"
>{{ cartCount.length }}</span
>
</div>
</div>
</ng-container>
<div *ngIf="process.id === (currentProcessId$ | async)">
<a (click)="openDeleteConfirmationDialog()">
<lib-icon class="process-delete-icon" name="close" width="15px" height="15px" mt="12px"></lib-icon>
</a>
</div>
</div>
<app-process-delete-dialog #deleteporcessdialog (deleted)="deleteProcess($event)" [process]="process"></app-process-delete-dialog>
</div>

View File

@@ -1,133 +0,0 @@
@import 'variables';
.grid-container {
display: flex;
flex-direction: row;
height: 42px;
border-bottom: 3px solid #fff;
& > .cart-container {
display: flex;
flex-direction: row;
height: 38px;
background-color: #e6eff9;
border-radius: 25px;
z-index: 2;
position: relative;
bottom: 1px;
padding-right: 20px;
cursor: pointer;
}
}
.download {
background-color: $hima-download-cart-color !important;
}
.process-name-container {
cursor: pointer;
max-width: 100px;
}
.process-name-container-branch {
cursor: pointer;
max-width: 200px;
}
.process-name {
font-size: 18px;
font-weight: bold;
color: #557596;
// opacity: 0.3;
margin-top: 5px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-right: 10px;
display: block;
}
.process-name-branch {
font-size: 18px;
font-weight: bold;
color: #596470;
opacity: 0.3;
margin-top: 5px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-right: 10px;
display: block;
}
.process-cart-number {
font-size: 18px;
font-weight: bold;
color: #557596;
// opacity: 0.3;
position: relative;
top: 4px;
left: 8px;
}
.number-download {
color: #ffffff !important;
}
// .process-cart-icon {
// opacity: 0.3;
// }
.selected-process {
border-bottom: 3px solid $hima-color-red;
.process-name {
color: $color-active;
opacity: 1;
}
.process-name-branch {
color: #596470;
opacity: 1;
}
.process-cart-number {
color: $color-active;
opacity: 1;
}
.process-cart-icon {
opacity: 1;
}
}
.process-delete-icon {
margin-left: 10px;
cursor: pointer;
}
.process-cart-icon-numbered {
margin-left: 3px;
}
.process-cart-number-container {
margin-left: 1px;
}
.grid-item {
display: inline-block;
padding-right: 30px;
height: 100%;
}
.last {
padding-right: 0px;
}
.items-in-cart {
@apply bg-active-customer !important;
color: #fff !important;
}

View File

@@ -1,22 +0,0 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { ProcessTabComponent } from './process-tab.component';
describe('ProcessTabComponent', () => {
let component: ProcessTabComponent;
let fixture: ComponentFixture<ProcessTabComponent>;
beforeEach(
waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [ProcessTabComponent],
}).compileComponents();
})
);
beforeEach(() => {
fixture = TestBed.createComponent(ProcessTabComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
});

View File

@@ -1,182 +0,0 @@
import {
Component,
OnInit,
Input,
ViewChild,
OnDestroy,
ChangeDetectionStrategy,
ChangeDetectorRef,
AfterViewInit,
ViewRef,
} from '@angular/core';
import { Process } from '../../core/models/process.model';
import { Store, Select } from '@ngxs/store';
import {
DeleteProcess,
PreventProductLoad,
ChangeCurrentRoute,
SetProcessNewStatusToFalse,
} from '../../core/store/actions/process.actions';
import { Breadcrumb } from '../../core/models/breadcrumb.model';
import { ProcessDeleteDialogComponent } from '../../modules/process/components/process-delete-dialog/process-delete-dialog.component';
import { Observable, Subject } from 'rxjs';
import { Notify } from '../../core/store/actions/notifier.actions';
import { AddBreadcrumb } from '../../core/store/actions/breadcrumb.actions';
import { map, switchMap, take, withLatestFrom, first } from 'rxjs/operators';
import { trigger, transition, animate, style, keyframes } from '@angular/animations';
import { AppState } from '../../core/store/state/app.state';
import { AppSetCurrentProcess } from '../../core/store/actions/app.actions';
import { ProcessSelectors } from '../../core/store/selectors/process.selectors';
import { ModuleSwitcher } from '../../core/models/app-switcher.enum';
import { USER_FORM_STATE_KEY, USER_EXTRAS_FORM_STATE_KEY, USER_ERRORS_FORM_STATE_KEY } from '../../core/utils/app.constants';
import { DeleteFormState } from '../../core/store/actions/forms.actions';
import { ApplicationService } from '@core/application';
import { DomainCheckoutService } from '@domain/checkout';
import { BreadcrumbService } from '@core/breadcrumb';
import { Router } from '@angular/router';
@Component({
selector: 'app-process-tab',
templateUrl: './process-tab.component.html',
styleUrls: ['./process-tab.component.scss'],
animations: [
trigger('cartnumber', [
transition('* => delete', [
animate(
1000,
keyframes([
style({ transform: 'translateY(-60%)', opacity: 0, offset: 0 }),
style({ transform: 'translateY(10%)', opacity: 1, offset: 0.4 }),
style({ transform: 'translateY(0%)', opacity: 1, offset: 0.6 }),
style({ transform: 'translateY(5%)', opacity: 1, offset: 0.8 }),
style({ transform: 'translateY(0%)', opacity: 1, offset: 0.99 }),
])
),
]),
]),
],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ProcessTabComponent implements OnInit, OnDestroy, AfterViewInit {
@Input() process: Process;
@Input() processes: Array<Process>;
@Input() module: ModuleSwitcher;
@Input() last = false;
@Select(ProcessSelectors.getProcesses) procecesses$: Observable<Process[]>;
@Select(AppState.getCurrentProcessId) currentProcessId$: Observable<number>;
destroy$ = new Subject();
@ViewChild('deleteporcessdialog')
processDeleteDialogComponent: ProcessDeleteDialogComponent;
cartCount$: Observable<number>;
cartanimation = '';
cartBackgroundForDownload = false;
constructor(
private store: Store,
private router: Router,
private cdr: ChangeDetectorRef,
private applicationService: ApplicationService,
private domainCheckoutService: DomainCheckoutService,
private breadcrumb: BreadcrumbService
) {}
isProcessFinished = this.router.url === '/cart/confirmation';
async deleteProcess(process: Process) {
this.store.dispatch(new DeleteProcess(process));
this.store.dispatch(new Notify(1));
const processes = this.store.selectSnapshot(ProcessSelectors.getProcesses) as Process[];
if (processes) {
this.processDeleteCleanUp();
if (processes.length < 1) {
this.router.navigate(['/dashboard']);
} else {
const newSelectedProcess = processes[processes.length - 1];
const breadcrumbs = await this.breadcrumb.getBreadcrumbByKey$(newSelectedProcess.id).pipe(first()).toPromise();
if (!breadcrumbs) return;
const breadcrumb = breadcrumbs.sort((a, b) => b.timestamp - a.timestamp)[0];
if (!breadcrumb?.path) return;
this.router.navigate([breadcrumb.path], { queryParams: breadcrumb.params });
}
}
}
openDeleteConfirmationDialog() {
if (this.process.closeDirectlyTab || this.isProcessFinished) {
this.deleteProcess(this.process);
} else {
this.processDeleteDialogComponent.openDialog();
}
}
selectProcess(process: Process): void {
this.store
.dispatch(new AppSetCurrentProcess(process.id))
.pipe(
take(1),
switchMap(() => this.store.dispatch(new PreventProductLoad())),
switchMap(() => this.store.dispatch(new Notify(process.id))),
withLatestFrom(this.breadcrumb.getBreadcrumbByKey$(process.id))
)
.subscribe(([_, breadcrumbs]) => {
if (!breadcrumbs) return;
const breadcrumb = breadcrumbs.sort((a, b) => b.timestamp - a.timestamp)[0];
if (!breadcrumb?.path) return;
this.router.navigate([breadcrumb.path], { queryParams: breadcrumb.params });
});
this.applicationService.setActivatedProcessId(process.id);
}
ngOnInit() {
this.cartCount$ = this.domainCheckoutService.getShoppingCart({ processId: this.process.id }).pipe(
map((response) => {
if (response?.items?.length === undefined) {
return 0;
} else {
return response?.items?.length;
}
})
);
}
ngAfterViewInit() {
setTimeout(() => {
this.store.dispatch(new SetProcessNewStatusToFalse());
}, 400);
}
ngOnDestroy() {
this.destroy$.next();
}
openCart(process: Process) {
this.selectProcess(process);
const newBread: Breadcrumb = {
name: 'Warenkorb',
path: '/cart/review',
};
this.store.dispatch(new AddBreadcrumb(newBread, 'shoppingCart'));
this.store.dispatch(new ChangeCurrentRoute('/cart/review'));
this.router.navigate(['/cart/review']);
}
private processDeleteCleanUp() {
this.store.dispatch(new DeleteFormState(USER_FORM_STATE_KEY));
this.store.dispatch(new DeleteFormState(USER_EXTRAS_FORM_STATE_KEY));
this.store.dispatch(new DeleteFormState(USER_ERRORS_FORM_STATE_KEY));
}
detectChanges() {
setTimeout(() => {
if (this.cdr !== null && this.cdr !== undefined && !(this.cdr as ViewRef).destroyed) {
this.cdr.detectChanges();
}
}, 0);
}
}

View File

@@ -1,25 +0,0 @@
<app-modal id="error-modal" higerOder="true">
<div class="error-modal">
<div class="header">
<h1>{{ title }}</h1>
<lib-icon (click)="closeErrorModal()" height="21px" class="close-icon" name="close" alt="close"></lib-icon>
</div>
<div class="body">
<span class="message" *ngIf="!hasInvalidAttributes" [innerHTML]="message"></span>
<ng-container *ngIf="hasInvalidAttributes">
<span *ngFor="let propertie of invalidAttributes">
{{ propertie.key + ' => ' + propertie.value }}
</span>
</ng-container>
</div>
<div class="actions">
<div class="align-right">
<ng-container *ngIf="!logout">
<app-button *ngIf="!safeMode" (action)="resetState()">App neu starten</app-button>
<app-button [primary]="true" (action)="closeErrorModal()">Erneut versuchen</app-button></ng-container
>
<ng-container *ngIf="logout"> <app-button [primary]="true" (action)="closeErrorModal()">Erneut anmelden</app-button></ng-container>
</div>
</div>
</div>
</app-modal>

View File

@@ -1,52 +0,0 @@
.error-modal {
font-family: 'Open Sans';
line-height: 21px;
margin: 16px 0;
display: flex;
align-items: center;
flex-direction: column;
min-height: 185px;
h1 {
font-size: 20px;
font-weight: bold;
text-align: center;
margin-top: 20px;
margin-bottom: 15px;
line-height: 24px;
}
.message {
font-size: 16px;
line-height: 21px;
font-weight: 600;
text-align: center;
}
.header {
.close-icon {
position: absolute;
top: 25px;
right: 25px;
height: 21px;
}
.close-icon:hover {
cursor: pointer;
}
}
.actions {
margin-top: 36px;
margin-bottom: 24px;
display: flex;
justify-content: center;
align-items: center;
}
}
.body {
padding: 0px 40px 0px 60px;
display: flex;
flex-direction: column;
}

View File

@@ -1,22 +0,0 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { ErrorComponent } from './error.component';
describe('ErrorComponent', () => {
let component: ErrorComponent;
let fixture: ComponentFixture<ErrorComponent>;
beforeEach(
waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [ErrorComponent],
}).compileComponents();
})
);
beforeEach(() => {
fixture = TestBed.createComponent(ErrorComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
});

View File

@@ -1,92 +0,0 @@
import { Component, OnInit, OnDestroy } from '@angular/core';
import { ModalService } from '@libs/ui';
import { UserStateService } from '../../services/user-state.service';
import { Subject } from 'rxjs';
import { Router } from '@angular/router';
import { SsoService } from 'sso';
import { ErrorService } from './error.service';
import { isNullOrUndefined } from '@utils/common';
@Component({
selector: 'app-error',
templateUrl: './error.component.html',
styleUrls: ['./error.component.scss'],
})
export class ErrorComponent implements OnInit, OnDestroy {
id = 'error-modal';
errorOpened = false;
message: string;
destroy$ = new Subject();
logout = false;
safeMode = false;
invalidAttributes: { key: string; value: string }[];
hasInvalidAttributes = false;
safeCodes = [400, 409];
validMessageCodes = [0, 400, 404, 409, 500, 504];
title = 'Irgendwas hat nicht funktioniert…';
constructor(
private modalService: ModalService,
private stateService: UserStateService,
private router: Router,
private ssoService: SsoService,
private errorService: ErrorService
) {}
ngOnInit() {}
openErrorModal({ code, message, invalidProperties, logout }) {
if (!this.errorOpened) {
this.safeMode = this.safeCodes.includes(code);
this.hasInvalidAttributes = !isNullOrUndefined(invalidProperties);
if (this.hasInvalidAttributes) {
this.formatInvalidProperties(invalidProperties);
}
this.title = logout ? 'Willkommen zurück!' : 'Irgendwas hat nicht funktioniert…';
this.message = logout ? 'Sie waren zu lange nicht in der ISA aktiv.<br>Bitte melden Sie sich erneut an.' : '';
if (this.validMessageCodes.includes(code) && !isNullOrUndefined(message)) {
this.message = message;
}
this.errorOpened = true;
this.modalService.open(this.id);
}
this.logout = !this.logout ? logout : this.logout;
}
closeErrorModal() {
if (this.logout) {
this.ssoService.logoff();
return;
}
this.errorOpened = false;
this.errorService.addRecovery(true);
this.modalService.close(this.id);
}
async resetState() {
if (this.logout) {
this.ssoService.logoff();
return;
}
this.modalService.close(this.id);
this.router.navigate(['/dashboard']);
this.stateService.resetState().then(() => {
window.location.reload();
});
this.errorOpened = false;
}
formatInvalidProperties(invalidProperties: string) {
const properties = invalidProperties.replace('{', '').replace('}', '').replace(/"/g, '').split(',');
if (properties && properties.length > 0) {
this.invalidAttributes = properties.map((propertie) => {
const keyValuePropertie = propertie.split(':');
return { key: keyValuePropertie[0], value: keyValuePropertie[1] };
});
}
}
ngOnDestroy(): void {
this.destroy$.next();
this.errorOpened = false;
}
}

View File

@@ -1,11 +0,0 @@
import { NgModule } from '@angular/core';
import { ErrorComponent } from './error.component';
import { CommonModule } from '@angular/common';
import { ModalModule, IconModule, ButtonModule } from '@libs/ui';
@NgModule({
imports: [CommonModule, ModalModule, IconModule, ButtonModule],
declarations: [ErrorComponent],
exports: [ErrorComponent],
})
export class ErrorModule {}

View File

@@ -1,20 +0,0 @@
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class ErrorService {
private errors = new Subject<{ code: number; message: string; invalidProperties: string; logout: boolean }>();
private recovery = new Subject<boolean>();
constructor() {}
public addErrors = (code: number, errors: string, invalidProperties: string, logout = false): void =>
// tslint:disable-next-line: semicolon
this.errors.next({ code: code, message: errors, invalidProperties: invalidProperties, logout: logout });
public getErrors = () => this.errors.asObservable();
public addRecovery = (positive: boolean): void => this.recovery.next(positive);
public recovery$ = () => this.recovery.asObservable();
}

View File

@@ -1,23 +0,0 @@
import { ErrorHandler, Injectable } from '@angular/core';
import { ErrorService } from './component/error.service';
import { LoggingService, LogType } from '../services/logging.service';
import { isWhiteList } from '../utils/http-interceptor-whitelist.utils';
@Injectable()
export class HimaSalesErrorHandler implements ErrorHandler {
constructor(private errorService: ErrorService, private logger: LoggingService) {}
handleError(error: any): void {
if (
// tslint:disable-next-line: quotemark
error.message === "You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable."
) {
return;
} else if (error && error.url && error.status && isWhiteList(error.url, error.status)) {
return;
} else {
this.errorService.addErrors(error.status ? error.status : 0, error.message, undefined);
this.logger.log('HUNGENDUBEL INSTORE MA APP', error, LogType.Error);
}
}
}

View File

@@ -1,51 +0,0 @@
/* import { BasicAuthorizationInterceptor } from './basic-authorization.interceptor';
import { TestBed, getTestBed } from '@angular/core/testing';
import { HttpTestingController, HttpClientTestingModule } from '@angular/common/http/testing';
import { HttpClient, HTTP_INTERCEPTORS } from '@angular/common/http';
xdescribe('BasicAuthorizationInterceptor', () => {
let injector: TestBed;
let httpMock: HttpTestingController;
let httpClient: HttpClient;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [
{
provide: HTTP_INTERCEPTORS,
useFactory: () =>
new BasicAuthorizationInterceptor({
client: 'testclient',
password: 'testpassword',
endpoints: ['https://test1.com']
}),
multi: true
}
]
});
injector = getTestBed();
httpMock = injector.get(HttpTestingController);
httpClient = injector.get(HttpClient);
});
it('should add a basic authorization header', () => {
httpClient.get('https://test1.com/abcdef').subscribe();
const req = httpMock.expectOne('https://test1.com/abcdef');
expect(req.request.headers.has('Authorization')).toBeTruthy();
expect(req.request.headers.get('Authorization')).toBe('Basic dGVzdGNsaWVudDp0ZXN0cGFzc3dvcmQ=');
});
it('should not add a basic authorization header', () => {
httpClient.get('https://test2.com/abcdef').subscribe();
const req = httpMock.expectOne('https://test2.com/abcdef');
expect(req.request.headers.has('Authorization')).toBeFalsy();
});
});
*/

View File

@@ -1,55 +0,0 @@
import { Injectable } from '@angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http';
import { Observable, AsyncSubject, ReplaySubject } from 'rxjs';
import { map, filter, shareReplay, first, flatMap, withLatestFrom, tap } from 'rxjs/operators';
export interface BasicAuthorizationOptions {
client: string;
password: string;
endpoints: string[];
}
export const SKIP_BASIC_AUTHORIZATION_INTERCEPTOR = '';
@Injectable()
export class BasicAuthorizationInterceptor implements HttpInterceptor {
private optionsSub = new ReplaySubject<{ token: string; endpointMatchers: RegExp[] }>();
constructor(options$: Observable<BasicAuthorizationOptions>) {
options$
.pipe(
map((options) => {
const token = btoa(`${options.client}:${options.password}`);
const endpointMatchers: RegExp[] = [];
for (const endpoint of options.endpoints) {
endpointMatchers.push(new RegExp(`^${endpoint}`, 'i'));
}
return { token, endpointMatchers };
})
)
.subscribe(this.optionsSub);
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// let request = req;
if (req.headers.has('SKIP_BASIC_AUTHORIZATION_INTERCEPTOR')) {
return next.handle(req);
}
return this.optionsSub.pipe(
first(),
map((options) => {
let request = req;
if (options.endpointMatchers.find((matcher) => matcher.test(req.url))) {
const headers = req.headers.set('Authorization', `Basic ${options.token}`);
request = req.clone({ headers });
}
return request;
}),
flatMap((request) => next.handle(request))
);
}
}

View File

@@ -1,85 +0,0 @@
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpErrorResponse } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { ErrorService } from '../error/component/error.service';
import { Injectable } from '@angular/core';
import { OAuthErrorEvent } from 'angular-oauth2-oidc';
import { LoggingService, LogType } from '../services/logging.service';
import { isWhiteList } from '../utils/http-interceptor-whitelist.utils';
import { UiMessageModalComponent, UiModalService } from '@ui/modal';
@Injectable()
export class HttpErrorHandlerInterceptor implements HttpInterceptor {
constructor(private errorService: ErrorService, private logger: LoggingService, private modal: UiModalService) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req).pipe(catchError((error: HttpErrorResponse, caught: any) => this.handleError(error)));
}
getInvalidProperties(error: any) {
if (
error &&
this.showInvalidProperties(error.url, error.status) &&
error.error &&
error.error.invalidProperties &&
Object.keys(error.error.invalidProperties) &&
Object.keys(error.error.invalidProperties).length > 0
) {
return JSON.stringify(error.error.invalidProperties);
}
return undefined;
}
getMessage(error: any) {
return error.error && error.error ? error.error.message : undefined;
}
private handleError(error: HttpErrorResponse) {
console.log(error.status);
if (error.status === 0) {
this.modal.open({
content: UiMessageModalComponent,
title: 'Die Netzwerkverbindung wurde unterbrochen',
data: { message: 'Bitte überprüfen Sie Ihre Netzwerkverbindung.' },
});
return throwError(error);
}
if (!isWhiteList(error.url)) {
if (isWhiteList(error.url, error.status)) {
return throwError(error);
}
let errorMessage = '';
this.logger.log('HUGENDUBEL INSTORE MA APP Backend', error, LogType.Error);
if (error.error instanceof OAuthErrorEvent) {
return;
} else if (error.error instanceof ErrorEvent) {
// client-side error
errorMessage = `Client-Side => Error: ${error.error.message}`;
this.errorService.addErrors(error.status, errorMessage, undefined);
} else {
// server-side error
errorMessage = `Server-Side => Error Code: ${error.status}\nMessage: ${error.message}`;
if (error.status === 503 && error.url.includes('/availability/')) {
return throwError(errorMessage);
}
const message = this.getMessage(error);
const invalidProperties = this.getInvalidProperties(error);
const logout = error.status === 401;
this.errorService.addErrors(error.status, message, invalidProperties, logout);
}
return throwError(errorMessage);
} else {
return throwError(error);
}
}
private showInvalidProperties(url: string, code: number = -1) {
const blackList: { url: string; codes: number[] }[] = [{ url: '/order/checkout', codes: [400] }];
return !blackList.find((entry) => !!url.includes(entry.url) && !!entry.codes.includes(code));
}
}

View File

@@ -1,3 +0,0 @@
export * from './basic-authorization.interceptor';
export * from './sso-authorization.interceptor';
export * from './http-error-handler.interceptor';

View File

@@ -1,19 +0,0 @@
import { HttpInterceptor, HttpEvent, HttpRequest, HttpHandler } from '@angular/common/http';
import { Observable } from 'rxjs';
import { SsoService } from 'sso';
import { Injectable } from '@angular/core';
@Injectable()
export class SsoAuthorizationInterceptor implements HttpInterceptor {
constructor(private ssoService: SsoService) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (req.headers.has('SKIP_SSO_AUTHORIZATION_INTERCEPTOR')) {
return next.handle(req);
}
const token = this.ssoService.getToken();
const headers = req.headers.set('Authorization', `Bearer ${token}`);
const request = req.clone({ headers });
return next.handle(request);
}
}

View File

@@ -1,103 +0,0 @@
import { Organisation, User } from '../models/user.model';
import { CustomerMapping } from './customer.mapping';
import { TestBed } from '@angular/core/testing';
import { DatePipe } from '@angular/common';
import { CustomerDTO } from '@swagger/crm';
import { CustomerTypeInternal } from '../models/customer-type.model';
fdescribe('CustomerMapping', () => {
let mapper: CustomerMapping;
const mockUser: User = {
id: 123,
first_name: 'Vorname',
last_name: 'Nachname',
title: 'Dr',
gender: 'Herr',
email: 'test@test.de',
phone_number: '017012345678',
mobile_number: '019998765432',
organisation: {
name: 'Mock Org',
department: 'Mock Dept.',
vatId: '123',
},
delivery_addres: {
id: 456,
first_name: 'Vorname Addresse',
last_name: 'Nachname Addresse',
street: 'Test Strasse',
streetNo: 12,
zip: '80636',
city: 'Munich',
country: 'Deutschland',
},
};
beforeEach(() => {
TestBed.configureTestingModule({
providers: [DatePipe, CustomerMapping],
});
mapper = TestBed.inject(CustomerMapping);
});
it('should be created', () => {
expect(mapper instanceof CustomerMapping).toBeTruthy();
});
describe('fromOrganisationToOrganisationDTO', () => {
const mockOrganisation: Organisation = mockUser.organisation;
it('should map the provided organisation (Organisation) to OrganisationDTO', () => {
const result = mapper.fromOrganisationToOrganisationDTO(mockOrganisation);
expect(result.vatId).toEqual(mockOrganisation.vatId);
expect(result.department).toEqual(mockOrganisation.department);
expect(result.name).toEqual(mockOrganisation.name);
});
});
describe('fromUserToCommunicationDetails', () => {
it('should map the provided user (User) to communication details (CommunicationDetailsDTO)', () => {
const result = mapper.fromUserToCommunicationDetails(mockUser);
expect(result.phone).toEqual(mockUser.phone_number);
expect(result.mobile).toEqual(mockUser.mobile_number);
expect(result.email).toEqual(mockUser.email);
});
});
describe('fromUserToShippingAddressDTO', () => {
it('should map the provided user (User) to shipping address (ShippingAddressDTO)', () => {
const result = mapper.fromUserToShippingAddressDTO(mockUser);
expect(result.title).toEqual(mockUser.delivery_addres.title);
expect(result.gender).toEqual(2);
expect(result.firstName).toEqual(mockUser.delivery_addres.first_name);
expect(result.lastName).toEqual(mockUser.delivery_addres.last_name);
expect(result.source).toEqual(mockUser.delivery_addres.id);
});
});
describe('customerDTOtoUser', () => {
const mockCustomer: CustomerDTO = { id: 123, customerType: 16 };
it('should return a User with customerType property set to B2B', () => {
const result = mapper.customerDTOtoUser(mockCustomer);
expect(result.customerType).toBe(CustomerTypeInternal.B2B);
});
});
describe('customerInfoDTOtoUser', () => {
const mockCustomer: CustomerDTO = { id: 123, customerType: 8 };
it('should return a User with customerType property set to B2C', () => {
const result = mapper.customerInfoDTOtoUser(mockCustomer);
expect(result.customerType).toBe(CustomerTypeInternal.B2C);
});
});
});

View File

@@ -1,818 +0,0 @@
import { Injectable } from '@angular/core';
import { User, Address, Features, Organisation } from '../models/user.model';
import {
Gender,
AddressDTO,
EntityDTOContainerOfShippingAddressDTO,
OrganisationDTO,
CommunicationDetailsDTO,
AssignedPayerDTO,
CustomerInfoDTO,
PayerDTO,
CustomerDTO,
ShippingAddressDTO,
CustomerType,
} from '@swagger/crm';
import { NotificationChannels } from '../models/notification-channels.enum';
import { EntityDTOContainerOfBranchDTO, ShippingAddressDTO as CheckoutShippingAddressDTO } from '@swagger/checkout';
import { DatePipe } from '@angular/common';
import { BACKEND_API_TIMESTAMP_FORMAT } from '../utils/app.formats';
import { KeyValueDTOOfStringAndString } from '@swagger/cat';
import { CustomerTypeInternal } from '../models/customer-type.model';
import { isNullOrUndefined } from '@utils/common';
@Injectable({ providedIn: 'root' })
export class CustomerMapping {
constructor(private datePipe: DatePipe) {}
fromUser(customer: User): CustomerDTO {
if (isNullOrUndefined(customer)) {
throw new Error('argument customer:User is null or undefined.');
}
let gender: Gender = 0;
let address: AddressDTO = {};
const addresses: EntityDTOContainerOfShippingAddressDTO[] = [];
let organisation: OrganisationDTO = {};
let communicationDetails: CommunicationDetailsDTO = {};
const notificationChannels = this.getNotificationChannels(customer);
const invoiceAdresses: AssignedPayerDTO[] = [];
const shippingAddresses: EntityDTOContainerOfShippingAddressDTO[] = [];
const features: Array<KeyValueDTOOfStringAndString> = [];
let isGuestAccount: boolean;
let hasOnlineAccount: boolean;
if (customer.mobile_number || customer.phone_number || customer.email) {
communicationDetails = {
email: customer.email ? customer.email : null,
mobile: customer.mobile_number ? customer.mobile_number : null,
phone: customer.phone_number ? customer.phone_number : null,
fax: null,
};
}
if (customer.gender) {
gender = this.getGender(customer.gender);
}
if (customer.delivery_addres) {
address = {
city: customer.delivery_addres.city,
country: customer.delivery_addres.country,
zipCode: customer.delivery_addres.zip + '',
street: customer.delivery_addres.street,
streetNumber: customer.delivery_addres.streetNo + '',
info: customer.delivery_addres.note ? customer.delivery_addres.note : null,
};
}
if (customer.invoice_address) {
address = {
city: customer.invoice_address.city,
country: customer.invoice_address.country,
zipCode: customer.invoice_address.zip + '',
street: customer.invoice_address.street,
streetNumber: customer.invoice_address.streetNo + '',
info: customer.invoice_address.note ? customer.invoice_address.note : null,
};
}
if (customer.organisation) {
organisation = {
name: customer.organisation.name, // Firma
nameSuffix: customer.organisation.extraAddress, // Addresszutsaiz
department: customer.organisation.department, // abteilung
vatId: customer.organisation.vatId, // USt-ID
};
}
if (Array.isArray(customer.poossible_delivery_addresses) && customer.poossible_delivery_addresses.length > 0) {
customer.poossible_delivery_addresses.forEach((adrs: Address) => {
addresses.push(this.addressesItem(adrs));
shippingAddresses.push(this.addressesItem(adrs));
});
}
if (Array.isArray(customer.poossible_invoice_addresses) && customer.poossible_invoice_addresses.length > 0) {
customer.poossible_invoice_addresses.forEach((adrs: Address) => {
if (adrs) {
addresses.push(this.addressesItem(adrs));
invoiceAdresses.push({
payer: {
data: {
firstName: adrs.first_name,
lastName: adrs.last_name,
gender: adrs.gender ? this.getGender(adrs.gender) : 0,
title: adrs.title ? adrs.title : null,
address: this.invoiceAdressItem(adrs),
payerType: !!adrs.payer_type ? adrs.payer_type : 8,
},
},
});
}
});
}
if (Array.isArray(customer.features) && customer.features.length > 0) {
customer.features.forEach((feature) => {
if (feature.key === 'guestaccount') {
isGuestAccount = true;
}
if (feature.key === 'onlineshop') {
hasOnlineAccount = true;
}
features.push({
key: feature.key,
value: feature.value,
description: feature.description,
enabled: true,
});
});
}
const createdInBranch = <EntityDTOContainerOfBranchDTO>{
id: customer.createdInBranch,
// TODO: what is the meaning of enabled attribute
enabled: true,
};
return <CustomerDTO>{
id: customer.id,
firstName: customer.first_name,
lastName: customer.last_name,
title: customer.title ? customer.title : null,
dateOfBirth: customer.date_of_birth ? customer.date_of_birth : null,
customerNumber: customer.customerId ? customer.customerId : null,
customerType: 8, // 8 => B2C customer, and the store app will have only B2C customers
customerGroup: null,
agentComment: null,
statusChangeComment: null,
deactivationComment: null,
statusComment: null,
label: null,
user: null,
campaignCode: null,
bonusCard: null,
isGuestAccount: isGuestAccount,
hasOnlineAccount: hasOnlineAccount,
attributes: null,
createdInBranch: createdInBranch,
customerStatus: 0, // Will this be ever different?
gender: gender,
address: address,
fetchOnDeliveryNote: null,
payers: invoiceAdresses,
organisation: organisation,
shippingAddresses: shippingAddresses,
communicationDetails: communicationDetails,
notificationChannels: notificationChannels,
features: features.length > 0 ? features : null,
};
}
getGender(gender: string): Gender {
if (gender === 'Frau') {
return 4;
} else if (gender === 'Herr') {
return 2;
} else {
return 0;
}
}
private addressesItem(address: Address): EntityDTOContainerOfShippingAddressDTO {
if (!address) {
return;
}
return {
data: {
firstName: address.first_name,
lastName: address.last_name,
gender: address.gender ? this.getGender(address.gender) : 0,
title: address.title ? address.title : null,
type: 1,
address: {
city: address.city,
country: address.country,
zipCode: address.zip + '',
street: address.street,
streetNumber: address.streetNo + '',
info: address.note,
},
},
};
}
private invoiceAdressItem(address: Address): AddressDTO {
return {
city: address.city,
country: address.country,
zipCode: address.zip + '',
street: address.street,
streetNumber: address.streetNo + '',
info: address.note,
};
}
customerInfoDTOtoUser(customerInfo: CustomerInfoDTO): User {
if (isNullOrUndefined(customerInfo)) {
throw new Error('argument customerInfo:CustomerInfoDTO is null or undefined.');
}
const id = customerInfo.id;
const firstName = customerInfo.firstName;
const lastName = customerInfo.lastName;
const gender = this.getGenderFromCode(customerInfo.gender);
let dateOfBirth: string;
if (!!customerInfo.dateOfBirth) {
dateOfBirth = customerInfo.dateOfBirth;
}
let email: string;
let phoneNumber: string;
let mobileNumber: string;
if (!!customerInfo.communicationDetails) {
email = customerInfo.communicationDetails.email;
phoneNumber = customerInfo.communicationDetails.phone;
mobileNumber = customerInfo.communicationDetails.mobile;
}
let newUser = false;
if (customerInfo.version === 2) {
newUser = true;
}
let notificationSms = false;
let notificationEmail = false;
if (customerInfo.notificationChannels) {
notificationSms =
customerInfo.notificationChannels === NotificationChannels.Sms ||
customerInfo.notificationChannels === NotificationChannels.EmailSms;
notificationEmail =
customerInfo.notificationChannels === NotificationChannels.Email ||
customerInfo.notificationChannels === NotificationChannels.EmailSms;
}
let baseAddres: Address = {} as Address;
if (!!customerInfo.address) {
baseAddres = {
id: Date.now() + Math.random(),
title: customerInfo.title,
first_name: customerInfo.firstName,
last_name: customerInfo.lastName,
city: customerInfo.address.city ? customerInfo.address.city : undefined,
zip: customerInfo.address.zipCode ? customerInfo.address.zipCode : undefined,
country: customerInfo.address.country,
street: customerInfo.address.street,
streetNo: customerInfo.address.streetNumber ? customerInfo.address.streetNumber : undefined,
};
}
let features: Features[] = [];
if (customerInfo.features) {
features = customerInfo.features
.filter((t) => t.enabled === true && !isNullOrUndefined(t.description))
.map((t) => {
return <Features>{
key: t.key,
value: t.value,
description: t.description,
};
});
}
let organisation: Organisation = {};
if (customerInfo.organisation) {
organisation = {
name: customerInfo.organisation.name,
extraAddress: customerInfo.organisation.nameSuffix,
department: customerInfo.organisation.department,
vatId: customerInfo.organisation.vatId,
};
}
// TODO: review missing info from the API
return <User>{
id: id,
customerId: customerInfo.customerNumber,
first_name: firstName,
last_name: lastName,
gender: gender,
date_of_birth: dateOfBirth,
email: email,
phone_number: phoneNumber,
mobile_number: mobileNumber,
newUser: newUser,
customer_card: false,
newsletter: false, // missing info from API
shop: false,
notificationSms: notificationSms,
notificationEmail: notificationEmail,
payement_method: null, // missing info from API
tolino: false, // missing info from API
title: null, // missing info from API
base_addres: baseAddres,
hasOnlineAccount: customerInfo.hasOnlineAccount,
isGuestAccount: customerInfo.isGuestAccount,
features: features,
changed: customerInfo.changed,
created: customerInfo.created,
customerNumber: customerInfo.customerNumber,
organisation: organisation,
customerType: this.fromCustomerType(customerInfo.customerType),
};
}
private getGenderFromCode(gender: number): string {
if (gender === 4) {
return 'Frau';
} else if (gender === 2) {
return 'Herr';
} else if (gender === 1) {
return 'Divers';
} else {
return null;
}
}
customerDTOtoUser(customerDto: CustomerDTO): User {
if (isNullOrUndefined(customerDto)) {
throw new Error('argument customerDto:CustomerInfoDTO is null or undefined.');
}
const id = customerDto.id;
const firstName = customerDto.firstName;
const lastName = customerDto.lastName;
const gender = this.getGenderFromCode(customerDto.gender);
let dateOfBirth: string;
if (!!customerDto.dateOfBirth) {
dateOfBirth = customerDto.dateOfBirth;
}
let email: string;
let phoneNumber: string;
let mobileNumber: string;
if (!!customerDto.communicationDetails) {
email = customerDto.communicationDetails.email;
phoneNumber = customerDto.communicationDetails.phone;
mobileNumber = customerDto.communicationDetails.mobile;
}
let newUser = false;
if (customerDto.version === 2) {
newUser = true;
}
let notificationSms = false;
let notificationEmail = false;
if (customerDto.notificationChannels) {
notificationSms =
customerDto.notificationChannels === NotificationChannels.Sms || customerDto.notificationChannels === NotificationChannels.EmailSms;
notificationEmail =
customerDto.notificationChannels === NotificationChannels.Email ||
customerDto.notificationChannels === NotificationChannels.EmailSms;
}
let baseAddres: Address;
if (!!customerDto.address) {
baseAddres = {
id: Date.now() + Math.random(),
title: customerDto.title,
first_name: customerDto.firstName,
last_name: customerDto.lastName,
city: customerDto.address.city,
zip: customerDto.address.zipCode,
country: customerDto.address.country,
street: customerDto.address.street,
streetNo: customerDto.address.streetNumber,
};
}
let features: Features[] = [];
if (customerDto.features) {
features = customerDto.features
.filter((t) => t.enabled === true && !isNullOrUndefined(t.description))
.map((t) => {
return <Features>{
key: t.key,
value: t.value,
description: t.description,
};
});
}
let organisation: Organisation = {};
if (customerDto.organisation) {
organisation = {
name: customerDto.organisation.name,
extraAddress: customerDto.organisation.nameSuffix,
department: customerDto.organisation.department,
vatId: customerDto.organisation.vatId,
};
}
// TODO: review missing info from the API
return <User>{
id: id,
customerId: customerDto.customerNumber,
first_name: firstName,
last_name: lastName,
gender: gender,
date_of_birth: dateOfBirth,
email: email,
phone_number: phoneNumber,
mobile_number: mobileNumber,
newUser: newUser,
customer_card: false,
createdInBranch: customerDto.createdInBranch ? customerDto.createdInBranch.id : null,
newsletter: false, // missing info from API
shop: false,
notificationSms: notificationSms,
notificationEmail: notificationEmail,
payement_method: null, // missing info from API
tolino: false, // missing info from API
title: null, // missing info from API
features: features,
changed: customerDto.changed,
created: customerDto.created,
customerNumber: customerDto.customerNumber,
organisation: organisation,
hasOnlineAccount: customerDto.hasOnlineAccount,
customerType: this.fromCustomerType(customerDto.customerType),
};
}
fromShippingAddressDtoToAddress(shippingAddress: ShippingAddressDTO): Address {
if (isNullOrUndefined(shippingAddress)) {
throw new Error('argument shippingAddress:ShippingAddressDTO is null or undefined.');
}
let gender = '';
if (!!shippingAddress.gender) {
gender = this.getGenderFromCode(shippingAddress.gender);
}
let country = '';
let city = '';
let street = '';
let streetNo = null;
let zip: string = null;
if (!!shippingAddress.address) {
country = shippingAddress.address.country;
city = shippingAddress.address.city;
street = shippingAddress.address.street;
streetNo = shippingAddress.address.streetNumber;
zip = shippingAddress.address.zipCode;
}
let company_name = null;
let company_department = null;
let company_tax_number = null;
if (!!shippingAddress.organisation) {
company_name = shippingAddress.organisation.name;
company_department = shippingAddress.organisation.department;
company_tax_number = shippingAddress.organisation.vatId;
}
let defaultSince: Date;
if (!!shippingAddress.isDefault) {
defaultSince = new Date(shippingAddress.isDefault);
}
return <Address>{
id: shippingAddress.id,
title: shippingAddress.title,
first_name: shippingAddress.firstName,
last_name: shippingAddress.lastName,
gender: gender,
country: country,
city: city,
street: street,
streetNo: streetNo,
zip: zip,
company_name: company_name,
company_department: company_department,
company_tax_number: company_tax_number,
defaultSince: defaultSince,
};
}
fromAddressToShippingAddressDTO(address: Address, forUpdate: boolean): ShippingAddressDTO {
if (isNullOrUndefined(address)) {
throw new Error('argument address:Address is null or undefined.');
}
let _address: AddressDTO;
if (forUpdate) {
_address = <AddressDTO>{
id: address.id,
country: address.country,
city: address.city,
street: address.street,
streetNumber: address.streetNo + '',
zipCode: address.zip + '',
};
} else {
_address = <AddressDTO>{
country: address.country,
city: address.city,
street: address.street,
streetNumber: address.streetNo + '',
zipCode: address.zip + '',
};
}
return <ShippingAddressDTO>{
type: 1,
title: address.title,
firstName: address.first_name,
lastName: address.last_name,
gender: this.getGender(address.gender),
status: 1,
isDefault: this.datePipe.transform(new Date(), BACKEND_API_TIMESTAMP_FORMAT),
organisation: <OrganisationDTO>{
name: address.company_name,
department: address.company_department,
vatId: address.company_tax_number,
},
address: _address,
};
}
fromAddressToAddressDTO(address: Address): AddressDTO {
if (isNullOrUndefined(address)) {
throw new Error('argument address:Address is null or undefined.');
}
return <AddressDTO>{
country: address.country,
city: address.city,
street: address.street,
streetNumber: address.streetNo + '',
zipCode: address.zip + '',
};
}
fromAddressToAssignedPAyerDto(address: Address): PayerDTO {
if (!address) {
return null;
}
let _address: AddressDTO;
if (!isNullOrUndefined(address.id)) {
_address = <AddressDTO>{
id: address.id,
country: address.country,
city: address.city,
street: address.street,
streetNumber: address.streetNo + '',
zipCode: address.zip + '',
};
} else {
_address = <AddressDTO>{
country: address.country,
city: address.city,
street: address.street,
streetNumber: address.streetNo + '',
zipCode: address.zip + '',
};
}
return <PayerDTO>{
type: 1,
title: address.title,
firstName: address.first_name,
lastName: address.last_name,
gender: this.getGender(address.gender),
status: 1,
isDefault: this.datePipe.transform(new Date(), BACKEND_API_TIMESTAMP_FORMAT),
address: _address,
payerNumber: address.payer_number,
payerStatus: address.payer_status,
payerType: !!address.payer_type ? address.payer_type : 8,
organisation: <OrganisationDTO>{
name: address.company_name,
department: address.company_department,
vatId: address.company_tax_number,
},
};
}
fromAssignedPayerDtoToAddress(assignedPayers: AssignedPayerDTO): Address {
if (isNullOrUndefined(assignedPayers)) {
throw new Error('argument shippingAddress:ShippingAddressDTO is null or undefined.');
}
let id = null;
let firstName = null;
let lastName = null;
if (!!assignedPayers.payer && !!assignedPayers.payer.data) {
id = assignedPayers.payer.data.id;
firstName = assignedPayers.payer.data.firstName;
lastName = assignedPayers.payer.data.lastName;
}
let gender = '';
if (!!assignedPayers.payer && !!assignedPayers.payer.data && !!assignedPayers.payer.data.gender) {
gender = this.getGenderFromCode(assignedPayers.payer.data.gender);
}
let country = '';
let city = '';
let street = '';
let streetNo = null;
let zip: string = null;
if (!!assignedPayers.payer && !!assignedPayers.payer.data && !!assignedPayers.payer.data.address) {
country = assignedPayers.payer.data.address.country;
city = assignedPayers.payer.data.address.city;
street = assignedPayers.payer.data.address.street;
streetNo = assignedPayers.payer.data.address.streetNumber;
zip = assignedPayers.payer.data.address.zipCode;
}
let payerNumber = null;
let payerStatus = null;
let payerType = null;
let title = null;
if (!!assignedPayers.payer && !!assignedPayers.payer.data) {
payerNumber = assignedPayers.payer.data.payerNumber;
payerStatus = assignedPayers.payer.data.payerStatus;
payerType = assignedPayers.payer.data.payerType;
title = assignedPayers.payer.data.title;
}
let company_name = null;
let company_department = null;
let company_tax_number = null;
if (!!assignedPayers.payer && !!assignedPayers.payer.data && !!assignedPayers.payer.data.organisation) {
company_name = assignedPayers.payer.data.organisation.name;
company_department = assignedPayers.payer.data.organisation.department;
company_tax_number = assignedPayers.payer.data.organisation.vatId;
}
let defaultSince: Date;
if (!!assignedPayers.isDefault) {
defaultSince = new Date(assignedPayers.isDefault);
}
return <Address>{
id: id,
title: title,
first_name: firstName,
last_name: lastName,
gender: gender,
country: country,
city: city,
street: street,
streetNo: streetNo,
zip: zip,
company_name: company_name,
company_department: company_department,
company_tax_number: company_tax_number,
payer_number: payerNumber,
payer_status: payerStatus,
payer_type: payerType,
defaultSince: defaultSince,
};
}
fromUserSimple(customer: User): CustomerDTO {
if (isNullOrUndefined(customer)) {
throw new Error('argument customer:User is null or undefined.');
}
let gender: Gender = 0;
let address: AddressDTO = {};
let organisation: OrganisationDTO = {};
let communicationDetails: CommunicationDetailsDTO = {};
const notificationChannels = this.getNotificationChannels(customer);
if (customer.mobile_number || customer.phone_number || customer.email) {
communicationDetails = {
email: customer.email ? customer.email : null,
mobile: customer.mobile_number ? customer.mobile_number : null,
phone: customer.phone_number ? customer.phone_number : null,
fax: null,
};
}
if (customer.gender) {
gender = this.getGender(customer.gender);
}
if (customer.delivery_addres) {
address = {
city: customer.delivery_addres.city,
country: customer.delivery_addres.country,
zipCode: customer.delivery_addres.zip + '',
street: customer.delivery_addres.street,
streetNumber: customer.delivery_addres.streetNo + '',
info: customer.delivery_addres.note ? customer.delivery_addres.note : null,
};
}
if (customer.organisation) {
organisation = {
name: customer.organisation.name, // Firma
nameSuffix: customer.organisation.extraAddress, // Addresszutsaiz
department: customer.organisation.department, // abteilung
vatId: customer.organisation.vatId, // USt-ID
};
}
const createdInBranch = <EntityDTOContainerOfBranchDTO>{
id: customer.createdInBranch,
// TODO: what is the meaning of enabled attribute
enabled: true,
};
return <CustomerDTO>{
id: customer.id,
firstName: customer.first_name,
lastName: customer.last_name,
title: customer.title ? customer.title : null,
dateOfBirth: customer.date_of_birth ? customer.date_of_birth : null,
customerNumber: customer.customerId ? customer.customerId : null,
customerType: this.toCustomerType(customer.customerType),
customerGroup: null,
agentComment: null,
statusChangeComment: null,
deactivationComment: null,
statusComment: null,
label: null,
user: null,
campaignCode: null,
bonusCard: null,
isGuestAccount: null,
features: null,
attributes: null,
createdInBranch: createdInBranch,
customerStatus: 0, // Will this be ever different?
gender: gender,
fetchOnDeliveryNote: null,
organisation: organisation,
communicationDetails: communicationDetails,
notificationChannels: notificationChannels,
};
}
getNotificationChannels(c: User): NotificationChannels {
if (!c) {
return NotificationChannels.NotSet;
}
return c.notificationEmail && c.notificationSms
? NotificationChannels.EmailSms
: c.notificationEmail
? NotificationChannels.Email
: c.notificationSms
? NotificationChannels.Sms
: NotificationChannels.NotSet;
}
fromOrganisationToOrganisationDTO(organisation: Organisation): OrganisationDTO {
if (!organisation) {
return {};
}
return {
name: organisation.name,
department: organisation.department,
vatId: organisation.vatId,
};
}
fromUserToCommunicationDetails(user: User): CommunicationDetailsDTO {
return {
phone: user.phone_number,
mobile: user.mobile_number,
email: user.email,
};
}
fromUserToShippingAddressDTO(user: User): CheckoutShippingAddressDTO {
const deliveryDetails = user.delivery_addres;
if (!deliveryDetails) {
throw Error('No Delivery Address set.');
}
return {
title: deliveryDetails.title,
gender: this.getGender(user.gender),
firstName: deliveryDetails.first_name,
lastName: deliveryDetails.last_name,
organisation: this.fromOrganisationToOrganisationDTO(user.organisation),
communicationDetails: this.fromUserToCommunicationDetails(user),
address: this.fromAddressToAddressDTO(deliveryDetails),
source: deliveryDetails.id,
};
}
fromCustomerType(type: CustomerType): CustomerTypeInternal {
return type;
}
toCustomerType(type: CustomerTypeInternal): CustomerType {
return type;
}
}

View File

@@ -1,57 +0,0 @@
import { FeedCard } from '../models/feed-card.model';
import { FeedBook } from '../models/feed-book.model';
import { FeedEvent } from '../models/feed-event.model';
import { FeedNews } from '../models/feed-news.model';
import { FeedRecommandation } from '../models/feed-recommandation.model';
import { Injectable } from '@angular/core';
import { FeedKpi } from '../models/feed-kpi.model';
@Injectable({ providedIn: 'root' })
export class FeedMapping {
constructor() {}
fromFeedDTO(feed: any): FeedCard {
const books: FeedBook[] = [];
const event: FeedEvent[] = [];
const news: FeedNews[] = [];
const recommandation: FeedRecommandation = null;
let kpi: FeedKpi = null;
if (feed.items && feed.type === 'products') {
feed.items.forEach((item) => {
books.push({
ean: item.product.ean,
name: item.product.name,
});
});
} else if (feed.type === 'info') {
feed.items.forEach((i) => {
news.push(<FeedNews>{
id: i.id,
title: i.heading,
content: i.text,
icon: i.image,
});
});
} else if (feed.type === 'kpi') {
kpi = {
actual: feed.items[0].actual,
target: feed.items[0].target,
percantage: Math.round((feed.items[0].actual / feed.items[0].target) * 100),
};
}
return <FeedCard>{
cardTitle: feed.label,
type: feed.type,
headline: feed.headline ? feed.headline : '',
text: feed.desc ? feed.desc : '',
books: books,
event: event,
news: news,
kpi: kpi,
recommandation: recommandation,
showRecommIcon: feed.emphasize ? true : false,
};
}
}

View File

@@ -1,26 +0,0 @@
import { Injectable } from '@angular/core';
import { OrderByDTO } from '@swagger/cat';
import { FilterItem } from '../models/filter-item.model';
import { OptionDTO } from '@cmf/core';
@Injectable({ providedIn: 'root' })
export class FilterItemMapping {
constructor() {}
fromOptionDto(option: OptionDTO): FilterItem {
return {
id: option.value,
name: option.label,
selected: false,
};
}
fromOrderByDto(orderBy: OrderByDTO): FilterItem {
return {
id: orderBy.by,
name: orderBy.label,
desc: orderBy.desc,
selected: false,
};
}
}

View File

@@ -1,87 +0,0 @@
import { Injectable } from '@angular/core';
import { InputDTO, UISettingsDTO, QueryTokenDTO, OrderByDTO } from '@swagger/cat';
import { Filter } from '../models/filter.model';
import { FilterItemMapping } from './filter-item.mapping';
import { FilterItem } from '../models/filter-item.model';
@Injectable({ providedIn: 'root' })
export class FilterMapping {
constructor(private filterItemMapping: FilterItemMapping) {}
fromInputDto(input: InputDTO): Filter {
let items: FilterItem[] = [];
let max: number;
if (input.options != null && input.options.values) {
(max = input.options.max), (items = input.options.values.map((item) => this.filterItemMapping.fromOptionDto(item)));
}
if (input.value != null) {
if (max == null) {
const selectedValues = input.value.split(';');
for (const selected of selectedValues) {
const idx = items.findIndex((f) => f.id === selected);
if (idx >= 0) {
items[idx].selected = true;
}
}
} else {
const idx = items.findIndex((f) => f.id === input.value);
if (idx >= 0) {
items[idx].selected = true;
}
}
}
return {
expanded: false,
id: input.key,
name: input.label,
max,
items,
};
}
toQueryTokenDto(target: QueryTokenDTO, source: Filter[], archive: boolean, negative: boolean) {
const orderBy = source.find((f) => f.id === 'orderBy');
if (orderBy != null) {
target.orderBy = orderBy.items
.filter((i) => i.selected)
.map(
(m) =>
({
by: m.id,
desc: m.desc,
} as OrderByDTO)
);
}
const filter = source.filter((f) => f.id !== 'orderBy').filter((f) => f.items && f.items.some((s) => s.selected));
if (Array.isArray(filter)) {
const kvps = filter.map((fil) => {
const key = fil.id;
const value = fil.items
.filter((f) => f.selected)
.map((f) => (negative && fil.id !== 'stock' ? '!' + f.id : f.id))
.join(';');
return [key, value];
});
if (kvps && kvps.length > 0) {
target.filter = {};
for (const kvp of kvps) {
target.filter[kvp[0]] = kvp[1];
}
}
}
if (archive) {
if (!target.filter) {
target.filter = {};
}
target.filter['cattype'] = '2';
}
return target;
}
}

View File

@@ -1,73 +0,0 @@
import { Product } from '../models/product.model';
import { ItemDTO, PriceDTO } from '@swagger/cat';
import { Injectable } from '@angular/core';
import { isNullOrUndefined } from '@utils/common';
@Injectable({ providedIn: 'root' })
export class ProductMapping {
fromItemDTO(item: ItemDTO): Product {
if (isNullOrUndefined(item)) {
throw new Error('argument item:ItemDTO is null or undefined.');
}
let currency = '';
let price = 0;
let priceDto: PriceDTO;
let ssc = '';
let sscText = '';
let storeStatusCode = 0;
if (!!item.catalogAvailability) {
priceDto = item.catalogAvailability.price;
ssc = item.catalogAvailability.ssc;
sscText = item.catalogAvailability.sscText;
storeStatusCode = item.catalogAvailability.status;
}
if (!!priceDto && priceDto.value) {
price = priceDto.value.value || price;
currency = priceDto.value.currency || currency;
}
let itemsInStock = 0;
if (Array.isArray(item.stockInfos)) {
itemsInStock = item.stockInfos.reduce((aggr, si) => aggr + si.inStock, 0);
}
let assortment: string;
if (item.shelfInfos) {
assortment = item.shelfInfos[0].assortment ? item.shelfInfos[0].assortment : item.shelfInfos[0].label;
}
return {
author: item.product.contributors,
availability: !!item.catalogAvailability,
currency,
price,
id: item.id,
itemsInStock,
err: '',
category: assortment,
icon: '',
notAvailableReason: itemsInStock === 0 ? '' : '',
publisher: item.product.manufacturer,
recommandation: false,
serial: item.product.serial,
slogan: item.product.additionalName,
title: item.product.name,
type: item.product.formatDetail,
typeIcon: item.product.format,
location: item.product.productGroup,
publicationDate: new Date(item.product.publicationDate),
ean: item.product.ean,
imageId: item.imageId,
edition: item.product.edition,
volume: item.product.volume,
ssc,
sscText,
storeStatusCode: storeStatusCode,
};
}
}

View File

@@ -1,33 +0,0 @@
import { ItemDTO } from '@swagger/cat';
import { Injectable } from '@angular/core';
import { RecommendationItem } from '../models/recommendation.model';
import { isNullOrUndefined } from '@utils/common';
@Injectable({ providedIn: 'root' })
export class RecommendationMapping {
fromItemDTO(item: ItemDTO): RecommendationItem {
if (isNullOrUndefined(item)) {
throw new Error('argument item:ItemDTO is null or undefined.');
}
let price = 0;
let images = [];
if (!!item.catalogAvailability) {
price = item.catalogAvailability.price.value.value;
}
if (Array.isArray(item.images) && item.images.length > 0) {
images = item.images;
}
return {
id: item.ids ? item.ids.dig : null,
ean: item.product.ean,
name: item.product.name,
price: price,
imgUrl: images[0].url,
type: item.product ? item.product.formatDetail : null,
};
}
}

View File

@@ -1,217 +0,0 @@
import { Injectable } from '@angular/core';
import { OrderItemListItemDTO, OrderDTO } from '@swagger/oms';
import { ShelfOrder } from '../models/shelf-order.model';
import { CollectingShelfOrder } from '../models/collecting-shelf-order.model';
import { CollectingShelfService } from '../services/collecting-shelf.service';
import { isNullOrUndefined } from '@utils/common';
export const orderStatusMapper: { [id: number]: string } = {
0: '-', // 'Not Set'
1: 'bestellt', // 'neu' 'Ordering'
2: '-', // 'Placed'
4: '-', // 'Accepted'
8: '-', // 'Parked'
16: 'bestellt', // 'In Process'
32: '-', // 'Preparation For Shipping'
64: 'versendet', // 'Dispatched'
128: 'eingetroffen', // 'Arrived'
256: 'abgeholt', // 'Fetched'
512: 'storniert (Kunde)', // 'Canceled By Buyer'
1024: 'storniert', // 'Canceled By Retailer'
2048: 'storniert (Lieferant)', // 'Canceled By Supplier'
4096: 'nicht lieferbar', // 'Not Available'
8192: 'nachbestellt', // 'ReOrdered'
16384: '-', // 'Returned By Buyer'
32768: '-', // 'Available For Download'
65536: '-', // 'Downloaded'
131072: '-', // 'Not Fetched'
262144: 'ans Lager (nicht abgeholt)', // 'Back To Stock'
524288: 'angefragt', // 'Requested'
1048576: 'weitergeleitet intern', // 'Redirected Internally'
2097152: '-', // 'Overdue'
4194304: 'zugestellt', // 'Delivered'
8388608: 'Lieferant wird ermittelt', // 'Determine Supplier'
16777216: 'derzeit nicht lieferbar', // 'Supplier Temporarily OutOfStock'
33554432: 'reserviert', // 'Reserved'
67108864: '-', // 'Assembled'
134217728: '-', // 'Packed'
};
export const activeOrderStatusMapper: { [id: number]: string } = {
16: 'bestellt', // 'In Process'
64: 'versendet', // 'Dispatched'
128: 'eingetroffen', // 'Arrived'
256: 'abgeholt', // 'Fetched'
512: 'storniert (Kunde)', // 'Canceled By Buyer'
1024: 'storniert', // 'Canceled By Retailer'
2048: 'storniert (Lieferant)', // 'Canceled By Supplier'
4096: 'nicht lieferbar', // 'Not Available'
8192: 'nachbestellt', // 'ReOrdered'
262144: 'ans Lager (nicht abgeholt)', // 'Back To Stock'
524288: 'angefragt', // 'Requested'
1048576: 'weitergeleitet intern', // 'Redirected Internally'
4194304: 'zugestellt', // 'Delivered'
8388608: 'Lieferant wird ermittelt', // 'Determine Supplier'
16777216: 'derzeit nicht lieferbar', // 'Supplier Temporarily OutOfStock'
33554432: 'reserviert', // 'Reserved'
};
export enum OrderStatus {
InProcess = 16, // 'bestellt'
Dispatched = 64, // 'versendet'
Arrived = 128, // 'eingetroffen'
Fetched = 256, // 'abgeholt'
CanceledByBuyer = 512, // 'storniert (Kunde)'
CanceledByRetailer = 1024, // 'storniert'
CanceledBySupplier = 2048, // 'storniert (Lieferant)'
NotAvailable = 4096, // 'nicht lieferbar'
ReOrdered = 8192, // 'nachbestellt'
BackToStock = 262144, // 'ans Lager (nicht abgeholt)'
Requested = 524288, // 'angefragt'
RedirectedInternally = 1048576, // 'weitergeleitet intern'
SupplierTemporarilyOutOfStock = 16777216, // derzeit nicht lieferbar
}
export const orderDetailStatus: { [id: number]: string } = {
0: '-',
1: 'neu', // 'Ordering'
2: 'bestellt', // 'InProcess'
3: 'eingetroffen', // 'Arrived'
};
export const orderChannelMapper: { [id: number]: string } = {
0: '-', // 'NotSet'
1: '', // 'System'
2: 'Filiale', // 'Branch'
4: 'HSC', // 'CallCenter'
8: 'Internet', // 'Online'
16: 'Mobil', // 'Mobile'
32: '', // 'BackOffice'
};
export const orderPaymentType: { [id: number]: string } = {
0: 'NotSet',
1: 'Bei Abholung', // 'WhenCollecting',
2: 'Kostenfrei', // 'Free',
4: 'Barzahlung', // 'Cash',
8: 'Einzugsermächtigung', // 'DirectDebit',
16: 'Dauerhafte Einzugsermächtigung', // 'DebitAdviceMandate',
31: 'Debit Karte', // 'DebitCard',
64: 'Kreditkarte', // 'CreditCard',
128: 'Rechnung', // 'Invoice',
256: 'Vorauskasse', // 'PrePayment',
512: 'Gutschein', // 'Voucher',
1024: 'Sammelrechnung', // 'CollectiveInvoice',
2048: 'PayPal', // 'PayPal',
4096: 'Sofortüberweisung', // 'InstantTransfer',
8192: 'Nachnahme', // 'PayOnDelivery',
16384: 'Kundenkarte', // 'BonusCard',
};
export const receiptType: { [id: number]: string } = {
0: 'NotSet',
1: 'Lieferschein', // 'ShippingNote',
2: 'Gutschrift', // 'CreditNote',
4: 'Sammellieferschein', // 'CollectiveShippingNote',
8: 'Sammelgutschrift', // 'CollectiveCreditNote',
16: 'Sammellieferschein für Kundenkartenkäufe', // 'BonusCardCollectiveShippingNote',
31: 'Sammelgutschrift für Kundenkartenkäufe', // 'BonusCardCollectiveCreditNote',
64: 'Zahlungsbeleg', // 'PaymentReceipt',
128: 'Rechnung', // 'Invoice',
256: 'Sammelrechnung', // 'CollectiveInvoice',
512: 'Proformarechnung', // 'ProformaInvoice',
};
@Injectable({ providedIn: 'root' })
export class ShelfMapping {
fromOrderItemListItemDTOArrayToCollectingShelfOrder(
orders: OrderItemListItemDTO[],
collectingShelfService: CollectingShelfService,
firstBatch: boolean
): CollectingShelfOrder[] {
if (isNullOrUndefined(orders) || orders.length < 1) {
return [];
}
return orders.map((order, index) => {
const hideCompartmentNumber = collectingShelfService.processOrderCompartmentNumber(orders, order, index, firstBatch);
return <CollectingShelfOrder>{
orderId: order.orderId,
orderItemId: order.orderItemId,
orderItemSubsetId: order.orderItemSubsetId,
orderPId: order.orderPId,
orderItemPId: order.orderItemPId,
orderItemSubsetPId: order.orderItemSubsetPId,
shopName: order.shopName,
orderBranchId: order.orderBranchId,
orderNumber: order.orderNumber,
orderType: order.orderType,
processingStatus: order.processingStatus,
orderDate: order.orderDate,
product: order.product,
quantity: order.quantity,
overallQuantity: order.overallQuantity,
price: order.price,
currency: order.currency,
buyerNumber: order.buyerNumber,
organisation: order.organisation,
gender: order.gender,
title: order.title,
lastName: order.lastName,
firstName: order.firstName,
specialComment: order.specialComment,
ssc: order.ssc,
sscText: order.sscText,
supplier: order.supplier,
compartmentCode: order.compartmentCode,
compartmentInfo: order.compartmentInfo,
hideCompartmentNumber: hideCompartmentNumber,
isPrebooked: order.isPrebooked,
};
});
}
fromOrderDTOtoShelfOrder(order: OrderDTO): ShelfOrder {
if (isNullOrUndefined(order)) {
return;
}
return <ShelfOrder>{
id: order.id,
created: order.created,
changed: order.changed,
version: order.version,
status: order.status,
pId: order.pId,
orderType: order.orderType,
clientChannel: order.clientChannel,
orderNumber: order.orderNumber,
orderDate: order.orderDate,
acceptanceDate: order.acceptanceDate,
orderBranch: order.orderBranch,
processingStatus: order.processingStatus,
completedDate: order.completedDate,
items: order.items,
buyer: order.buyer,
buyerComment: order.buyerComment,
notificationChannels: order.notificationChannels,
shippingAddress: order.shipping,
targetBranch: order.targetBranch,
logistician: order.logistician,
payer: order.billing,
paymentType: order.paymentType,
paymentComment: order.paymentComment,
payment: order.payment,
invoiceText: order.invoiceText,
paymentStatus: order.paymentStatus,
paymentReferenceNumber: order.paymentReferenceNumber,
agentComment: order.agentComment,
specialAgreements: order.specialAgreements,
validationStatus: order.validationStatus,
termsOfDelivery: order.termsOfDelivery,
campaignCode: order.campaignCode,
orderValue: order.orderValue,
orderValueCurrency: order.orderValueCurrency,
itemsCount: order.itemsCount,
};
}
}

View File

@@ -1,4 +0,0 @@
export interface GoodsInSearch {
input: string;
branchnumber: string;
}

View File

@@ -1,9 +0,0 @@
export interface ActionResult<T> {
error?: boolean;
errorReasons?: { [key: string]: string };
http?: {
code: number;
};
message?: string;
result?: T;
}

View File

@@ -1,4 +0,0 @@
export enum ModuleSwitcher {
Customer,
Branch,
}

View File

@@ -1,5 +0,0 @@
export interface AutocompleteResults {
display?: string;
type?: string;
query?: string;
}

View File

@@ -1,23 +0,0 @@
import { ItemDTO } from '@swagger/cat';
import { BranchInfoDTO } from '@swagger/isa';
export interface BookData {
id: number;
book: ItemDTO;
quantity: number;
customQuantity?: number;
price: number;
currency: string;
imgUrl: string;
deliveryType: string;
cartEntryId: number;
branch?: BranchInfoDTO;
orderId?: number;
orderTypeChanged: boolean;
avaMessage: string;
pickUpPrice?: number;
deliveryPrice?: number;
downloadPrice?: number;
deliveryDate?: string;
pickUpDate?: string;
}

View File

@@ -1,5 +0,0 @@
export interface BranchProcess {
id: number;
currentRoute?: string;
currentRouteQueryParams?: any;
}

View File

@@ -1,10 +0,0 @@
import { BranchType } from '@swagger/checkout';
export const BranchTypeCode: { [key: string]: BranchType } = {
NotSet: 0,
Store: 1,
WebStore: 2,
CallCenter: 4,
Headquarter: 8,
HumanResources: 16,
};

View File

@@ -1,5 +0,0 @@
export interface Breadcrumb {
name: string;
path: string;
queryParams?: any;
}

View File

@@ -1,18 +0,0 @@
import { AddressDTO } from '@swagger/checkout';
import { AvailabilityDTO } from '@swagger/availability';
export interface CartEntry {
bookId: number;
quantity: number;
deliveryType: string;
branch?: number;
deliveryDate?: string;
pickUpDate?: string;
orderTypeChanged: boolean;
avaMessage: string;
pickUpPrice?: number;
deliveryPrice?: number;
downloadPrice?: number;
shippingAddress?: AddressDTO;
availability: AvailabilityDTO;
}

View File

@@ -1,18 +0,0 @@
import { ItemDTO } from '@swagger/cat';
import { BranchInfoDTO } from '@swagger/isa';
export interface Cart {
cartId: number;
cartEntryId: number;
book: ItemDTO;
quantity: number;
deliveryType: string;
branch?: BranchInfoDTO;
orderTypeChanged: boolean;
avaMessage: string;
pickUpPrice?: number;
deliveryPrice?: number;
downloadPrice?: number;
deliveryDate?: string;
pickUpDate?: string;
}

View File

@@ -1,7 +0,0 @@
export enum CheckoutType {
takeNow,
store,
delivery,
donwload,
deliveryB2b,
}

View File

@@ -1,37 +0,0 @@
import { OrderType, OrderItemProcessingStatusValue, ProductDTO, Gender } from '@swagger/oms';
import { ShelfOrderItemLevel } from './shelf-order-item-level.enum';
export interface CollectingShelfOrder {
orderId?: number;
orderItemId?: number;
orderItemSubsetId?: number;
orderPId?: string;
orderItemPId?: string;
orderItemSubsetPId?: string;
shopName?: string;
orderBranchId?: number;
orderNumber?: string;
orderType?: OrderType;
processingStatus?: OrderItemProcessingStatusValue;
orderDate?: string;
product?: ProductDTO;
quantity?: number;
overallQuantity?: number;
price?: number;
currency?: string;
buyerNumber?: string;
organisation?: string;
gender?: Gender;
title?: string;
lastName?: string;
firstName?: string;
specialComment?: string;
ssc?: string;
sscText?: string;
supplier?: string;
compartmentCode?: string;
compartmentInfo?: string;
shelfOrderItemLevel?: ShelfOrderItemLevel;
hideCompartmentNumber: boolean;
isPrebooked?: boolean;
}

View File

@@ -1,6 +0,0 @@
export interface Country {
index: number;
id: number;
key: string;
value: string;
}

View File

@@ -1,23 +0,0 @@
import { isNullOrUndefined } from '@utils/common';
import { Features } from './user.model';
export class CustomerFeatures {
online: boolean;
guest: boolean;
card: boolean;
employee: boolean;
constructor(features: Features[]) {
if (features && features.length > 0) {
this.online = !isNullOrUndefined(features.find((feature) => feature.key === 'onlineshop'));
this.guest = !isNullOrUndefined(features.find((feature) => feature.key === 'guestaccount'));
this.card = !isNullOrUndefined(features.find((feature) => feature.key === 'p4mUser'));
this.employee = !isNullOrUndefined(features.find((feature) => feature.key === 'staff'));
} else {
this.online = false;
this.guest = false;
this.card = false;
this.employee = false;
}
}
}

View File

@@ -1,21 +0,0 @@
export interface CustomerOrderItem {
id: number;
name: string;
price: number;
currency: string;
status: string;
isbn: string;
note: string;
orderType: string;
fsk: string;
ticketNumber: string;
suplier: string;
vat: number;
ssc: string;
quantity: number;
estimatedDate?: string;
shippingAddress?: string;
billingAddress?: string;
preordered?: boolean;
invoiceText?: string;
}

View File

@@ -1,56 +0,0 @@
export interface CustomerOrder {
id: number;
date: string;
status: string;
type: string;
location: string;
price: number;
currency: string;
count: number;
}
export const OrderStatus: { [key: number]: string } = {
0: '-',
1: 'In Bestellung',
2: 'In Bearbeitung',
3: 'Abgeschlossen',
};
export const OrderType: { [key: number]: string } = {
0: '-',
2: 'Filiale',
4: 'HSC',
8: 'Online',
};
export const OrderItemStatus: { [key: number]: string } = {
0: '-',
1: 'neu',
2: '-',
4: '-',
8: '-',
16: 'bestellt',
32: '-',
64: 'versendet',
128: 'eingetroffen',
256: 'abgeholt',
512: 'storniert (Kunde)',
1024: 'storniert',
2048: 'storniert (Lieferant)',
4096: 'nicht lieferbar',
8192: 'nachbestellt',
16384: '-',
32768: '-',
65536: '-',
131072: '-',
262144: 'ans Lager (nicht abgeholt)',
524288: 'angefragt',
1048576: 'weitergeleitet intern',
2097152: '-',
4194304: 'zugestellt',
8388608: 'Lieferant wird ermittelt',
16777216: 'derzeit nicht lieferbar',
33554432: 'reserviert',
67108864: '-',
134217728: '-',
};

View File

@@ -1,13 +0,0 @@
import { CustomerOrderItem } from './customer-order-item';
export interface CustomerOrder {
id: number;
customerName: string;
status: string;
type: string;
date: string;
branch: string;
items: CustomerOrderItem[];
orderType: number;
orderNumber: string;
}

View File

@@ -1,7 +0,0 @@
import { User } from './user.model';
export class CustomerSearchResponse {
customers: User[];
hits: number;
message?: string;
}

View File

@@ -1,6 +0,0 @@
import { User } from './user.model';
export interface CustomerSearchResult {
processId: number;
customers: User[];
}

View File

@@ -1,6 +0,0 @@
export interface CustomerSearch {
query: string;
skip: number;
take: number;
firstLoad: boolean;
}

View File

@@ -1,11 +0,0 @@
export type CustomerType = 0 | 1 | 2 | 4 | 8 | 16 | 32;
export enum CustomerTypeInternal {
'' = 0,
'Contact' = 1,
'Branch' = 2,
'Staff' = 4,
'B2C' = 8,
'B2B' = 16,
'WB' = 32,
}

View File

@@ -1,18 +0,0 @@
import { ShippingTarget } from '@swagger/checkout';
export class DeliveryOption {
static TAKE_NOW = 'Rücklage';
static DELIVERY = 'Versand';
static PICK_UP = 'Abholung';
static DOWNLOAD = 'Download';
static DELIVERY_B2B = 'B2B Versand';
}
export const DeliveryType: { [key: string]: ShippingTarget } = {
NotSet: 0,
Branch: 1,
ShippingAddress: 2,
BillingAddress: 4,
Packstation: 8,
Download: 16,
Quote: 32,
};

Some files were not shown because too many files have changed in this diff Show More