Compare commits

...

33 Commits

Author SHA1 Message Date
Nino
2da3859536 #4885 Compartment Info Bugfix 2024-11-25 15:21:37 +01:00
Lorenz Hilpert
1becbec412 Merge tag 'scandit-package-replacement' into develop
Scandit Lizenz und Package upgrade scandit-package-replacement
2024-11-25 14:04:57 +01:00
Lorenz Hilpert
cdc2553d73 Merge branch 'hotfix/scandit-package-replacement' 2024-11-25 13:56:08 +01:00
Lorenz Hilpert
05eb3cc756 Revert "Refactor ScanditOverlayComponent to use dependency injection for DataCaptureContext and DataCaptureView, and enhance module setup with async context initialization"
This reverts commit 6e1c434edf.
2024-11-22 18:31:05 +01:00
Lorenz Hilpert
6e1c434edf Refactor ScanditOverlayComponent to use dependency injection for DataCaptureContext and DataCaptureView, and enhance module setup with async context initialization 2024-11-22 18:23:05 +01:00
Lorenz Hilpert
ed8e937924 Update Scandit license keys across configuration files 2024-11-22 17:26:27 +01:00
Lorenz Hilpert
1bd17fd887 Update Scandit integration to use new SDK, adjust TypeScript configuration, and refine overlay component styles 2024-11-22 16:19:32 +01:00
Lorenz Hilpert
c35c82eaab Update Scandit license keys in configuration files 2024-11-21 14:16:58 +01:00
Lorenz Hilpert
258faec021 Refactor login strategy to conditionally trigger mobile device login 2024-11-19 16:55:00 +01:00
Lorenz Hilpert
4bcc523480 Update login strategy to always trigger mobile device login and prevent backdrop closing on modal 2024-11-19 16:31:55 +01:00
Lorenz Hilpert
8900a77d7a Enhance PickupShelfInDetailsComponent with signals for order items and optimize fetching logic 2024-11-19 16:27:18 +01:00
Lorenz Hilpert
895e2bd2ec Merge tag 'Abholfach-zubuchen-und-Coveransicht' into develop
Abholfach-zubuchen-und-Coveransicht Abholfach-zubuchen-und-Coveransicht
2024-11-19 13:54:23 +01:00
Lorenz Hilpert
4b10dd96d9 Merge branch 'hotfix/abholfach-zubuchen-und-cover-items' 2024-11-19 13:50:36 +01:00
Lorenz Hilpert
1126e4f0c1 Import tapResponse alongside ComponentStore in pickup-shelf-details.store.ts 2024-11-19 13:50:15 +01:00
Lorenz Hilpert
e9f24a88d6 Refactor PickupShelfInDetailsComponent to use signals for customer number and update fetch logic 2024-11-19 13:48:33 +01:00
Lorenz Hilpert
f30de35d51 Enhance authentication flow by improving error handling and validating ID tokens before login 2024-11-19 13:42:54 +01:00
Lorenz Hilpert
0c6f8abbad Fix login strategy condition to properly check for mobile devices 2024-11-18 13:49:32 +01:00
Lorenz Hilpert
54b37436eb Refactor authentication handling to utilize LoginStrategy and improve error management 2024-11-18 13:48:58 +01:00
Lorenz Hilpert
02bae79e4a Merged PR 1819: #4844 Implement dev-scanner configuration and enhance app initialization process
#4844 Implement dev-scanner configuration and enhance app initialization process
2024-11-13 17:09:34 +00:00
Lorenz Hilpert
f2c95b6a16 Merge tag '3.3' into develop
Merge branch release/3.3 3.3
2024-11-12 17:05:47 +01:00
Lorenz Hilpert
d48680c59e Merge branch 'release/3.3' 2024-11-12 17:04:15 +01:00
Lorenz Hilpert
775390b5df Add dynamic IDs to filter buttons in filter group component 2024-11-05 13:18:21 +01:00
Lorenz Hilpert
1788f566e3 Add filter buttons with unique IDs in package result component 2024-11-05 11:05:47 +01:00
Nino Righi
d4e1088190 Merged PR 1818: #4836 Small Bugfix
#4836 Small Bugfix
2024-10-31 17:07:26 +00:00
Lorenz Hilpert
a8ecd1f07b Merged PR 1817: Enhance error handling and state management in app module and root state service
Enhance error handling and state management in app module and root state service

Related work items: #4835
2024-10-30 12:14:58 +00:00
Lorenz Hilpert
2c239ac597 Merge branch 'develop' of ssh.dev.azure.com:v3/hugendubel/ISA/ISA-Frontend into develop 2024-10-30 11:47:07 +01:00
Lorenz Hilpert
200eb7f217 Mark NativeScanAdapter and native-container.service as deprecated 2024-10-30 11:46:14 +01:00
Nino Righi
694fc6d084 Merged PR 1816: #4836 Fix Printer Handling on Small Amount Invoice
#4836 Fix Printer Handling on Small Amount Invoice
2024-10-30 10:39:53 +00:00
Lorenz Hilpert
8ae990bcde Merged PR 1815: Angular Update V18
Related work items: #4830, #4834
2024-10-22 09:23:23 +00:00
Lorenz Hilpert
1d472ce3df (cherry picked from commit 4c027922283bc4113253310c9d47e7c8944427a3) 2024-10-07 19:04:31 +02:00
Nino Righi
1d19779dac Merged PR 1805: #4687 Hotfix Organisation Name Update Order
#4687 Hotfix Organisation Name Update Order
2024-10-07 14:33:04 +00:00
Nino
90e671d285 #4687 Changes to View, Added Organisation Input Field 2024-10-07 12:07:28 +02:00
Lorenz Hilpert
33fb44f20a Upgrade Version to 3.3 2024-09-24 16:51:36 +02:00
1054 changed files with 394416 additions and 21959 deletions

5
.gitignore vendored
View File

@@ -5,6 +5,8 @@
/tmp
/out-tsc
/
# dependencies
/node_modules
@@ -47,4 +49,5 @@ testem.log
.DS_Store
Thumbs.db
libs/swagger/src/lib/*
libs/swagger/src/lib/*
*storybook.log

View File

@@ -24,6 +24,6 @@ ARG BuildUniqueID
LABEL build.uniqueid="${BuildUniqueID:-1}"
RUN wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb -q -O /tmp/chrome.deb && apt update && apt install -y /tmp/chrome.deb
# ignore exitcode, sonst gibts keinen container
RUN npm test || true
RUN npm run ci || true
ENTRYPOINT [ "/bin/sleep", "60000" ]

View File

@@ -27,8 +27,7 @@
"pdfjs-dist/build/pdf",
"pdfjs-dist/web/pdf_viewer",
"pdfjs-dist/es5/build/pdf",
"pdfjs-dist/es5/web/pdf_viewer",
"scandit-sdk"
"pdfjs-dist/es5/web/pdf_viewer"
],
"outputPath": "dist/isa-app",
"index": "apps/isa-app/src/index.html",
@@ -44,7 +43,7 @@
"apps/isa-app/src/manifest.webmanifest",
{
"glob": "**/*",
"input": "node_modules/scandit-sdk/build",
"input": "node_modules/scandit-web-datacapture-barcode/build/engine",
"output": "scandit"
}
],
@@ -125,6 +124,36 @@
],
"scripts": []
}
},
"storybook": {
"builder": "@storybook/angular:start-storybook",
"options": {
"configDir": "apps/isa-app/.storybook",
"browserTarget": "isa-app:build",
"compodoc": true,
"compodocArgs": [
"-e",
"json",
"-d",
"apps/isa-app"
],
"port": 6006
}
},
"build-storybook": {
"builder": "@storybook/angular:build-storybook",
"options": {
"configDir": "apps/isa-app/.storybook",
"browserTarget": "isa-app:build",
"compodoc": true,
"compodocArgs": [
"-e",
"json",
"-d",
"apps/isa-app"
],
"outputDir": "storybook-static"
}
}
}
}

View File

@@ -0,0 +1,2 @@
<div class="scanner-container" #scanContainer></div>
<button class="close-scanner" type="button" (click)="close()">Scan abbrechen</button>

View File

@@ -0,0 +1,17 @@
import type { StorybookConfig } from '@storybook/angular';
const config: StorybookConfig = {
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
addons: [
'@storybook/addon-onboarding',
'@storybook/addon-links',
'@storybook/addon-essentials',
'@chromatic-com/storybook',
'@storybook/addon-interactions',
],
framework: {
name: '@storybook/angular',
options: {},
},
};
export default config;

View File

@@ -0,0 +1,17 @@
import type { Preview } from '@storybook/angular';
import { setCompodocJson } from '@storybook/addon-docs/angular';
import docJson from '../documentation.json';
setCompodocJson(docJson);
const preview: Preview = {
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
},
};
export default preview;

View File

@@ -0,0 +1,10 @@
// This tsconfig is used by Compodoc to generate the documentation for the project.
// If Compodoc is not used, this file can be deleted.
{
"extends": "./tsconfig.json",
// Exclude all files that are not needed for documentation generation.
"exclude": ["../src/test.ts", "../src/**/*.spec.ts", "../src/**/*.stories.ts"],
// Please make sure to include all files from which Compodoc should generate documentation.
"include": ["../src/**/*"],
"files": ["./typings.d.ts"]
}

View File

@@ -0,0 +1,11 @@
{
"extends": "../tsconfig.app.json",
"compilerOptions": {
"types": ["node"],
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true
},
"exclude": ["../src/test.ts", "../src/**/*.spec.ts"],
"include": ["../src/**/*.stories.*", "./preview.ts", "../src/polyfills.ts"],
"files": ["./typings.d.ts"]
}

4
apps/isa-app/.storybook/typings.d.ts vendored Normal file
View File

@@ -0,0 +1,4 @@
declare module '*.md' {
const content: string;
export default content;
}

373381
apps/isa-app/documentation.json Normal file
View File

File diff suppressed because one or more lines are too long

View File

@@ -1,23 +1,22 @@
import { Injectable, isDevMode } from '@angular/core';
import { EnvironmentService } from '@core/environment';
import { inject, Injectable } from '@angular/core';
import { PromptModalData, UiModalService, UiPromptModalComponent } from '@ui/modal';
import { Observable } from 'rxjs';
import { ScanAdapter } from './scan-adapter';
import { Config } from '@core/config';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
@Injectable()
export class DevScanAdapter implements ScanAdapter {
readonly name = 'Dev';
constructor(
private _modal: UiModalService,
private _environmentService: EnvironmentService,
) {}
private _modal = inject(UiModalService);
private _config = inject(Config);
async init(): Promise<boolean> {
return Promise.resolve(false);
// return new Promise((resolve, reject) => {
// resolve(isDevMode());
// });
return new Promise((resolve, reject) => {
resolve(coerceBooleanProperty(this._config.get('dev-scanner')));
});
}
scan(): Observable<string> {

View File

@@ -4,6 +4,10 @@ import { Observable } from 'rxjs';
import { filter, map, take } from 'rxjs/operators';
import { ScanAdapter } from './scan-adapter';
/**
* @deprecated This service will be removed in future versions.
* Please use the new ScanService instead.
*/
@Injectable()
export class NativeScanAdapter implements ScanAdapter {
readonly name = 'Native';

View File

@@ -37,7 +37,7 @@ export class ScanAdapterService {
for (const name of adapterOrder) {
adapter = this.getAdapter(name);
console.log('adapter', adapter);
if (adapter) {
break;
}

View File

@@ -3,12 +3,15 @@
}
.scanner-container {
width: 100vw;
/* width: 100vw;
height: 100vh;
max-width: 100vh;
max-height: 100vh; */
}
.close-scanner {
@apply absolute bottom-12 left-[50%] -translate-x-[50%] block px-6 py-4 bg-white text-brand border-2 border-solid border-brand rounded-full text-lg font-bold mx-auto mt-4;
@apply whitespace-nowrap;
}
@screen desktop {

View File

@@ -1,6 +1,7 @@
import { Component, ChangeDetectionStrategy, ElementRef, ViewChild, NgZone, AfterViewInit, OnDestroy } from '@angular/core';
import { UiMessageModalComponent, UiModalService } from '@ui/modal';
import { Barcode, BarcodePicker, ScanResult, ScanSettings } from 'scandit-sdk';
import { Component, ChangeDetectionStrategy, ElementRef, ViewChild, NgZone, AfterViewInit, OnDestroy, inject } from '@angular/core';
import { UiModalService } from '@ui/modal';
import { BarcodeCapture, BarcodeCaptureSettings, Symbology } from 'scandit-web-datacapture-barcode';
import { Camera, DataCaptureContext, DataCaptureView, FrameSourceState } from 'scandit-web-datacapture-core';
@Component({
selector: 'app-scandit-overlay',
@@ -9,7 +10,10 @@ import { Barcode, BarcodePicker, ScanResult, ScanSettings } from 'scandit-sdk';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ScanditOverlayComponent implements AfterViewInit, OnDestroy {
private _barcodePicker: BarcodePicker;
private dataCaptureContext: DataCaptureContext;
private dataCaptureView: DataCaptureView;
private barcodeCapture: BarcodeCapture;
private camera: Camera;
private _onScan?: (code: string) => void;
@@ -17,57 +21,58 @@ export class ScanditOverlayComponent implements AfterViewInit, OnDestroy {
@ViewChild('scanContainer', { read: ElementRef, static: true }) scanContainer: ElementRef;
constructor(
private _zone: NgZone,
private _modal: UiModalService,
) {}
constructor(private _zone: NgZone) {}
ngAfterViewInit(): void {
this.createBarcodePicker()
.then(() => {
this._barcodePicker.on('scan', (scanResult) => {
this._zone.run(() => this.handleScanrResult(scanResult));
ngOnInit(): void {
this.dataCaptureView = new DataCaptureView();
this.dataCaptureView.connectToElement(this.scanContainer.nativeElement);
this.dataCaptureView.showProgressBar();
}
async ngAfterViewInit() {
this.dataCaptureContext = await DataCaptureContext.create();
this.dataCaptureView.setContext(this.dataCaptureContext);
this.barcodeCapture = await BarcodeCapture.forContext(this.dataCaptureContext, this.getScanSettings());
this.barcodeCapture.addListener({
didScan: (_, session, __) => {
this._zone.run(() => {
const result = session.newlyRecognizedBarcode;
const code = result?.data ?? '';
this._onScan?.(code);
});
})
.catch((err: Error) => {
this._modal
.open({
content: UiMessageModalComponent,
title: 'Zugriff auf Kamera verweigert',
data: { message: 'Falls Sie den Zugriff erlauben möchten, können Sie das über die Webseiteinstellung Ihres Browsers.' },
})
.afterClosed$.subscribe(() => {
this._onClose?.();
});
});
}
async createBarcodePicker() {
this._barcodePicker = await BarcodePicker.create(this.scanContainer.nativeElement, {
playSoundOnScan: true,
vibrateOnScan: true,
},
});
this._barcodePicker.applyScanSettings(this.getScanSettings());
this.camera = Camera.default;
this.dataCaptureContext.setFrameSource(this.camera);
await this.camera.switchToDesiredState(FrameSourceState.On);
this.dataCaptureView.hideProgressBar();
}
getScanSettings(): ScanSettings {
return new ScanSettings({
blurryRecognition: false,
getScanSettings(): BarcodeCaptureSettings {
const settings = new BarcodeCaptureSettings();
enabledSymbologies: [
Barcode.Symbology.EAN8,
Barcode.Symbology.EAN13,
Barcode.Symbology.UPCA,
Barcode.Symbology.UPCE,
Barcode.Symbology.CODE128,
Barcode.Symbology.CODE39,
Barcode.Symbology.CODE93,
Barcode.Symbology.INTERLEAVED_2_OF_5,
Barcode.Symbology.QR,
],
codeDuplicateFilter: 1000,
});
settings.enableSymbologies([
Symbology.EAN8,
Symbology.EAN13UPCA,
Symbology.UPCE,
Symbology.Code128,
Symbology.Code39,
Symbology.Code93,
Symbology.InterleavedTwoOfFive,
Symbology.QR,
]);
return settings;
}
onScan(fn: (code: string) => void) {
@@ -78,26 +83,14 @@ export class ScanditOverlayComponent implements AfterViewInit, OnDestroy {
this._onClose = fn;
}
handleScanrResult(scanRestul: ScanResult) {
let result: string | undefined;
if (scanRestul.barcodes.length) {
result = scanRestul.barcodes[0].data;
} else if (scanRestul.texts.length) {
result = scanRestul.texts[0].value;
}
if (result) {
this._onScan?.(result);
}
}
close() {
this._onClose?.();
}
ngOnDestroy(): void {
this._zone.runOutsideAngular(() => {
this._barcodePicker?.destroy(true);
this.barcodeCapture?.setEnabled(false);
this.camera?.switchToDesiredState(FrameSourceState.Off);
});
}
}

View File

@@ -3,7 +3,9 @@ import { Observable, Subscriber } from 'rxjs';
import { ScanAdapter } from '../scan-adapter';
import { Overlay } from '@angular/cdk/overlay';
import { configure } from 'scandit-sdk';
import { configure } from 'scandit-web-datacapture-core';
import { barcodeCaptureLoader } from 'scandit-web-datacapture-barcode';
// import { ScanditModalComponent } from './scandit-modal';
import { Config } from '@core/config';
import { ComponentPortal } from '@angular/cdk/portal';
@@ -26,8 +28,10 @@ export class ScanditScanAdapter implements ScanAdapter {
async init(): Promise<boolean> {
if (this._environmentService.isTablet()) {
await configure(this._config.get('licence.scandit'), {
engineLocation: '/scandit/',
await configure({
licenseKey: this._config.get('licence.scandit'),
libraryLocation: new URL('scandit', document.baseURI).toString(),
moduleLoaders: [barcodeCaptureLoader()],
});
return true;

View File

@@ -1,5 +1,5 @@
import { DOCUMENT } from '@angular/common';
import { Component, effect, HostListener, Inject, OnInit, Renderer2, signal, untracked } from '@angular/core';
import { Component, effect, HostListener, inject, Inject, Injector, OnInit, Renderer2, signal, untracked } from '@angular/core';
import { Title } from '@angular/platform-browser';
import { SwUpdate } from '@angular/service-worker';
import { ApplicationService } from '@core/application';
@@ -10,7 +10,7 @@ import { asapScheduler, interval, Subscription } from 'rxjs';
import { UserStateService } from '@swagger/isa';
import { IsaLogProvider } from './providers';
import { EnvironmentService } from '@core/environment';
import { AuthService } from '@core/auth';
import { AuthService, LoginStrategy } from '@core/auth';
import { UiMessageModalComponent, UiModalService } from '@ui/modal';
import { injectOnline$ } from './services/network-status.service';
import { toSignal } from '@angular/core/rxjs-interop';
@@ -35,6 +35,8 @@ import { animate, style, transition, trigger } from '@angular/animations';
],
})
export class AppComponent implements OnInit {
readonly injector = inject(Injector);
$online = toSignal(injectOnline$());
$offlineBannerVisible = signal(false);
@@ -180,15 +182,9 @@ export class AppComponent implements OnInit {
if (this._document.hidden && this._authService.isAuthenticated()) {
this._authService.refresh();
} else if (!this._authService.isAuthenticated()) {
return this._modal
.open({
content: UiMessageModalComponent,
title: 'Sie sind nicht mehr angemeldet',
data: { message: 'Sie werden neu angemeldet' },
})
.afterClosed$.subscribe(() => {
this._authService.login();
});
const strategy = this.injector.get(LoginStrategy);
return strategy.login('Sie sind nicht mehr angemeldet');
}
}
}

View File

@@ -1,11 +1,11 @@
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
import { APP_INITIALIZER, ErrorHandler, Injector, LOCALE_ID, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { PlatformModule } from '@angular/cdk/platform';
import { Config, ConfigModule, JsonConfigLoader } from '@core/config';
import { AuthModule, AuthService } from '@core/auth';
import { AuthModule, AuthService, LoginStrategy } from '@core/auth';
import { CoreCommandModule } from '@core/command';
import { AppRoutingModule } from './app-routing.module';
@@ -45,21 +45,14 @@ import { firstValueFrom } from 'rxjs';
registerLocaleData(localeDe, localeDeExtra);
registerLocaleData(localeDe, 'de', localeDeExtra);
export function _appInitializerFactory(
config: Config,
auth: AuthService,
injector: Injector,
scanAdapter: ScanAdapterService,
nativeContainer: NativeContainerService,
networkStatus: NetworkStatusService,
) {
export function _appInitializerFactory(config: Config, injector: Injector) {
return async () => {
const statusElement = document.querySelector('#init-status');
const laoderElement = document.querySelector('#init-loader');
try {
let online = false;
const networkStatus = injector.get(NetworkStatusService);
while (!online) {
online = await firstValueFrom(networkStatus.online$);
@@ -72,24 +65,34 @@ export function _appInitializerFactory(
statusElement.innerHTML = 'Konfigurationen werden geladen...';
await config.init();
statusElement.innerHTML = 'Authentifizierung wird geprüft...';
await auth.init();
if (auth.isAuthenticated()) {
statusElement.innerHTML = 'App wird initialisiert...';
const state = injector.get(RootStateService);
await state.init();
}
statusElement.innerHTML = 'Native Container wird initialisiert...';
await nativeContainer.init();
statusElement.innerHTML = 'Scanner wird initialisiert...';
const scanAdapter = injector.get(ScanAdapterService);
await scanAdapter.init();
statusElement.innerHTML = 'Authentifizierung wird geprüft...';
const auth = injector.get(AuthService);
try {
await auth.init();
} catch (error) {
statusElement.innerHTML = 'Authentifizierung wird durchgeführt...';
const strategy = injector.get(LoginStrategy);
await strategy.login();
}
statusElement.innerHTML = 'App wird initialisiert...';
const state = injector.get(RootStateService);
await state.init();
statusElement.innerHTML = 'Native Container wird initialisiert...';
const nativeContainer = injector.get(NativeContainerService);
await nativeContainer.init();
} catch (error) {
laoderElement.remove();
statusElement.classList.add('text-xl');
statusElement.innerHTML = '<b>Fehler bei der Initialisierung</b><br><br>Bitte prüfen Sie die Netzwerkverbindung (WLAN).<br><br>';
statusElement.innerHTML +=
'⚡<br><br><b>Fehler bei der Initialisierung</b><br><br>Bitte prüfen Sie die Netzwerkverbindung (WLAN).<br><br>';
const reload = document.createElement('button');
reload.classList.add('bg-brand', 'text-white', 'p-2', 'rounded', 'cursor-pointer');
@@ -123,10 +126,10 @@ export function _notificationsHubOptionsFactory(config: Config, auth: AuthServic
@NgModule({
declarations: [AppComponent, MainComponent],
bootstrap: [AppComponent],
imports: [
BrowserModule,
BrowserAnimationsModule,
HttpClientModule,
ShellModule.forRoot(),
AppRoutingModule,
AppSwaggerModule,
@@ -160,7 +163,7 @@ export function _notificationsHubOptionsFactory(config: Config, auth: AuthServic
provide: APP_INITIALIZER,
useFactory: _appInitializerFactory,
multi: true,
deps: [Config, AuthService, Injector, ScanAdapterService, NativeContainerService, NetworkStatusService],
deps: [Config, Injector],
},
{
provide: NOTIFICATIONS_HUB_OPTIONS,
@@ -182,7 +185,7 @@ export function _notificationsHubOptionsFactory(config: Config, auth: AuthServic
useClass: IsaErrorHandler,
},
{ provide: LOCALE_ID, useValue: 'de-DE' },
provideHttpClient(withInterceptorsFromDi()),
],
bootstrap: [AppComponent],
})
export class AppModule {}

View File

@@ -1,66 +1,66 @@
import { HttpErrorInterceptor } from './http-error.interceptor';
import { createServiceFactory, SpectatorService } from '@ngneat/spectator';
import { UiMessageModalComponent, UiModalResult, UiModalService } from '@ui/modal';
import { of, Subject, throwError } from 'rxjs';
import { HttpErrorResponse } from '@angular/common/http';
import { AuthService } from '@core/auth';
// import { HttpErrorInterceptor } from './http-error.interceptor';
// import { createServiceFactory, SpectatorService } from '@ngneat/spectator';
// import { UiMessageModalComponent, UiModalResult, UiModalService } from '@ui/modal';
// import { of, Subject, throwError } from 'rxjs';
// import { HttpErrorResponse } from '@angular/common/http';
// import { AuthService } from '@core/auth';
describe('HttpErrorInterceptor', () => {
let spectator: SpectatorService<HttpErrorInterceptor>;
let modalMock: jasmine.SpyObj<UiModalService>;
let httpErrorInterceptor: HttpErrorInterceptor;
// describe('HttpErrorInterceptor', () => {
// let spectator: SpectatorService<HttpErrorInterceptor>;
// let modalMock: jasmine.SpyObj<UiModalService>;
// let httpErrorInterceptor: HttpErrorInterceptor;
const createService = createServiceFactory({
service: HttpErrorInterceptor,
mocks: [UiModalService, AuthService],
});
// const createService = createServiceFactory({
// service: HttpErrorInterceptor,
// mocks: [UiModalService, AuthService],
// });
beforeEach(() => {
spectator = createService();
httpErrorInterceptor = spectator.service;
modalMock = spectator.inject(UiModalService);
// beforeEach(() => {
// spectator = createService();
// httpErrorInterceptor = spectator.service;
// modalMock = spectator.inject(UiModalService);
modalMock.open.and.returnValue({
afterClosed$: of({} as UiModalResult<any>),
} as any);
});
// modalMock.open.and.returnValue({
// afterClosed$: of({} as UiModalResult<any>),
// } as any);
// });
it('should be created', () => {
expect(httpErrorInterceptor).toBeTruthy();
});
// it('should be created', () => {
// expect(httpErrorInterceptor).toBeTruthy();
// });
describe('intercept', () => {
it('should catch the error and call handleError', (done) => {
const error = new HttpErrorResponse({
status: 0,
statusText: '',
url: '',
});
// describe('intercept', () => {
// it('should catch the error and call handleError', (done) => {
// const error = new HttpErrorResponse({
// status: 0,
// statusText: '',
// url: '',
// });
const handleErrorSpy = spyOn(httpErrorInterceptor, 'handleError').and.callThrough();
httpErrorInterceptor.intercept(null, { handle: () => throwError(error) }).subscribe({
error: () => {
expect(handleErrorSpy).toHaveBeenCalledWith(error);
done();
},
});
});
});
// const handleErrorSpy = spyOn(httpErrorInterceptor, 'handleError').and.callThrough();
// httpErrorInterceptor.intercept(null, { handle: () => throwError(error) }).subscribe({
// error: () => {
// expect(handleErrorSpy).toHaveBeenCalledWith(error);
// done();
// },
// });
// });
// });
describe('handleError', () => {
it('should call modal.open with offline message if status is 0', () => {
const error = {
error: {
message: 'test',
},
status: 0,
};
httpErrorInterceptor.handleError(error as any);
expect(modalMock.open).toHaveBeenCalledWith({
content: UiMessageModalComponent,
title: 'Sie sind offline, keine Verbindung zum Netzwerk',
data: { message: 'Bereits geladene Inhalte werden angezeigt. Interaktionen sind aktuell nicht möglich.' },
});
});
});
});
// describe('handleError', () => {
// it('should call modal.open with offline message if status is 0', () => {
// const error = {
// error: {
// message: 'test',
// },
// status: 0,
// };
// httpErrorInterceptor.handleError(error as any);
// expect(modalMock.open).toHaveBeenCalledWith({
// content: UiMessageModalComponent,
// title: 'Sie sind offline, keine Verbindung zum Netzwerk',
// data: { message: 'Bereits geladene Inhalte werden angezeigt. Interaktionen sind aktuell nicht möglich.' },
// });
// });
// });
// });

View File

@@ -1,9 +1,9 @@
import { Injectable } from '@angular/core';
import { inject, Injectable, Injector } from '@angular/core';
import { HttpInterceptor, HttpEvent, HttpHandler, HttpRequest, HttpErrorResponse } from '@angular/common/http';
import { NEVER, Observable, throwError } from 'rxjs';
import { from, NEVER, Observable, throwError } from 'rxjs';
import { UiMessageModalComponent, UiModalService } from '@ui/modal';
import { catchError, filter, mergeMap, takeUntil, tap } from 'rxjs/operators';
import { AuthService } from '@core/auth';
import { AuthService, LoginStrategy } from '@core/auth';
import { IsaLogProvider } from '../providers';
import { LogLevel } from '@core/logger';
import { injectOnline$ } from '../services/network-status.service';
@@ -11,6 +11,7 @@ import { injectOnline$ } from '../services/network-status.service';
@Injectable()
export class HttpErrorInterceptor implements HttpInterceptor {
readonly offline$ = injectOnline$().pipe(filter((online) => !online));
readonly injector = inject(Injector);
constructor(
private _modal: UiModalService,
@@ -27,18 +28,9 @@ export class HttpErrorInterceptor implements HttpInterceptor {
handleError(error: HttpErrorResponse): Observable<any> {
if (error.status === 401) {
return this._modal
.open({
content: UiMessageModalComponent,
title: 'Sie sind nicht mehr angemeldet',
data: { message: 'Sie werden neu angemeldet' },
})
.afterClosed$.pipe(
tap(() => {
this._auth.login();
}),
mergeMap(() => NEVER),
);
const strategy = this.injector.get(LoginStrategy);
return from(strategy.login('Sie sind nicht mehr angemeldet')).pipe(mergeMap(() => NEVER));
}
if (!error.url.endsWith('/isa/logging')) {

View File

@@ -0,0 +1 @@
export * from './network-status.service';

View File

@@ -7,6 +7,7 @@ import { RootState } from './root.state';
import packageInfo from 'packageJson';
import { environment } from '../../environments/environment';
import { Subject } from 'rxjs';
import { AuthService } from '@core/auth';
@Injectable({ providedIn: 'root' })
export class RootStateService {
@@ -15,6 +16,7 @@ export class RootStateService {
private _cancelSave = new Subject<void>();
constructor(
private readonly _authService: AuthService,
private readonly _userStateService: UserStateService,
private _logger: Logger,
private _store: Store,
@@ -41,7 +43,7 @@ export class RootStateService {
takeUntil(this._cancelSave),
debounceTime(1000),
switchMap((state) => {
const raw = JSON.stringify({ ...state, version: packageInfo.version });
const raw = JSON.stringify({ ...state, version: packageInfo.version, sub: this._authService.getClaimByKey('sub') });
RootStateService.SaveToLocalStorageRaw(raw);
return this._userStateService.UserStateSetUserState({ content: raw });
}),
@@ -96,7 +98,12 @@ export class RootStateService {
static LoadFromLocalStorage(): RootState {
const raw = RootStateService.LoadFromLocalStorageRaw();
if (raw) {
return JSON.parse(raw);
try {
return JSON.parse(raw);
} catch (error) {
console.error('Error parsing local storage:', error);
this.RemoveFromLocalStorage();
}
}
return undefined;
}

View File

@@ -1,4 +1,4 @@
import { Directive, HostListener, Input } from '@angular/core';
import { Directive, HostListener, inject, Input } from '@angular/core';
import { ProductCatalogNavigationService } from '@shared/services/navigation';
@Directive({
@@ -8,7 +8,7 @@ import { ProductCatalogNavigationService } from '@shared/services/navigation';
export class NavigateOnClickDirective {
@Input('productImageNavigation') ean: string;
constructor(private readonly _productCatalogNavigation: ProductCatalogNavigationService) {}
private readonly _productCatalogNavigation = inject(ProductCatalogNavigationService);
@HostListener('click', ['$event'])
async onClick(event: MouseEvent) {

View File

@@ -73,7 +73,7 @@
},
"checkForUpdates": 3600000,
"licence": {
"scandit": "Ac2kvx5ZOzjvFl/LuAd6wds3C30YJ4g8Cm6PX4sgUnKPePVMuH+rFQIyVNn1YdS3myORojEOBsIZWhMw2nRUGBtOnQ5FO+cRHgQu0pkP+VG6OYvt8ETUTn8Aa2f9bmfqclO3LI8WN8psWr+adkZEtqNTvCgyDLZaICh8S7RfmwJVVWaOPX4LDagGhLDPS2YQdg+ibpR0l0ZlX2h/3GttofE64HOlBN3QtDB8yihHJNgVcUVy2UQVS+BXOyvIfZEFbFgPYVt5HZ0aQgcISlYVAmJvRsHyeKYRUnIi3ZN73EQmHzWcOV4/HWVoTs1MTW0mxV074vNwqExYW9LYmz9zgLUD7FMnKMFClkzRBHRN1CC7brosCnFkZWQp3CV4Ua48Fn9GW0Zpn/4MIZgzSU26inl5ZnT3dCc2+3BH3Us1uugTUrOPwFuwsPpr4NZYXWJMOmcm4kBBVJd0Uwk28GyZM7x1hXADcQgcc23+gDtbbUZWVmr3TE7GBcV3j+XUeOk8nHZw3DsYJ46MT2sSfks3QXRl4tBkBad6M+UxW4tb2IQC/4K9IXKhDN8VYXpzrrRHJNvOiH5+NrixthDZHHd/MGNuBDrJFOuXq8L7O6PxBoVZ0NPXmCO5vKyYdhBz5gJ5u3vNkSke7p+cDcvFicsVyNWRaaoFL0UN9gxFVMY8hkJKBZyGxZL2LEDXD44PxPsDnygpDC1Jyc7pkhEA0fKSc9aXZb1HDpwe1hqyyRVBtHkgPiN78GmJImbaUlj8XsK3yVyAjs2XvPR0/3ASHS53ViwzWKL3Oi8I515IJDtDhlvrkeh7MrWMaEu9k6ZQXJi1uJsh3JGfyS+yDlPjePVdsVqnZ4uw9pKMXvjlJeNcIiGM2Cf5S89nk2Qe/56MrKB9Frm6Q5wQHai8TNdCZYBi67dmAX8KHPXvEU4K6KyyW89YGcabZQ3eOJDr4oqW9ZcYPBttREdH3WI/HxvpEq6bqoDhT9AxpIWEMVb6y/DcDHSEresepibug4qOr9xOPq0yk2uiWYhPubFnCk7thQCXOGv9crWnQoOrt9c1qoaWXM4YmKqfcaQ67Tn+uFQmYTZyqw4jlJU4GgKe5/GNCVQM5aNUg1J0Px1NlFCS+rrIDyMQp0byFcgTd/E9sA5d1+YZKHKmJiQwEAz6oU9yyoUlxntSI42GHB/UttPc7Hj14V5+oJ+Yz+CZodmkXFg57Vx4NuxveNtO"
"scandit": "ApI1Sg5/KzWICPhn0CbflI4cjgXiFkKnhgEmUvAt0D3CWsck0SCIm557hJvGTqXD0A1mt3IvEwA8eQ1CBXL3EoA9lASWUcLt+FSWP9gHao+LT6Oz9QMrsJoJRC/OboodKS4/F+ZVa02eUT53X0GX9jJeEnh8EerqZimrl6oDjNLvHCzBnxtmsMZidAUaErqK5yaQQCkUKcfTQhRzVhqvsjdENR76EmrrvCSr93oMCnnDHjcyOz8O3stcNrCOGbxwDBIMwf05CzoEBhxF9SoT/fwvfWa9BRvlMTxl9lUUE2f3Aw9+0CBG7PYgZ9XGCiCM2Wy9T5cf0C5MOLr2MAIgQrlDua0oEtmC5FjFojQHJEITRmLyQnAyUU8Y+jkUUCtQNmweZKtTQT9le+G9QCzRoSsmVsVxCKxII3+8iLA+Lh9meI/vJSaQ2VpDgZVrV1Vc4V/z0Exu7Ap7R0MpzCy4hhRzdpWgf4OG815ZctwMvM7qfBqykG98B9oP3YC/LG+cpVK4Ipx5VnWBUIwVHDiocHhMjyroTBAeH3BfeCdOEpKwNK9vOxrbvUpqAc8ZOaaal08hWgFVFR8BQN+rEkiqKx5fONRXefkkyUWMdqwvoBn7ZzCr+F9GmD52Q7ZuDcyAigm2ArwDipT9QUH7gxphogcKRNAWAyaENWWKRBFWmeNYXJOuGXLr05Zye7joGFbJHxPYyUJ6KMombmYlI2GHc4JQGJNneDuL93v8AkYcfM2WfOjf0hm+SVw0TP95bD1xJwSCoKO5L6FxqMu5GPcOqik3sjGHg6XsEr10m7jt7G2PfNTgS3ft+TP32OjeSu9M80Fu3nmeqLM4a4lx9fFbGMpWDEU9wdftmPByeW7KoXyS2bkd1Mg9UP8ci0nKo2d/ThPn+iMEjXSibNeIGibaIT4ys3pjDLW2mx0IxQpwnuQfZzAHBszpMkwKuG4+FrqXff9+bZGuRQlVwhm2v5xbKRjgi99i14QkuNVUkePxllLZGFgQH0DjsLINZb+R9stBYjQmMGc2kkJfyhHx2/zvT2D2mpgcaq0ZRKco2CqDiLUJoHwLPse/fu4PWvqSKVpwiqhCehuloWTE3jkFUncwDI50hPvrfCx92cOJUE8RTsQgDoAh7JKsPHctSZfUicZyLHOuHMhtIoxs6l/lzFowbhZob/Uo7aRsipOcP/iZgEPnMEu/obXcLR3QrlQ+WuEppU4g2MZ9+xENLCqVDLEHiz+jZIvl42ePc4Ip4lJ5RQFcSSR1yYb+z/4KB/3bc308PHnEMEB5TYvVpK7M3kg2CTjos5lh733qQKwqYamgFjpr5oPUxRYwRvj41GYpcLN3Tr4WOi4lfevR5eVFBu1Jc9baY8JIOvcCL5WhXsEemnx5dUhcnP2ozloFCvU1IS60KHkXtF4NJl89tu5EECquTN5Lf9cvtIhlbmPsVg=="
},
"gender": {
"0": "Keine Anrede",

View File

@@ -72,7 +72,7 @@
},
"checkForUpdates": 900000,
"licence": {
"scandit": "Ac2kvx5ZOzjvFl/LuAd6wds3C30YJ4g8Cm6PX4sgUnKPePVMuH+rFQIyVNn1YdS3myORojEOBsIZWhMw2nRUGBtOnQ5FO+cRHgQu0pkP+VG6OYvt8ETUTn8Aa2f9bmfqclO3LI8WN8psWr+adkZEtqNTvCgyDLZaICh8S7RfmwJVVWaOPX4LDagGhLDPS2YQdg+ibpR0l0ZlX2h/3GttofE64HOlBN3QtDB8yihHJNgVcUVy2UQVS+BXOyvIfZEFbFgPYVt5HZ0aQgcISlYVAmJvRsHyeKYRUnIi3ZN73EQmHzWcOV4/HWVoTs1MTW0mxV074vNwqExYW9LYmz9zgLUD7FMnKMFClkzRBHRN1CC7brosCnFkZWQp3CV4Ua48Fn9GW0Zpn/4MIZgzSU26inl5ZnT3dCc2+3BH3Us1uugTUrOPwFuwsPpr4NZYXWJMOmcm4kBBVJd0Uwk28GyZM7x1hXADcQgcc23+gDtbbUZWVmr3TE7GBcV3j+XUeOk8nHZw3DsYJ46MT2sSfks3QXRl4tBkBad6M+UxW4tb2IQC/4K9IXKhDN8VYXpzrrRHJNvOiH5+NrixthDZHHd/MGNuBDrJFOuXq8L7O6PxBoVZ0NPXmCO5vKyYdhBz5gJ5u3vNkSke7p+cDcvFicsVyNWRaaoFL0UN9gxFVMY8hkJKBZyGxZL2LEDXD44PxPsDnygpDC1Jyc7pkhEA0fKSc9aXZb1HDpwe1hqyyRVBtHkgPiN78GmJImbaUlj8XsK3yVyAjs2XvPR0/3ASHS53ViwzWKL3Oi8I515IJDtDhlvrkeh7MrWMaEu9k6ZQXJi1uJsh3JGfyS+yDlPjePVdsVqnZ4uw9pKMXvjlJeNcIiGM2Cf5S89nk2Qe/56MrKB9Frm6Q5wQHai8TNdCZYBi67dmAX8KHPXvEU4K6KyyW89YGcabZQ3eOJDr4oqW9ZcYPBttREdH3WI/HxvpEq6bqoDhT9AxpIWEMVb6y/DcDHSEresepibug4qOr9xOPq0yk2uiWYhPubFnCk7thQCXOGv9crWnQoOrt9c1qoaWXM4YmKqfcaQ67Tn+uFQmYTZyqw4jlJU4GgKe5/GNCVQM5aNUg1J0Px1NlFCS+rrIDyMQp0byFcgTd/E9sA5d1+YZKHKmJiQwEAz6oU9yyoUlxntSI42GHB/UttPc7Hj14V5+oJ+Yz+CZodmkXFg57Vx4NuxveNtO"
"scandit": "ApI1Sg5/KzWICPhn0CbflI4cjgXiFkKnhgEmUvAt0D3CWsck0SCIm557hJvGTqXD0A1mt3IvEwA8eQ1CBXL3EoA9lASWUcLt+FSWP9gHao+LT6Oz9QMrsJoJRC/OboodKS4/F+ZVa02eUT53X0GX9jJeEnh8EerqZimrl6oDjNLvHCzBnxtmsMZidAUaErqK5yaQQCkUKcfTQhRzVhqvsjdENR76EmrrvCSr93oMCnnDHjcyOz8O3stcNrCOGbxwDBIMwf05CzoEBhxF9SoT/fwvfWa9BRvlMTxl9lUUE2f3Aw9+0CBG7PYgZ9XGCiCM2Wy9T5cf0C5MOLr2MAIgQrlDua0oEtmC5FjFojQHJEITRmLyQnAyUU8Y+jkUUCtQNmweZKtTQT9le+G9QCzRoSsmVsVxCKxII3+8iLA+Lh9meI/vJSaQ2VpDgZVrV1Vc4V/z0Exu7Ap7R0MpzCy4hhRzdpWgf4OG815ZctwMvM7qfBqykG98B9oP3YC/LG+cpVK4Ipx5VnWBUIwVHDiocHhMjyroTBAeH3BfeCdOEpKwNK9vOxrbvUpqAc8ZOaaal08hWgFVFR8BQN+rEkiqKx5fONRXefkkyUWMdqwvoBn7ZzCr+F9GmD52Q7ZuDcyAigm2ArwDipT9QUH7gxphogcKRNAWAyaENWWKRBFWmeNYXJOuGXLr05Zye7joGFbJHxPYyUJ6KMombmYlI2GHc4JQGJNneDuL93v8AkYcfM2WfOjf0hm+SVw0TP95bD1xJwSCoKO5L6FxqMu5GPcOqik3sjGHg6XsEr10m7jt7G2PfNTgS3ft+TP32OjeSu9M80Fu3nmeqLM4a4lx9fFbGMpWDEU9wdftmPByeW7KoXyS2bkd1Mg9UP8ci0nKo2d/ThPn+iMEjXSibNeIGibaIT4ys3pjDLW2mx0IxQpwnuQfZzAHBszpMkwKuG4+FrqXff9+bZGuRQlVwhm2v5xbKRjgi99i14QkuNVUkePxllLZGFgQH0DjsLINZb+R9stBYjQmMGc2kkJfyhHx2/zvT2D2mpgcaq0ZRKco2CqDiLUJoHwLPse/fu4PWvqSKVpwiqhCehuloWTE3jkFUncwDI50hPvrfCx92cOJUE8RTsQgDoAh7JKsPHctSZfUicZyLHOuHMhtIoxs6l/lzFowbhZob/Uo7aRsipOcP/iZgEPnMEu/obXcLR3QrlQ+WuEppU4g2MZ9+xENLCqVDLEHiz+jZIvl42ePc4Ip4lJ5RQFcSSR1yYb+z/4KB/3bc308PHnEMEB5TYvVpK7M3kg2CTjos5lh733qQKwqYamgFjpr5oPUxRYwRvj41GYpcLN3Tr4WOi4lfevR5eVFBu1Jc9baY8JIOvcCL5WhXsEemnx5dUhcnP2ozloFCvU1IS60KHkXtF4NJl89tu5EECquTN5Lf9cvtIhlbmPsVg=="
},
"gender": {
"0": "Keine Anrede",

View File

@@ -4,6 +4,7 @@
"interval": 300000
},
"debug": true,
"dev-scanner": true,
"@cdn/product-image": {
"url": "https://produktbilder.paragon-data.net"
},
@@ -74,7 +75,7 @@
},
"checkForUpdates": 3600000,
"licence": {
"scandit": "Ac2kvx5ZOzjvFl/LuAd6wds3C30YJ4g8Cm6PX4sgUnKPePVMuH+rFQIyVNn1YdS3myORojEOBsIZWhMw2nRUGBtOnQ5FO+cRHgQu0pkP+VG6OYvt8ETUTn8Aa2f9bmfqclO3LI8WN8psWr+adkZEtqNTvCgyDLZaICh8S7RfmwJVVWaOPX4LDagGhLDPS2YQdg+ibpR0l0ZlX2h/3GttofE64HOlBN3QtDB8yihHJNgVcUVy2UQVS+BXOyvIfZEFbFgPYVt5HZ0aQgcISlYVAmJvRsHyeKYRUnIi3ZN73EQmHzWcOV4/HWVoTs1MTW0mxV074vNwqExYW9LYmz9zgLUD7FMnKMFClkzRBHRN1CC7brosCnFkZWQp3CV4Ua48Fn9GW0Zpn/4MIZgzSU26inl5ZnT3dCc2+3BH3Us1uugTUrOPwFuwsPpr4NZYXWJMOmcm4kBBVJd0Uwk28GyZM7x1hXADcQgcc23+gDtbbUZWVmr3TE7GBcV3j+XUeOk8nHZw3DsYJ46MT2sSfks3QXRl4tBkBad6M+UxW4tb2IQC/4K9IXKhDN8VYXpzrrRHJNvOiH5+NrixthDZHHd/MGNuBDrJFOuXq8L7O6PxBoVZ0NPXmCO5vKyYdhBz5gJ5u3vNkSke7p+cDcvFicsVyNWRaaoFL0UN9gxFVMY8hkJKBZyGxZL2LEDXD44PxPsDnygpDC1Jyc7pkhEA0fKSc9aXZb1HDpwe1hqyyRVBtHkgPiN78GmJImbaUlj8XsK3yVyAjs2XvPR0/3ASHS53ViwzWKL3Oi8I515IJDtDhlvrkeh7MrWMaEu9k6ZQXJi1uJsh3JGfyS+yDlPjePVdsVqnZ4uw9pKMXvjlJeNcIiGM2Cf5S89nk2Qe/56MrKB9Frm6Q5wQHai8TNdCZYBi67dmAX8KHPXvEU4K6KyyW89YGcabZQ3eOJDr4oqW9ZcYPBttREdH3WI/HxvpEq6bqoDhT9AxpIWEMVb6y/DcDHSEresepibug4qOr9xOPq0yk2uiWYhPubFnCk7thQCXOGv9crWnQoOrt9c1qoaWXM4YmKqfcaQ67Tn+uFQmYTZyqw4jlJU4GgKe5/GNCVQM5aNUg1J0Px1NlFCS+rrIDyMQp0byFcgTd/E9sA5d1+YZKHKmJiQwEAz6oU9yyoUlxntSI42GHB/UttPc7Hj14V5+oJ+Yz+CZodmkXFg57Vx4NuxveNtO"
"scandit": "ApI1Sg5/KzWICPhn0CbflI4cjgXiFkKnhgEmUvAt0D3CWsck0SCIm557hJvGTqXD0A1mt3IvEwA8eQ1CBXL3EoA9lASWUcLt+FSWP9gHao+LT6Oz9QMrsJoJRC/OboodKS4/F+ZVa02eUT53X0GX9jJeEnh8EerqZimrl6oDjNLvHCzBnxtmsMZidAUaErqK5yaQQCkUKcfTQhRzVhqvsjdENR76EmrrvCSr93oMCnnDHjcyOz8O3stcNrCOGbxwDBIMwf05CzoEBhxF9SoT/fwvfWa9BRvlMTxl9lUUE2f3Aw9+0CBG7PYgZ9XGCiCM2Wy9T5cf0C5MOLr2MAIgQrlDua0oEtmC5FjFojQHJEITRmLyQnAyUU8Y+jkUUCtQNmweZKtTQT9le+G9QCzRoSsmVsVxCKxII3+8iLA+Lh9meI/vJSaQ2VpDgZVrV1Vc4V/z0Exu7Ap7R0MpzCy4hhRzdpWgf4OG815ZctwMvM7qfBqykG98B9oP3YC/LG+cpVK4Ipx5VnWBUIwVHDiocHhMjyroTBAeH3BfeCdOEpKwNK9vOxrbvUpqAc8ZOaaal08hWgFVFR8BQN+rEkiqKx5fONRXefkkyUWMdqwvoBn7ZzCr+F9GmD52Q7ZuDcyAigm2ArwDipT9QUH7gxphogcKRNAWAyaENWWKRBFWmeNYXJOuGXLr05Zye7joGFbJHxPYyUJ6KMombmYlI2GHc4JQGJNneDuL93v8AkYcfM2WfOjf0hm+SVw0TP95bD1xJwSCoKO5L6FxqMu5GPcOqik3sjGHg6XsEr10m7jt7G2PfNTgS3ft+TP32OjeSu9M80Fu3nmeqLM4a4lx9fFbGMpWDEU9wdftmPByeW7KoXyS2bkd1Mg9UP8ci0nKo2d/ThPn+iMEjXSibNeIGibaIT4ys3pjDLW2mx0IxQpwnuQfZzAHBszpMkwKuG4+FrqXff9+bZGuRQlVwhm2v5xbKRjgi99i14QkuNVUkePxllLZGFgQH0DjsLINZb+R9stBYjQmMGc2kkJfyhHx2/zvT2D2mpgcaq0ZRKco2CqDiLUJoHwLPse/fu4PWvqSKVpwiqhCehuloWTE3jkFUncwDI50hPvrfCx92cOJUE8RTsQgDoAh7JKsPHctSZfUicZyLHOuHMhtIoxs6l/lzFowbhZob/Uo7aRsipOcP/iZgEPnMEu/obXcLR3QrlQ+WuEppU4g2MZ9+xENLCqVDLEHiz+jZIvl42ePc4Ip4lJ5RQFcSSR1yYb+z/4KB/3bc308PHnEMEB5TYvVpK7M3kg2CTjos5lh733qQKwqYamgFjpr5oPUxRYwRvj41GYpcLN3Tr4WOi4lfevR5eVFBu1Jc9baY8JIOvcCL5WhXsEemnx5dUhcnP2ozloFCvU1IS60KHkXtF4NJl89tu5EECquTN5Lf9cvtIhlbmPsVg=="
},
"gender": {
"0": "Keine Anrede",

View File

@@ -73,7 +73,7 @@
},
"checkForUpdates": 3600000,
"licence": {
"scandit": "AZ70hgtZLmFWHbYP+BDq8VAEgAxmNGYcPU8YpOc3DryEXj4zMzYQFrQuUm0YfzKaR2xbfjsImjXJN7C/TE1CYXsv3DeqIzgDRA/kII4LzqoICWm9mnwejER1kMu1Vf+1NWbwUuYJjsQVQXYkwkWgnv53obevBZtjpgZqIzlStufIU+sPNEWSd9BlrLmiSEu6nXC5lbstyPJVaqihsFoBPNJ0Q+IgJUjqaxhTf6hGtNB3Rqcpv0ZT349NdK9mQ+9lAQYCwXdmYbELdlbZVSja0aJQlkf0TaAjixDbNcR32/VMIw98F2YxVRVWPnADUaJ9Bn8oa7NkOJIUfB7VAg1KtUl8IIwSXvRk436UtPZGxg+PQM7UXX/TgiBuqCQWdUChoH/QAoY2fyKRKgXJHnGdt+pBRQ2OYs7TZWCJLZsLKgnHV+eWh3Y3EB1hN9cmcszdnmYiyD1gCY8pSETFbWV01nVPvIuUTc+jMipv3NN81VQDS5/eU1VJNpRuDiufdBKJv0jjHQk+hTdqe+2GSmMD24ZG8FQXJZZPBCodWR1A81T8toCaIAGHnOfZeO7nxZu9Uo+6ohrVxc5F4szSizra+M3zfuFnm5FGgxCKt5uibLYgRyNcmR8/9vqTfMZgalTklbKHEFw1i+xOwrFrqRMh7FoovDRoI3QuaHLpV+ZUpZo3zTOQofdXPthKAmSLc11cjn509oTnnXUBBoBn/hErrwC/v8dZep1YEKM3wjfuIFsHRrsECswqNjwN/yOZCirm2VvFJEPMuKIwy+8jdNGncm8v4R9Br3c+wYJ8696L7Dg0iFShoyoP4OqIvBNY+dYyy935yGkIF2KI6l01xQZcWoHSVKPd6/78Iwy4lTKhtOqtu7ETJqOE53gQpR9jaAY0RiBB1SLm+Jbwt3ipSJiCDgKmkVmk5AU9HC0XYR/erjg13HF4hIcpLPW1ZWMKSxTqZ8z8FMJilInBgfcBwvjNE5seWvdFKltvlItnGhSh7BLUZ5UInDhl00NJBE2PdstDvRvQjLKvYUoFly3jONVsUfuQpzrcjT0g6gEgL8ZtlmaF11owcCvqhNDxWuTAJTX/xXf6WehxEB0qy3xCdxahbuneS3DPI0z2kAQwrx19i5r+RKQ8bWDaQ/OGY47sLPJgDgijEz16CvAMBSN0PvOso6FstsT/ynR3LLTPcY0QDw21Dv3wNHNa9HbanJcb3/MuhZGlrKp7"
"scandit": "AqeFthV/MMKEPoQNIB3vSLYDthFhJ0F03QQtH+4f/ivCRKdYPjxnMU94RVMEaldhNTQj5sB8mJVNWq0vbHCzqul36KcLdG0kwFfBiXAoEkfOe2bRGTCX4bASLuwfHVv8+AG+Pwdhelf3XMcIvnrGkiR91EmRGeebiiVX448H6U+WdyXJtns/W/ZgdMZBGUHA4WsG9Ct5SyLGaeGYI1iK8LZtpKzLBcsvYwOkDeI4IjIuOZpMpy6CzmBAHQlAHWKmwjDizWwB2rU2BZ6i+3H99mpvSz9PCWm9PS+Jr30Me13bb+Oz5zHxeMdoJtZNIhsTgWyVErUlcuceNIibrDj84L4qfmHKPQtalW10+w9ODwRIS+nLCWOpo9we+UCsFgMJLX3lH0ZIXEWEZjAxhzXmdxxGEcroQQXAIH3PsN8RUSuaC6PPOirvKmZUbW3XUun+vmxMWxhDyGwnat9CpDpQ+CpsyPECaUEkbkVEg/Mq42QxVHF25UgZ37c5gN0wHNAjal/HSpZfLBzDeC5Y/iKqgKhjf68Bf7lnOBYokWFUxBRdcKLhtz3nGNZL3UpabCULf2J5lOZ0Mj8hdC+P4UNZMCRVEs4UR9pqHEoJVdQ5KMO8cXcvKGxGG4hT9UNNbk9o+jNWOlhCribVTmkj6kmInB8zDFkceKy3HUSC8p9TlzjxfBpzN0oVMG9DIlkTWh3nryr4lDJimIz4AriI6Xft3NJgiIkUVAjsTHl33IRz7jwyQOzyUR197o0ojRwvLpm3zkHwap4lyvMz7SoYhKoyN66Aul3K5iic8hVVUuriChEZyIwYml71vAfIpmwZC5GVF5XJBT1r0OG+xmJbH6sae0gOnMpFyed3R+BH/AU2OY9nrnTNLN83pk8huK7BPQVnpAQqU76IrZP5GAyd1GNRi9WWJo4mHefe/9/x22lfHg7BELhOXoJlDZWoj4uOYJVVycQD/ninQMtwkry/9VEOQmrg76bRVbz2OZ3RFDw/AXBd+1jA0K7gIP9G5dN4x2NjNbzPE4gMhHvNi+vUy8x+1uHRQdBzI/F7EkuFb59eKZYe1EHTofnzGofZUgiuXMDDVTi8dE80PmFRCTnNvEtK8rRcBFSqWxeX1u5h+PR8btTagOH0hNCNWUaVYpIcHXvKP5gFFluPy5+RR3TRXYhBKOeb+Q2uxrowwVJs2FOxwfORtKXti8pGIw9bVZk9yNCqafPuURqGKMw/e+H8ElIWaiWH2mcY9dDKN6lBhOdKYjhKQZ/4t9H+ZbyeKzebH8habpAZEDdN0fbqDMwWTD+eKo02s5wOzJ+0z4BpDszMd2acvQ1GVrF0KajpReco2FE+AQpbxHM2KQfKqiDHOUd02nG9AHjKRf/xUTySpD8pG3DvPZC+ImqQZN/t7/IegQptgrezq2w2y0L89sgNnnhRopaMRt/sPSirTXh/NA=="
},
"gender": {
"0": "Keine Anrede",

View File

@@ -73,7 +73,7 @@
},
"checkForUpdates": 3600000,
"licence": {
"scandit": "AZ70hgtZLmFWHbYP+BDq8VAEgAxmNGYcPU8YpOc3DryEXj4zMzYQFrQuUm0YfzKaR2xbfjsImjXJN7C/TE1CYXsv3DeqIzgDRA/kII4LzqoICWm9mnwejER1kMu1Vf+1NWbwUuYJjsQVQXYkwkWgnv53obevBZtjpgZqIzlStufIU+sPNEWSd9BlrLmiSEu6nXC5lbstyPJVaqihsFoBPNJ0Q+IgJUjqaxhTf6hGtNB3Rqcpv0ZT349NdK9mQ+9lAQYCwXdmYbELdlbZVSja0aJQlkf0TaAjixDbNcR32/VMIw98F2YxVRVWPnADUaJ9Bn8oa7NkOJIUfB7VAg1KtUl8IIwSXvRk436UtPZGxg+PQM7UXX/TgiBuqCQWdUChoH/QAoY2fyKRKgXJHnGdt+pBRQ2OYs7TZWCJLZsLKgnHV+eWh3Y3EB1hN9cmcszdnmYiyD1gCY8pSETFbWV01nVPvIuUTc+jMipv3NN81VQDS5/eU1VJNpRuDiufdBKJv0jjHQk+hTdqe+2GSmMD24ZG8FQXJZZPBCodWR1A81T8toCaIAGHnOfZeO7nxZu9Uo+6ohrVxc5F4szSizra+M3zfuFnm5FGgxCKt5uibLYgRyNcmR8/9vqTfMZgalTklbKHEFw1i+xOwrFrqRMh7FoovDRoI3QuaHLpV+ZUpZo3zTOQofdXPthKAmSLc11cjn509oTnnXUBBoBn/hErrwC/v8dZep1YEKM3wjfuIFsHRrsECswqNjwN/yOZCirm2VvFJEPMuKIwy+8jdNGncm8v4R9Br3c+wYJ8696L7Dg0iFShoyoP4OqIvBNY+dYyy935yGkIF2KI6l01xQZcWoHSVKPd6/78Iwy4lTKhtOqtu7ETJqOE53gQpR9jaAY0RiBB1SLm+Jbwt3ipSJiCDgKmkVmk5AU9HC0XYR/erjg13HF4hIcpLPW1ZWMKSxTqZ8z8FMJilInBgfcBwvjNE5seWvdFKltvlItnGhSh7BLUZ5UInDhl00NJBE2PdstDvRvQjLKvYUoFly3jONVsUfuQpzrcjT0g6gEgL8ZtlmaF11owcCvqhNDxWuTAJTX/xXf6WehxEB0qy3xCdxahbuneS3DPI0z2kAQwrx19i5r+RKQ8bWDaQ/OGY47sLPJgDgijEz16CvAMBSN0PvOso6FstsT/ynR3LLTPcY0QDw21Dv3wNHNa9HbanJcb3/MuhZGlrKp7"
"scandit": "AqeFthV/MMKEPoQNIB3vSLYDthFhJ0F03QQtH+4f/ivCRKdYPjxnMU94RVMEaldhNTQj5sB8mJVNWq0vbHCzqul36KcLdG0kwFfBiXAoEkfOe2bRGTCX4bASLuwfHVv8+AG+Pwdhelf3XMcIvnrGkiR91EmRGeebiiVX448H6U+WdyXJtns/W/ZgdMZBGUHA4WsG9Ct5SyLGaeGYI1iK8LZtpKzLBcsvYwOkDeI4IjIuOZpMpy6CzmBAHQlAHWKmwjDizWwB2rU2BZ6i+3H99mpvSz9PCWm9PS+Jr30Me13bb+Oz5zHxeMdoJtZNIhsTgWyVErUlcuceNIibrDj84L4qfmHKPQtalW10+w9ODwRIS+nLCWOpo9we+UCsFgMJLX3lH0ZIXEWEZjAxhzXmdxxGEcroQQXAIH3PsN8RUSuaC6PPOirvKmZUbW3XUun+vmxMWxhDyGwnat9CpDpQ+CpsyPECaUEkbkVEg/Mq42QxVHF25UgZ37c5gN0wHNAjal/HSpZfLBzDeC5Y/iKqgKhjf68Bf7lnOBYokWFUxBRdcKLhtz3nGNZL3UpabCULf2J5lOZ0Mj8hdC+P4UNZMCRVEs4UR9pqHEoJVdQ5KMO8cXcvKGxGG4hT9UNNbk9o+jNWOlhCribVTmkj6kmInB8zDFkceKy3HUSC8p9TlzjxfBpzN0oVMG9DIlkTWh3nryr4lDJimIz4AriI6Xft3NJgiIkUVAjsTHl33IRz7jwyQOzyUR197o0ojRwvLpm3zkHwap4lyvMz7SoYhKoyN66Aul3K5iic8hVVUuriChEZyIwYml71vAfIpmwZC5GVF5XJBT1r0OG+xmJbH6sae0gOnMpFyed3R+BH/AU2OY9nrnTNLN83pk8huK7BPQVnpAQqU76IrZP5GAyd1GNRi9WWJo4mHefe/9/x22lfHg7BELhOXoJlDZWoj4uOYJVVycQD/ninQMtwkry/9VEOQmrg76bRVbz2OZ3RFDw/AXBd+1jA0K7gIP9G5dN4x2NjNbzPE4gMhHvNi+vUy8x+1uHRQdBzI/F7EkuFb59eKZYe1EHTofnzGofZUgiuXMDDVTi8dE80PmFRCTnNvEtK8rRcBFSqWxeX1u5h+PR8btTagOH0hNCNWUaVYpIcHXvKP5gFFluPy5+RR3TRXYhBKOeb+Q2uxrowwVJs2FOxwfORtKXti8pGIw9bVZk9yNCqafPuURqGKMw/e+H8ElIWaiWH2mcY9dDKN6lBhOdKYjhKQZ/4t9H+ZbyeKzebH8habpAZEDdN0fbqDMwWTD+eKo02s5wOzJ+0z4BpDszMd2acvQ1GVrF0KajpReco2FE+AQpbxHM2KQfKqiDHOUd02nG9AHjKRf/xUTySpD8pG3DvPZC+ImqQZN/t7/IegQptgrezq2w2y0L89sgNnnhRopaMRt/sPSirTXh/NA=="
},
"gender": {
"0": "Keine Anrede",

View File

@@ -3,7 +3,8 @@
"silentRefresh": {
"interval": 300000
},
"debug": true,
"debug": false,
"@cdn/product-image": {
"url": "https://produktbilder.paragon-data.net"
},
@@ -74,8 +75,7 @@
},
"checkForUpdates": 3600000,
"licence": {
"scandit": "Ac2kvx5ZOzjvFl/LuAd6wds3C30YJ4g8Cm6PX4sgUnKPePVMuH+rFQIyVNn1YdS3myORojEOBsIZWhMw2nRUGBtOnQ5FO+cRHgQu0pkP+VG6OYvt8ETUTn8Aa2f9bmfqclO3LI8WN8psWr+adkZEtqNTvCgyDLZaICh8S7RfmwJVVWaOPX4LDagGhLDPS2YQdg+ibpR0l0ZlX2h/3GttofE64HOlBN3QtDB8yihHJNgVcUVy2UQVS+BXOyvIfZEFbFgPYVt5HZ0aQgcISlYVAmJvRsHyeKYRUnIi3ZN73EQmHzWcOV4/HWVoTs1MTW0mxV074vNwqExYW9LYmz9zgLUD7FMnKMFClkzRBHRN1CC7brosCnFkZWQp3CV4Ua48Fn9GW0Zpn/4MIZgzSU26inl5ZnT3dCc2+3BH3Us1uugTUrOPwFuwsPpr4NZYXWJMOmcm4kBBVJd0Uwk28GyZM7x1hXADcQgcc23+gDtbbUZWVmr3TE7GBcV3j+XUeOk8nHZw3DsYJ46MT2sSfks3QXRl4tBkBad6M+UxW4tb2IQC/4K9IXKhDN8VYXpzrrRHJNvOiH5+NrixthDZHHd/MGNuBDrJFOuXq8L7O6PxBoVZ0NPXmCO5vKyYdhBz5gJ5u3vNkSke7p+cDcvFicsVyNWRaaoFL0UN9gxFVMY8hkJKBZyGxZL2LEDXD44PxPsDnygpDC1Jyc7pkhEA0fKSc9aXZb1HDpwe1hqyyRVBtHkgPiN78GmJImbaUlj8XsK3yVyAjs2XvPR0/3ASHS53ViwzWKL3Oi8I515IJDtDhlvrkeh7MrWMaEu9k6ZQXJi1uJsh3JGfyS+yDlPjePVdsVqnZ4uw9pKMXvjlJeNcIiGM2Cf5S89nk2Qe/56MrKB9Frm6Q5wQHai8TNdCZYBi67dmAX8KHPXvEU4K6KyyW89YGcabZQ3eOJDr4oqW9ZcYPBttREdH3WI/HxvpEq6bqoDhT9AxpIWEMVb6y/DcDHSEresepibug4qOr9xOPq0yk2uiWYhPubFnCk7thQCXOGv9crWnQoOrt9c1qoaWXM4YmKqfcaQ67Tn+uFQmYTZyqw4jlJU4GgKe5/GNCVQM5aNUg1J0Px1NlFCS+rrIDyMQp0byFcgTd/E9sA5d1+YZKHKmJiQwEAz6oU9yyoUlxntSI42GHB/UttPc7Hj14V5+oJ+Yz+CZodmkXFg57Vx4NuxveNtO"
},
"scandit": "ApI1Sg5/KzWICPhn0CbflI4cjgXiFkKnhgEmUvAt0D3CWsck0SCIm557hJvGTqXD0A1mt3IvEwA8eQ1CBXL3EoA9lASWUcLt+FSWP9gHao+LT6Oz9QMrsJoJRC/OboodKS4/F+ZVa02eUT53X0GX9jJeEnh8EerqZimrl6oDjNLvHCzBnxtmsMZidAUaErqK5yaQQCkUKcfTQhRzVhqvsjdENR76EmrrvCSr93oMCnnDHjcyOz8O3stcNrCOGbxwDBIMwf05CzoEBhxF9SoT/fwvfWa9BRvlMTxl9lUUE2f3Aw9+0CBG7PYgZ9XGCiCM2Wy9T5cf0C5MOLr2MAIgQrlDua0oEtmC5FjFojQHJEITRmLyQnAyUU8Y+jkUUCtQNmweZKtTQT9le+G9QCzRoSsmVsVxCKxII3+8iLA+Lh9meI/vJSaQ2VpDgZVrV1Vc4V/z0Exu7Ap7R0MpzCy4hhRzdpWgf4OG815ZctwMvM7qfBqykG98B9oP3YC/LG+cpVK4Ipx5VnWBUIwVHDiocHhMjyroTBAeH3BfeCdOEpKwNK9vOxrbvUpqAc8ZOaaal08hWgFVFR8BQN+rEkiqKx5fONRXefkkyUWMdqwvoBn7ZzCr+F9GmD52Q7ZuDcyAigm2ArwDipT9QUH7gxphogcKRNAWAyaENWWKRBFWmeNYXJOuGXLr05Zye7joGFbJHxPYyUJ6KMombmYlI2GHc4JQGJNneDuL93v8AkYcfM2WfOjf0hm+SVw0TP95bD1xJwSCoKO5L6FxqMu5GPcOqik3sjGHg6XsEr10m7jt7G2PfNTgS3ft+TP32OjeSu9M80Fu3nmeqLM4a4lx9fFbGMpWDEU9wdftmPByeW7KoXyS2bkd1Mg9UP8ci0nKo2d/ThPn+iMEjXSibNeIGibaIT4ys3pjDLW2mx0IxQpwnuQfZzAHBszpMkwKuG4+FrqXff9+bZGuRQlVwhm2v5xbKRjgi99i14QkuNVUkePxllLZGFgQH0DjsLINZb+R9stBYjQmMGc2kkJfyhHx2/zvT2D2mpgcaq0ZRKco2CqDiLUJoHwLPse/fu4PWvqSKVpwiqhCehuloWTE3jkFUncwDI50hPvrfCx92cOJUE8RTsQgDoAh7JKsPHctSZfUicZyLHOuHMhtIoxs6l/lzFowbhZob/Uo7aRsipOcP/iZgEPnMEu/obXcLR3QrlQ+WuEppU4g2MZ9+xENLCqVDLEHiz+jZIvl42ePc4Ip4lJ5RQFcSSR1yYb+z/4KB/3bc308PHnEMEB5TYvVpK7M3kg2CTjos5lh733qQKwqYamgFjpr5oPUxRYwRvj41GYpcLN3Tr4WOi4lfevR5eVFBu1Jc9baY8JIOvcCL5WhXsEemnx5dUhcnP2ozloFCvU1IS60KHkXtF4NJl89tu5EECquTN5Lf9cvtIhlbmPsVg==" },
"gender": {
"0": "Keine Anrede",
"1": "Enby",

View File

@@ -1,10 +1,10 @@
import { coerceArray, coerceStringArray } from '@angular/cdk/coercion';
import { Injectable } from '@angular/core';
import { coerceArray } from '@angular/cdk/coercion';
import { inject, Injectable } from '@angular/core';
import { Config } from '@core/config';
import { isNullOrUndefined } from '@utils/common';
import { AuthConfig, OAuthService } from 'angular-oauth2-oidc';
import { JwksValidationHandler } from 'angular-oauth2-oidc-jwks';
import { asapScheduler, BehaviorSubject } from 'rxjs';
import { BehaviorSubject } from 'rxjs';
@Injectable({
providedIn: 'root',
@@ -44,21 +44,26 @@ export class AuthService {
this._oAuthService.tokenValidationHandler = new JwksValidationHandler();
this._oAuthService.setupAutomaticSilentRefresh();
try {
await this._oAuthService.loadDiscoveryDocumentAndTryLogin();
} catch (error) {
this.login();
throw error;
}
await this._oAuthService.loadDiscoveryDocumentAndTryLogin();
this._initialized.next(true);
}
isAuthenticated() {
return this.isIdTokenValid();
}
isIdTokenValid() {
console.log('ID Token Expiration:', new Date(this._oAuthService.getIdTokenExpiration()));
return this._oAuthService.hasValidIdToken();
}
isAccessTokenValid() {
console.log('ACCESS Token Expiration:', new Date(this._oAuthService.getAccessTokenExpiration()));
return this._oAuthService.hasValidAccessToken();
}
getToken() {
return this._oAuthService.getAccessToken();
}
@@ -99,9 +104,6 @@ export class AuthService {
async logout() {
await this._oAuthService.revokeTokenAndLogout();
// asapScheduler.schedule(() => {
// window.location.reload();
// }, 250);
}
hasRole(role: string | string[]) {

View File

@@ -1,3 +1,4 @@
export * from './auth.module';
export * from './auth.service';
export * from './if-role.directive';
export * from './login.strategy';

View File

@@ -0,0 +1,78 @@
import { ScanAdapterService } from '@adapter/scan';
import { inject, Injectable } from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import { EnvironmentService } from '@core/environment';
import { UiConfirmModalComponent, UiModalResult, UiModalService } from '@ui/modal';
import { injectNetworkStatus$ } from '../../app/services';
import { AuthService } from './auth.service';
import { AuthService as IsaAuthService } from '@swagger/isa';
import { firstValueFrom, lastValueFrom } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class LoginStrategy {
private readonly environmentService = inject(EnvironmentService);
private readonly networkStatus = toSignal(injectNetworkStatus$());
private readonly modal = inject(UiModalService);
private readonly scanAdapterService = inject(ScanAdapterService);
private readonly isaAuthService = inject(IsaAuthService);
private readonly authService = inject(AuthService);
async login(title = 'Anmeldung') {
console.log('LoginStrategy.login');
let loginModalResult: UiModalResult<boolean>;
if (this.authService.isIdTokenValid()) {
console.log('LoginStrategy.login: idToken is valid');
await this.authService.login();
return;
}
try {
if (this.environmentService.isMobileDevice()) {
console.log('LoginStrategy.login: isMobileDevice');
do {
const loginModal = this.modal.open({
content: UiConfirmModalComponent,
title,
config: {
canClose: false,
backdropClose: false,
},
data: {
message: 'Bitte wählen Sie die Anmeldeoption aus.',
rejectLabel: 'Anmeldung mit Logindaten',
confirmLabel: 'Anmeldung mit Keycard',
},
});
loginModalResult = await loginModal.afterClosed$.toPromise();
} while (this.networkStatus() === 'offline');
}
} catch (error) {}
try {
if (loginModalResult.data) {
const result = await lastValueFrom(this.scanAdapterService.scan());
if (typeof result === 'string') {
const res = await firstValueFrom(
this.isaAuthService.AuthLogin({
code: result,
application: 'isa',
hostname: location.host,
}),
);
if (res.token) {
console.log('LoginStrategy.login: setKeyCardToken', res.token);
this.authService.setKeyCardToken(res.token);
}
}
}
} catch (error) {}
console.log('LoginStrategy.login: login');
await this.authService.login();
}
}

View File

@@ -1 +0,0 @@
describe('Breadcrumb Actions', () => {});

View File

@@ -1,36 +1,36 @@
// unit test JsonConfigLoader
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { createServiceFactory, SpectatorService } from '@ngneat/spectator';
import { CORE_JSON_CONFIG_LOADER_URL } from '../tokens';
import { JsonConfigLoader } from './json.config-loader';
// // unit test JsonConfigLoader
// import { HttpTestingController } from '@angular/common/http/testing';
// import { createServiceFactory, SpectatorService } from '@ngneat/spectator';
// import { CORE_JSON_CONFIG_LOADER_URL } from '../tokens';
// import { JsonConfigLoader } from './json.config-loader';
describe('JsonConfigLoader', () => {
let spectator: SpectatorService<JsonConfigLoader>;
const createService = createServiceFactory({
imports: [HttpClientTestingModule],
service: JsonConfigLoader,
mocks: [],
providers: [{ provide: CORE_JSON_CONFIG_LOADER_URL, useValue: '/assets/config.json' }],
});
let httpTestingController: HttpTestingController;
// describe('JsonConfigLoader', () => {
// let spectator: SpectatorService<JsonConfigLoader>;
// const createService = createServiceFactory({
// imports: [HttpClientTestingModule],
// service: JsonConfigLoader,
// mocks: [],
// providers: [{ provide: CORE_JSON_CONFIG_LOADER_URL, useValue: '/assets/config.json' }],
// });
// let httpTestingController: HttpTestingController;
beforeEach(() => {
spectator = createService();
httpTestingController = spectator.inject(HttpTestingController);
});
// beforeEach(() => {
// spectator = createService();
// httpTestingController = spectator.inject(HttpTestingController);
// });
it('should create', () => {
expect(spectator.service).toBeTruthy();
});
// it('should create', () => {
// expect(spectator.service).toBeTruthy();
// });
describe('load', () => {
it('should call the provided url', async () => {
const reqPromise = spectator.service.load();
const req = httpTestingController.expectOne('/assets/config.json');
req.flush({ unit: 'test' });
const result = await reqPromise;
httpTestingController.verify();
expect(result).toEqual({ unit: 'test' });
});
});
});
// describe('load', () => {
// it('should call the provided url', async () => {
// const reqPromise = spectator.service.load();
// const req = httpTestingController.expectOne('/assets/config.json');
// req.flush({ unit: 'test' });
// const result = await reqPromise;
// httpTestingController.verify();
// expect(result).toEqual({ unit: 'test' });
// });
// });
// });

View File

@@ -62,6 +62,10 @@ export class EnvironmentService {
matchDesktopXXLarge$ = this._breakpointObserver.observe(MATCH_DESKTOP_XXLARGE).pipe(map((result) => result.matches));
isMobileDevice(): boolean {
return this._platform.ANDROID || this._platform.IOS;
}
/**
* @deprecated Use `matchDesktopSmall` or 'matchDesktop' instead.
*/

View File

@@ -1,16 +1,16 @@
import { TestBed } from '@angular/core/testing';
// import { TestBed } from '@angular/core/testing';
import { DomainAvailabilityService } from './availability.service';
// import { DomainAvailabilityService } from './availability.service';
describe('AvailabilityService', () => {
let service: DomainAvailabilityService;
// describe('AvailabilityService', () => {
// let service: DomainAvailabilityService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(DomainAvailabilityService);
});
// beforeEach(() => {
// TestBed.configureTestingModule({});
// service = TestBed.inject(DomainAvailabilityService);
// });
it('should be created', () => {
expect(service).toBeTruthy();
});
});
// it('should be created', () => {
// expect(service).toBeTruthy();
// });
// });

View File

@@ -36,7 +36,7 @@ export class DomainCatalogService {
search({ queryToken }: { queryToken: QueryTokenDTO }) {
return this.searchService
.SearchSearch({
queryToken,
...queryToken,
stockId: null,
})
.pipe(share());
@@ -55,54 +55,32 @@ export class DomainCatalogService {
getDetailsById({ id }: { id: number }) {
return this.searchService.SearchDetail({
id,
stockId: null,
});
}
getDetailsByEan({ ean }: { ean: string }) {
return this.searchService.SearchDetailByEAN({
ean,
stockId: null,
});
return this.searchService.SearchDetailByEAN(ean);
}
searchByIds({ ids }: { ids: number[] }) {
return this.searchService.SearchById({
ids,
stockId: null,
});
return this.searchService.SearchById(ids);
}
searchByEans({ eans }: { eans: string[] }) {
return this.searchService.SearchByEAN({
stockId: null,
branchNumber: null,
eans,
});
return this.searchService.SearchByEAN(eans);
}
searchTop({ queryToken }: { queryToken: QueryTokenDTO }) {
return this.searchService.SearchTop({
stockId: null,
queryToken,
});
return this.searchService.SearchTop(queryToken);
}
searchComplete({ queryToken }: { queryToken: AutocompleteTokenDTO }) {
return this.searchService.SearchAutocomplete({
stockId: null,
queryToken,
});
return this.searchService.SearchAutocomplete(queryToken);
}
@memorize()
getPromotionPoints({ items }: { items: { id: number; quantity: number; price?: number }[] }) {
return this.promotionService
.PromotionLesepunkte({
items,
stockId: null,
})
.pipe(shareReplay());
return this.promotionService.PromotionLesepunkte(items).pipe(shareReplay());
}
@memorize()

View File

@@ -1,5 +1,5 @@
import { HttpErrorResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { inject, Injectable } from '@angular/core';
import { ActionHandler, CommandService } from '@core/command';
import { ReorderModalComponent, ReorderResult } from '@modal/reorder';
import { AvailabilityDTO2, OrderItemListItemDTO } from '@swagger/oms';
@@ -11,11 +11,11 @@ import { OrderItemsContext } from './order-items.context';
@Injectable()
export class OrderAtSupplierActionHandler extends ActionHandler<OrderItemsContext> {
constructor(
private _command: CommandService,
private _domainOmsService: DomainOmsService,
private _uiModal: UiModalService,
) {
private _command = inject(CommandService);
private _domainOmsService = inject(DomainOmsService);
private _uiModal = inject(UiModalService);
constructor() {
super('ORDER_AT_SUPPLIER');
}

View File

@@ -1,20 +1,18 @@
import { Injectable } from '@angular/core';
import { inject, Injectable } from '@angular/core';
import { ActionHandler } from '@core/command';
import { DomainPrinterService, Printer } from '@domain/printer';
import { PrintModalComponent, PrintModalData } from '@modal/printer';
import { UiModalService } from '@ui/modal';
import { NativeContainerService } from '@external/native-container';
import { OrderItemsContext } from './order-items.context';
import { EnvironmentService } from '@core/environment';
@Injectable()
export class PrintCompartmentLabelActionHandler extends ActionHandler<OrderItemsContext> {
constructor(
private uiModal: UiModalService,
private domainPrinterService: DomainPrinterService,
private nativeContainerService: NativeContainerService,
private _environmentSerivce: EnvironmentService,
) {
private uiModal = inject(UiModalService);
private domainPrinterService = inject(DomainPrinterService);
private _environmentSerivce = inject(EnvironmentService);
constructor() {
super('PRINT_COMPARTMENTLABEL');
}
printCompartmentLabelHelper(printer: string, orderItemSubsetIds: number[]) {

View File

@@ -1,4 +1,4 @@
import { Injectable } from '@angular/core';
import { inject, Injectable } from '@angular/core';
import { ActionHandler } from '@core/command';
import { OrderItemsContext } from './order-items.context';
import { DomainPrinterService } from '@domain/printer';
@@ -8,10 +8,10 @@ import { PrintModalComponent } from '@modal/printer';
@Injectable()
export class PrintPriceDiffQrCodeLabelActionHandler extends ActionHandler<OrderItemsContext> {
constructor(
private uiModal: UiModalService,
private domainPrinterService: DomainPrinterService,
) {
private uiModal = inject(UiModalService);
private domainPrinterService = inject(DomainPrinterService);
constructor() {
super('PRINT_PRICEDIFFQRCODELABEL');
}

View File

@@ -1,22 +1,20 @@
import { Injectable } from '@angular/core';
import { inject, Injectable } from '@angular/core';
import { ActionHandler } from '@core/command';
import { OrderItemsContext } from './order-items.context';
import { DomainPrinterService, Printer } from '@domain/printer';
import { UiModalService } from '@ui/modal';
import { PrintModalComponent, PrintModalData } from '@modal/printer';
import { groupBy } from '@ui/common';
import { NativeContainerService } from '@external/native-container';
import { ReceiptDTO } from '@swagger/oms';
import { EnvironmentService } from '@core/environment';
@Injectable()
export class PrintShippingNoteActionHandler extends ActionHandler<OrderItemsContext> {
constructor(
private uiModal: UiModalService,
private domainPrinterService: DomainPrinterService,
private nativeContainerService: NativeContainerService,
private _environmentSerivce: EnvironmentService,
) {
private uiModal = inject(UiModalService);
private domainPrinterService = inject(DomainPrinterService);
private _environmentSerivce = inject(EnvironmentService);
constructor() {
super('PRINT_SHIPPINGNOTE');
}
@@ -51,7 +49,7 @@ export class PrintShippingNoteActionHandler extends ActionHandler<OrderItemsCont
content: PrintModalComponent,
config: { showScrollbarY: false },
data: {
printImmediately: !this.nativeContainerService.isNative,
printImmediately: !this._environmentSerivce.matchTablet(),
printerType: 'Label',
print: async (printer) => await this.printShippingNoteHelper(printer, receipts),
} as PrintModalData,

View File

@@ -1,56 +1,77 @@
import { Injectable } from '@angular/core';
import { inject, Injectable } from '@angular/core';
import { ActionHandler } from '@core/command';
import { OrderItemsContext } from './order-items.context';
import { OMSPrintService } from '@swagger/print';
import { UiModalService } from '@ui/modal';
import { PrintModalComponent, PrintModalData } from '@modal/printer';
import { NativeContainerService } from '@external/native-container';
import { groupBy } from '@ui/common';
import { Group, groupBy } from '@ui/common';
import { DomainPrinterService, Printer } from '@domain/printer';
import { firstValueFrom } from 'rxjs';
import { EnvironmentService } from '@core/environment';
import { ReceiptDTO, ResponseArgs } from '@swagger/oms';
@Injectable()
export class PrintSmallamountinvoiceActionHandler extends ActionHandler<OrderItemsContext> {
constructor(
private uiModal: UiModalService,
private omsPrintService: OMSPrintService,
private nativeContainerService: NativeContainerService,
) {
private uiModal = inject(UiModalService);
private omsPrintService = inject(OMSPrintService);
private _environmentSerivce = inject(EnvironmentService);
private _domainPrinterService = inject(DomainPrinterService);
constructor() {
super('PRINT_SMALLAMOUNTINVOICE');
}
async handler(data: OrderItemsContext): Promise<OrderItemsContext> {
await this.uiModal
.open({
content: PrintModalComponent,
config: { showScrollbarY: false },
data: {
printImmediately: !this.nativeContainerService.isNative,
printerType: 'Label',
print: async (printer) => {
try {
const receipts = data?.receipts?.filter((r) => r?.receiptType & 128);
for (const group of groupBy(receipts, (receipt) => receipt?.buyer?.buyerNumber)) {
await this.omsPrintService
.OMSPrintKleinbetragsrechnung({
data: group?.items?.map((r) => r?.id),
printer,
})
.toPromise();
}
return {
error: false,
};
} catch (error) {
console.error(error);
return {
error: true,
message: error?.message || error,
};
}
},
} as PrintModalData,
})
.afterClosed$.toPromise();
private _printKleinbetragsrechnungHelper(printer: string, receiptGroup: Group<string, ReceiptDTO>): Promise<ResponseArgs> {
return firstValueFrom(
this.omsPrintService.OMSPrintKleinbetragsrechnung({
data: receiptGroup?.items?.map((r) => r?.id),
printer,
}),
);
}
async handler(data: OrderItemsContext): Promise<OrderItemsContext> {
const printerList = await firstValueFrom(this._domainPrinterService.getAvailableLabelPrinters());
let printer: Printer;
if (Array.isArray(printerList)) {
printer = printerList.find((printer) => printer.selected === true);
}
if (!printer || this._environmentSerivce.matchTablet()) {
await firstValueFrom(
this.uiModal.open({
content: PrintModalComponent,
config: { showScrollbarY: false },
data: {
printImmediately: !this._environmentSerivce.matchTablet(),
printerType: 'Label',
print: async (printer) => {
try {
const receipts = data?.receipts?.filter((r) => r?.receiptType & 128);
for (const group of groupBy(receipts, (receipt) => receipt?.buyer?.buyerNumber)) {
await this._printKleinbetragsrechnungHelper(printer, group);
}
return {
error: false,
};
} catch (error) {
console.error(error);
return {
error: true,
message: error?.message || error,
};
}
},
} as PrintModalData,
}).afterClosed$,
);
} else {
const receipts = data?.receipts?.filter((r) => r?.receiptType & 128);
for (const group of groupBy(receipts, (receipt) => receipt?.buyer?.buyerNumber)) {
await this._printKleinbetragsrechnungHelper(printer.key, group);
}
}
return data;
}
}

View File

@@ -1,4 +1,4 @@
import { Injectable } from '@angular/core';
import { inject, Injectable } from '@angular/core';
import { ActionHandler, CommandService } from '@core/command';
import { OrderItemsContext } from './order-items.context';
import { UiModalService } from '@ui/modal';
@@ -9,12 +9,12 @@ import { ToasterService } from '@shared/shell';
@Injectable()
export class ReOrderActionHandler extends ActionHandler<OrderItemsContext> {
constructor(
private _command: CommandService,
private _domainCheckoutService: DomainCheckoutService,
private _uiModal: UiModalService,
private _toastService: ToasterService,
) {
private _command = inject(CommandService);
private _domainCheckoutService = inject(DomainCheckoutService);
private _uiModal = inject(UiModalService);
private _toastService = inject(ToasterService);
constructor() {
super('REORDER');
}

View File

@@ -236,7 +236,9 @@ export class DomainOmsService {
}
if (!!organisation && !!buyer.organisation) {
buyer.organisation.name = organisation;
buyer.organisation = {
name: organisation,
};
}
return this.orderService

View File

@@ -274,12 +274,9 @@ export class DomainRemissionService {
switchMap((stock) =>
this._search
.SearchSearch({
stockId: null,
queryToken: {
stockId: stock.id,
input: { qs: ean },
doNotTrack: true,
},
stockId: stock.id,
input: { qs: ean },
doNotTrack: true,
})
.pipe(
catchError((err) => of({ hits: 0, result: [] })),

View File

@@ -4,6 +4,10 @@ import { map } from 'rxjs/operators';
import { WindowRef } from './window-ref.service';
import { ScanRequestType } from './scan-request.type';
import { Platform } from '@angular/cdk/platform';
/**
* @deprecated This service is deprecated and will be removed in future versions.
*/
@Injectable({
providedIn: 'root',
})

View File

@@ -1,4 +1,4 @@
import { Injectable } from '@angular/core';
import { inject, Injectable } from '@angular/core';
import { CrmCustomerService } from '@domain/crm';
import { AddressDTO } from '@swagger/crm';
import { UiModalService } from '@ui/modal';
@@ -7,10 +7,9 @@ import { AddressSelectionModalComponent } from './address-selection-modal.compon
@Injectable({ providedIn: 'root' })
export class AddressSelectionModalService {
constructor(
private customerService: CrmCustomerService,
private modal: UiModalService,
) {}
private modal = inject(UiModalService);
constructor(private customerService: CrmCustomerService) {}
async validateAddress(address: AddressDTO): Promise<AddressDTO> {
if (address.street && address.streetNumber && address.zipCode && address.city && address.country) {

View File

@@ -1,12 +1,12 @@
import { UiModalRef, UiModalService } from '@ui/modal';
import { KulturpassOrderModalData } from './kulturpass-order-modal.data';
import { KulturpassOrderModalComponent } from './kulturpass-order-modal.component';
import { Injectable } from '@angular/core';
import { inject, Injectable } from '@angular/core';
import { DisplayOrderDTO } from '@swagger/oms';
@Injectable({ providedIn: 'root' })
export class KulturpassOrderModalService {
constructor(private modal: UiModalService) {}
private modal = inject(UiModalService);
open(data: KulturpassOrderModalData): UiModalRef<[DisplayOrderDTO, string], KulturpassOrderModalData> {
return this.modal.open<[DisplayOrderDTO, string], KulturpassOrderModalData>({

View File

@@ -1,5 +1,7 @@
import { Injectable } from '@angular/core';
import { ComponentStore, OnStoreInit, tapResponse } from '@ngrx/component-store';
import { inject, Injectable } from '@angular/core';
import { ComponentStore, OnStoreInit } from '@ngrx/component-store';
import { tapResponse } from '@ngrx/operators';
import {
AddToShoppingCartDTO,
AvailabilityDTO,
@@ -39,6 +41,12 @@ export interface KulturpassOrderModalState {
@Injectable()
export class KulturpassOrderModalStore extends ComponentStore<KulturpassOrderModalState> implements OnStoreInit {
private _checkoutService = inject(DomainCheckoutService);
private _branchService = inject(BranchService);
private _authService = inject(AuthService);
private _availabilityService = inject(DomainAvailabilityService);
private _modal = inject(UiModalService);
readonly processId = Date.now();
get orderItemListItem() {
@@ -61,13 +69,7 @@ export class KulturpassOrderModalStore extends ComponentStore<KulturpassOrderMod
return this.get((s) => s.fetchShoppingCart);
}
constructor(
private _checkoutService: DomainCheckoutService,
private _branchService: BranchService,
private _authService: AuthService,
private _availabilityService: DomainAvailabilityService,
private _modal: UiModalService,
) {
constructor() {
super({
availabilities: {},
});

View File

@@ -1,15 +1,16 @@
import { Injectable } from '@angular/core';
import { ComponentStore, OnStoreInit, tapResponse } from '@ngrx/component-store';
import { inject, Injectable } from '@angular/core';
import { ComponentStore, OnStoreInit } from '@ngrx/component-store';
import { tapResponse } from '@ngrx/operators';
import { ItemDTO, ResponseArgsOfItemDTO } from '@swagger/cat';
import { switchMap, withLatestFrom } from 'rxjs/operators';
import { Observable, Subject, asapScheduler } from 'rxjs';
import { Subject, asapScheduler } from 'rxjs';
import { DomainAvailabilityService } from '@domain/availability';
import { DomainCatalogService } from '@domain/catalog';
import { AddToShoppingCartDTO, AvailabilityDTO, BranchDTO } from '@swagger/checkout';
import { AddToShoppingCartDTO, BranchDTO } from '@swagger/checkout';
import { tap } from 'rxjs/operators';
import { UiModalService } from '@ui/modal';
import { KulturpassOrderModalStore } from '../kulturpass-order-modal.store';
import { getCatalogProductNumber } from '../catalog-product-number';
export interface KulturpassOrderSearchboxState {
query: string;
@@ -19,6 +20,11 @@ export interface KulturpassOrderSearchboxState {
@Injectable()
export class KulturpassOrderSearchboxStore extends ComponentStore<KulturpassOrderSearchboxState> implements OnStoreInit {
private _parentStore = inject(KulturpassOrderModalStore);
private _catalogService = inject(DomainCatalogService);
private _availabilityService = inject(DomainAvailabilityService);
private _modal = inject(UiModalService);
get query() {
return this.get((s) => s.query);
}
@@ -35,12 +41,7 @@ export class KulturpassOrderSearchboxStore extends ComponentStore<KulturpassOrde
hint$ = new Subject<string>();
constructor(
private _parentStore: KulturpassOrderModalStore,
private _catalogService: DomainCatalogService,
private _availabilityService: DomainAvailabilityService,
private _modal: UiModalService,
) {
constructor() {
super({ query: '', fetching: false });
}

View File

@@ -1,4 +1,4 @@
export * from './message-modal.action';
export * from './message-modal.component';
export * from './message-modal.data';
export * from './message-modal.service';
export * from './message-modal.injector';

View File

@@ -0,0 +1,11 @@
import { inject } from '@angular/core';
import { UiModalRef, UiModalService } from '@ui/modal';
import { MessageModalData } from './message-modal.data';
import { MessageModalComponent } from './message-modal.component';
export const injectOpenMessageModal = () => {
const modalService = inject(UiModalService);
return (data: MessageModalData): UiModalRef<void, MessageModalComponent> =>
modalService.open({ title: data.title, content: MessageModalComponent, data });
};

View File

@@ -1,17 +0,0 @@
import { Injectable } from '@angular/core';
import { UiModalRef, UiModalService } from '@ui/modal';
import { MessageModalData } from './message-modal.data';
import { MessageModalComponent } from './message-modal.component';
@Injectable({ providedIn: 'root' })
export class MessageModalService {
constructor(private _modal: UiModalService) {}
open(data: MessageModalData): UiModalRef<void, MessageModalComponent> {
return this._modal.open({
title: data.title,
content: MessageModalComponent,
data,
});
}
}

View File

@@ -1,3 +1,2 @@
export * from './modal-printer.component';
export * from './modal-printer.data';
export * from './modal-printer.module';

View File

@@ -6,12 +6,19 @@ import { isResponseArgs } from '@utils/object';
import { Observable, Subscription } from 'rxjs';
import { map, shareReplay } from 'rxjs/operators';
import { PrintModalData } from './modal-printer.data';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { UiIconModule } from '@ui/icon';
import { UiSelectModule } from '@ui/select';
import { UiSpinnerModule } from '@ui/spinner';
@Component({
selector: 'modal-print-cart',
templateUrl: 'modal-printer.component.html',
styleUrls: ['modal-printer.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [CommonModule, FormsModule, UiIconModule, UiSelectModule, UiSpinnerModule],
})
export class PrintModalComponent implements OnInit, OnDestroy {
selectedPrinterKey: string;

View File

@@ -1,15 +0,0 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { UiIconModule } from '@ui/icon';
import { UiModalModule } from '@ui/modal';
import { UiSelectModule } from '@ui/select';
import { UiSpinnerModule } from '@ui/spinner';
import { PrintModalComponent } from './modal-printer.component';
@NgModule({
declarations: [PrintModalComponent],
imports: [CommonModule, FormsModule, UiModalModule, UiIconModule, UiSelectModule, UiSpinnerModule],
exports: [PrintModalComponent],
})
export class ModalPrinterModule {}

View File

@@ -1,5 +1,4 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { ApplicationService } from '@core/application';
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
import { DomainAvailabilityService } from '@domain/availability';
import { DomainOmsService } from '@domain/oms';
import { ComponentStore } from '@ngrx/component-store';
@@ -25,6 +24,9 @@ interface GoodsInListReorderModalState {
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ReorderModalComponent extends ComponentStore<GoodsInListReorderModalState> {
private domainAvailabilityService = inject(DomainAvailabilityService);
private _omsService = inject(DomainOmsService);
readonly orderItem$ = this.select((s) => s.orderItem);
get orderItem() {
return this.get((s) => s.orderItem);
@@ -138,12 +140,7 @@ export class ReorderModalComponent extends ComponentStore<GoodsInListReorderModa
takeAwayAvailabilityError: boolean;
selectedReason: string;
constructor(
public modalRef: UiModalRef<ReorderResult, { item: OrderItemListItemDTO; showReasons: boolean }>,
private domainAvailabilityService: DomainAvailabilityService,
private _omsService: DomainOmsService,
private _applicationService: ApplicationService,
) {
constructor(public modalRef: UiModalRef<ReorderResult, { item: OrderItemListItemDTO; showReasons: boolean }>) {
super({
orderItem: modalRef.data?.item,
checkedAvailability: undefined,

View File

@@ -1,4 +1,4 @@
import { Injectable } from '@angular/core';
import { inject, Injectable } from '@angular/core';
import { DomainPackageInspectionService } from '@domain/package-inspection';
import { PackageDTO2 } from '@swagger/wws';
import { UiModalRef, UiModalService } from '@ui/modal';
@@ -8,10 +8,8 @@ import { WrongDestinationModalData } from './wrong-destination-modal.data';
@Injectable({ providedIn: 'root' })
export class WrongDestinationModalService {
constructor(
private _packageInspectionService: DomainPackageInspectionService,
private _modal: UiModalService,
) {}
private _packageInspectionService = inject(DomainPackageInspectionService);
private _modal = inject(UiModalService);
fetchPackages(): Observable<PackageDTO2[]> {
return this._packageInspectionService.queryPackages({

View File

@@ -30,7 +30,7 @@
</div>
<div class="items scroll-bar">
@for (item of items; track item.id; let first = $first) {
@for (item of items; track item.uId; let first = $first) {
@defer (on viewport) {
<page-price-update-item [item]="item" [selected]="isSelected(item)" [class.mt-px-10]="!first"></page-price-update-item>
} @placeholder {

View File

@@ -1,5 +1,5 @@
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
import { ChangeDetectionStrategy, Component, Input, ViewChild } from '@angular/core';
import { ChangeDetectionStrategy, Component, Input, OnChanges, ViewChild } from '@angular/core';
import { ProductListItemDTO } from '@swagger/wws';
import { map } from 'rxjs/operators';
import { PriceUpdateComponentStore } from '../price-update.component.store';

View File

@@ -4,7 +4,9 @@ import { ApplicationService } from '@core/application';
import { BreadcrumbService } from '@core/breadcrumb';
import { DomainAvailabilityService, ItemData } from '@domain/availability';
import { DomainCatalogService } from '@domain/catalog';
import { ComponentStore, tapResponse } from '@ngrx/component-store';
import { ComponentStore } from '@ngrx/component-store';
import { tapResponse } from '@ngrx/operators';
import { ItemDTO, ResponseArgsOfItemDTO } from '@swagger/cat';
import { AvailabilityDTO, BranchDTO } from '@swagger/checkout';
import { UiErrorModalComponent, UiModalService } from '@ui/modal';

View File

@@ -5,7 +5,9 @@ import { DomainAvailabilityService } from '@domain/availability';
import { DomainCatalogService } from '@domain/catalog';
import { DomainCheckoutService } from '@domain/checkout';
import { DomainOmsService } from '@domain/oms';
import { ComponentStore, tapResponse } from '@ngrx/component-store';
import { ComponentStore } from '@ngrx/component-store';
import { tapResponse } from '@ngrx/operators';
import { ItemDTO } from '@swagger/cat';
import {
AddToShoppingCartDTO,

View File

@@ -7,7 +7,6 @@ import { ProductImageModule } from '@cdn/product-image';
import { RouterModule } from '@angular/router';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { UiSpinnerModule } from '@ui/spinner';
import { ModalPrinterModule } from '@modal/printer';
import { UiFormControlModule } from '@ui/form-control';
import { UiInputModule } from '@ui/input';
import { UiCheckboxModule } from '@ui/checkbox';
@@ -34,7 +33,6 @@ import { LoaderComponent, SkeletonLoaderComponent } from '@shared/components/loa
FormsModule,
ReactiveFormsModule,
UiSpinnerModule,
ModalPrinterModule,
UiFormControlModule,
UiInputModule,
UiCheckboxModule,

View File

@@ -2,7 +2,9 @@ import { Injectable } from '@angular/core';
import { UntypedFormGroup } from '@angular/forms';
import { ApplicationService } from '@core/application';
import { DomainCheckoutService } from '@domain/checkout';
import { ComponentStore, tapResponse } from '@ngrx/component-store';
import { ComponentStore } from '@ngrx/component-store';
import { tapResponse } from '@ngrx/operators';
import { NotificationChannel, PayerDTO, ShoppingCartDTO, ShoppingCartItemDTO } from '@swagger/checkout';
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
import { BehaviorSubject, Subject } from 'rxjs';

View File

@@ -4,7 +4,6 @@ import { CommonModule } from '@angular/common';
import { CheckoutSummaryComponent } from './checkout-summary.component';
import { PageCheckoutPipeModule } from '../pipes/page-checkout-pipe.module';
import { ProductImageModule } from '@cdn/product-image';
import { ModalPrinterModule } from '@modal/printer';
import { RouterModule } from '@angular/router';
import { UiCommonModule } from '@ui/common';
import { UiSpinnerModule } from '@ui/spinner';
@@ -18,7 +17,6 @@ import { AuthModule } from '@core/auth';
RouterModule,
PageCheckoutPipeModule,
ProductImageModule,
ModalPrinterModule,
IconModule,
UiCommonModule,
UiSpinnerModule,

View File

@@ -12,7 +12,9 @@ import {
} from '@angular/core';
import { UntypedFormControl } from '@angular/forms';
import { DomainOmsService, DomainReceiptService } from '@domain/oms';
import { ComponentStore, tapResponse } from '@ngrx/component-store';
import { ComponentStore } from '@ngrx/component-store';
import { tapResponse } from '@ngrx/operators';
import { OrderDTO, OrderItemListItemDTO, ReceiptDTO, ReceiptType } from '@swagger/oms';
import { isEqual } from 'lodash';
import { combineLatest, NEVER, Subject, Observable } from 'rxjs';

View File

@@ -1,6 +1,8 @@
import { Injectable, OnDestroy } from '@angular/core';
import { DomainCustomerOrderService, DomainOmsService, OrderItemsContext } from '@domain/oms';
import { ComponentStore, tapResponse } from '@ngrx/component-store';
import { ComponentStore } from '@ngrx/component-store';
import { tapResponse } from '@ngrx/operators';
import { OrderDTO, OrderItemListItemDTO, OrderItemProcessingStatusValue, ReceiptDTO } from '@swagger/oms';
import { log } from '@utils/common';
import { isEqual } from 'lodash';

View File

@@ -1,6 +1,6 @@
import { BooleanInput, NumberInput, coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion';
import { CdkMenuModule } from '@angular/cdk/menu';
import { Component, ChangeDetectionStrategy, Input } from '@angular/core';
import { Component, ChangeDetectionStrategy, Input, inject } from '@angular/core';
import { IconComponent } from '@shared/components/icon';
import { SharedMenuModule } from '@shared/components/menu';
import { combineLatest } from 'rxjs';
@@ -30,6 +30,8 @@ export interface CustomerMenuComponentState {
imports: [CdkMenuModule, SharedMenuModule, IconComponent, RouterLink, NgIf, AsyncPipe],
})
export class CustomerMenuComponent extends ComponentStore<CustomerMenuComponentState> {
private _navigation = inject(CustomerSearchNavigation);
@Input() set customerId(value: NumberInput) {
this.patchState({ customerId: coerceNumberProperty(value) });
}
@@ -100,7 +102,7 @@ export class CustomerMenuComponent extends ComponentStore<CustomerMenuComponentS
),
);
constructor(private _navigation: CustomerSearchNavigation) {
constructor() {
super({
showCustomerCard: true,
showCustomerDetails: true,

View File

@@ -1,4 +1,4 @@
import { Component, ChangeDetectionStrategy, Input, EventEmitter, Output, ViewChild } from '@angular/core';
import { Component, ChangeDetectionStrategy, Input, EventEmitter, Output, ViewChild, inject } from '@angular/core';
import { CustomerInfoDTO } from '@swagger/crm';
import { BooleanInput, NumberInput, coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion';
import { CustomerCreateNavigation, CustomerSearchNavigation } from '@shared/services/navigation';
@@ -12,6 +12,9 @@ import { asapScheduler } from 'rxjs';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CustomerResultListComponent {
public customerSearchNavigation = inject(CustomerSearchNavigation);
public customerCreateNavigation = inject(CustomerCreateNavigation);
private _compact: boolean;
@Input()
get compact() {
@@ -64,11 +67,6 @@ export class CustomerResultListComponent {
@Output()
scrollIndexChange = new EventEmitter<number>();
constructor(
public customerSearchNavigation: CustomerSearchNavigation,
public customerCreateNavigation: CustomerCreateNavigation,
) {}
scrolledIndexChange(index: number) {
if (index && this.customers.length <= this.viewport?.getRenderedRange()?.end) {
this.endReached.emit();

View File

@@ -14,7 +14,9 @@ import {
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { CacheService } from '@core/cache';
import { DomainCheckoutService } from '@domain/checkout';
import { ComponentStore, tapResponse } from '@ngrx/component-store';
import { ComponentStore } from '@ngrx/component-store';
import { tapResponse } from '@ngrx/operators';
import { OptionDTO } from '@swagger/checkout';
import { UiCheckboxComponent } from '@ui/checkbox';
import { first, isBoolean, isString } from 'lodash';

View File

@@ -35,6 +35,16 @@ import { CustomerCreateNavigation, CustomerSearchNavigation } from '@shared/serv
@Directive()
export abstract class AbstractCreateCustomer implements OnInit, OnDestroy {
protected modal = inject(UiModalService);
protected activatedRoute = inject(ActivatedRoute);
protected router = inject(Router);
protected customerService = inject(CrmCustomerService);
protected addressVlidationModal = inject(AddressSelectionModalService);
protected breadcrumb = inject(BreadcrumbService);
protected cdr = inject(ChangeDetectorRef);
protected customerSearchNavigation = inject(CustomerSearchNavigation);
protected onDestroy$ = new Subject<void>();
abstract validateAddress?: boolean;
@@ -68,16 +78,7 @@ export abstract class AbstractCreateCustomer implements OnInit, OnDestroy {
readonly customerCreateNavigation = inject(CustomerCreateNavigation);
constructor(
protected activatedRoute: ActivatedRoute,
protected router: Router,
protected customerService: CrmCustomerService,
protected addressVlidationModal: AddressSelectionModalService,
protected modal: UiModalService,
protected breadcrumb: BreadcrumbService,
protected cdr: ChangeDetectorRef,
protected customerSearchNavigation: CustomerSearchNavigation,
) {
constructor() {
this._initProcessId$();
}

View File

@@ -1,4 +1,4 @@
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { Component, ChangeDetectionStrategy, inject } from '@angular/core';
import { CheckboxComponent } from '@shared/components/checkbox';
import { FormControl, FormGroup, Validators, ReactiveFormsModule } from '@angular/forms';
import { SelectModule } from '@shared/components/select';
@@ -36,6 +36,12 @@ import { GenderSettingsService } from '@shared/services/gender';
],
})
export class AddBillingAddressMainViewComponent {
private _customerService = inject(CrmCustomerService);
private _addressSelection = inject(AddressSelectionModalService);
private _store = inject(CustomerSearchStore);
private _navigation = inject(CustomerSearchNavigation);
public genderSettings = inject(GenderSettingsService);
formGroup = new FormGroup({
gender: new FormControl<Gender>(undefined, [Validators.required]),
title: new FormControl<string>(undefined),
@@ -57,14 +63,6 @@ export class AddBillingAddressMainViewComponent {
map(([processId, customerId]) => this._navigation.detailsRoute({ processId, customerId })),
);
constructor(
private _customerService: CrmCustomerService,
private _addressSelection: AddressSelectionModalService,
private _store: CustomerSearchStore,
private _navigation: CustomerSearchNavigation,
public genderSettings: GenderSettingsService,
) {}
async save() {
if (this.formGroup.invalid) {
this.formGroup.markAllAsTouched();

View File

@@ -1,4 +1,4 @@
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy } from '@angular/core';
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, inject } from '@angular/core';
import { CheckboxComponent } from '@shared/components/checkbox';
import { FormControl, FormGroup, Validators, ReactiveFormsModule } from '@angular/forms';
import { SelectModule } from '@shared/components/select';
@@ -36,6 +36,12 @@ import { GenderSettingsService } from '@shared/services/gender';
],
})
export class AddShippingAddressMainViewComponent implements OnInit, OnDestroy {
private _customerService = inject(CrmCustomerService);
private _addressSelection = inject(AddressSelectionModalService);
private _store = inject(CustomerSearchStore);
private _navigation = inject(CustomerSearchNavigation);
public genderSettings = inject(GenderSettingsService);
private _onDestroy = new Subject<void>();
detailsRoute$ = combineLatest([this._store.processId$, this._store.customerId$]).pipe(
@@ -63,14 +69,6 @@ export class AddShippingAddressMainViewComponent implements OnInit, OnDestroy {
isBusinessKonto$ = this._store.isBusinessKonto$;
constructor(
private _customerService: CrmCustomerService,
private _addressSelection: AddressSelectionModalService,
private _store: CustomerSearchStore,
private _navigation: CustomerSearchNavigation,
public genderSettings: GenderSettingsService,
) {}
ngOnInit() {
this._store.customer$.pipe(takeUntil(this._onDestroy)).subscribe(() => {
if (this._store.isBusinessKonto) {

View File

@@ -27,6 +27,13 @@ import { provideCancelSearchSubject } from '@shared/services/cancel-subject';
],
})
export class CustomerSearchComponent implements OnInit, OnDestroy {
private _store = inject(CustomerSearchStore);
private _activatedRoute = inject(ActivatedRoute);
private _router = inject(Router);
private _breadcrumbService = inject(BreadcrumbService);
private _navigation = inject(CustomerSearchNavigation);
private _createNavigation = inject(CustomerCreateNavigation);
private searchStore = inject(CustomerSearchStore);
keyEscPressed = toSignal(fromEvent(document, 'keydown').pipe(filter((e: KeyboardEvent) => e.key === 'Escape')));
@@ -74,14 +81,7 @@ export class CustomerSearchComponent implements OnInit, OnDestroy {
private _onDestroy$ = new Subject<void>();
constructor(
private _store: CustomerSearchStore,
private _activatedRoute: ActivatedRoute,
private _router: Router,
private _breadcrumbService: BreadcrumbService,
private _navigation: CustomerSearchNavigation,
private _createNavigation: CustomerCreateNavigation,
) {}
constructor() {}
ngOnInit(): void {
this.checkAndUpdateProcessId();

View File

@@ -1,4 +1,4 @@
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, Host } from '@angular/core';
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, Host, inject } from '@angular/core';
import { CustomerSearchStore } from '../../store';
import { CrmCustomerService } from '@domain/crm';
import { debounceTime, map, switchMap, takeUntil } from 'rxjs/operators';
@@ -6,7 +6,9 @@ import { Observable, Subject, combineLatest } from 'rxjs';
import { AssignedPayerDTO, CustomerDTO, ListResponseArgsOfAssignedPayerDTO } from '@swagger/crm';
import { AsyncPipe, NgFor, NgIf } from '@angular/common';
import { CustomerPipesModule } from '@shared/pipes/customer';
import { ComponentStore, tapResponse } from '@ngrx/component-store';
import { ComponentStore } from '@ngrx/component-store';
import { tapResponse } from '@ngrx/operators';
import { UiModalService } from '@ui/modal';
import { CustomerSearchNavigation } from '@shared/services/navigation';
import { RouterLink } from '@angular/router';
@@ -31,6 +33,12 @@ export class DetailsMainViewBillingAddressesComponent
extends ComponentStore<DetailsMainViewBillingAddressesComponentState>
implements OnInit, OnDestroy
{
private _host = inject(CustomerDetailsViewMainComponent, { host: true });
private _store = inject(CustomerSearchStore);
private _customerService = inject(CrmCustomerService);
private _modal = inject(UiModalService);
private _navigation = inject(CustomerSearchNavigation);
assignedPayers$ = this.select((state) => state.assignedPayers);
selectedPayer$ = this.select((state) => state.selectedPayer);
@@ -69,13 +77,7 @@ export class DetailsMainViewBillingAddressesComponent
),
);
constructor(
@Host() private _host: CustomerDetailsViewMainComponent,
private _store: CustomerSearchStore,
private _customerService: CrmCustomerService,
private _modal: UiModalService,
private _navigation: CustomerSearchNavigation,
) {
constructor() {
super({
assignedPayers: [],
selectedPayer: undefined,

View File

@@ -1,4 +1,4 @@
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, Host } from '@angular/core';
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, Host, inject } from '@angular/core';
import { CustomerSearchStore } from '../../store';
import { CrmCustomerService } from '@domain/crm';
import { map, switchMap, takeUntil } from 'rxjs/operators';
@@ -6,7 +6,9 @@ import { Observable, Subject, combineLatest } from 'rxjs';
import { CustomerDTO, ListResponseArgsOfAssignedPayerDTO, ShippingAddressDTO } from '@swagger/crm';
import { AsyncPipe, NgFor, NgIf } from '@angular/common';
import { CustomerPipesModule } from '@shared/pipes/customer';
import { ComponentStore, tapResponse } from '@ngrx/component-store';
import { ComponentStore } from '@ngrx/component-store';
import { tapResponse } from '@ngrx/operators';
import { UiModalService } from '@ui/modal';
import { CustomerSearchNavigation } from '@shared/services/navigation';
import { RouterLink } from '@angular/router';
@@ -30,6 +32,12 @@ export class DetailsMainViewDeliveryAddressesComponent
extends ComponentStore<DetailsMainViewDeliveryAddressesComponentState>
implements OnInit, OnDestroy
{
private _host = inject(CustomerDetailsViewMainComponent, { host: true });
private _store = inject(CustomerSearchStore);
private _customerService = inject(CrmCustomerService);
private _modal = inject(UiModalService);
private _navigation = inject(CustomerSearchNavigation);
shippingAddresses$ = this.select((state) => state.shippingAddresses);
selectedShippingAddress$ = this.select((state) => state.selectedShippingAddress);
@@ -87,13 +95,7 @@ export class DetailsMainViewDeliveryAddressesComponent
map(([isKundenkarte, isBusinessKonto, isMitarbeiter]) => isKundenkarte || isBusinessKonto || isMitarbeiter),
);
constructor(
@Host() private _host: CustomerDetailsViewMainComponent,
private _store: CustomerSearchStore,
private _customerService: CrmCustomerService,
private _modal: UiModalService,
private _navigation: CustomerSearchNavigation,
) {
constructor() {
super({
shippingAddresses: [],
selectedShippingAddress: undefined,

View File

@@ -30,6 +30,16 @@ export interface CustomerDetailsViewMainState {
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CustomerDetailsViewMainComponent extends ComponentStore<CustomerDetailsViewMainState> implements OnInit, OnDestroy {
private _store = inject(CustomerSearchStore);
private _navigation = inject(CustomerSearchNavigation);
private _checkoutService = inject(DomainCheckoutService);
private _modalService = inject(UiModalService);
private _application = inject(ApplicationService);
private _catalogNavigation = inject(ProductCatalogNavigationService);
private _checkoutNavigation = inject(CheckoutNavigationService);
private _router = inject(Router);
private _activatedRoute = inject(ActivatedRoute);
private _genderSettings = inject(GenderSettingsService);
private _onDestroy$ = new Subject<void>();
customerService = inject(CrmCustomerService);
@@ -189,18 +199,7 @@ export class CustomerDetailsViewMainComponent extends ComponentStore<CustomerDet
map(([type, hasCard]) => type === 'webshop' || hasCard),
);
constructor(
private _store: CustomerSearchStore,
private _navigation: CustomerSearchNavigation,
private _checkoutService: DomainCheckoutService,
private _modalService: UiModalService,
private _application: ApplicationService,
private _catalogNavigation: ProductCatalogNavigationService,
private _checkoutNavigation: CheckoutNavigationService,
private _router: Router,
private _activatedRoute: ActivatedRoute,
private _genderSettings: GenderSettingsService,
) {
constructor() {
super({ isBusy: false, shoppingCart: undefined, shippingAddress: undefined, payer: undefined });
}

View File

@@ -32,6 +32,14 @@ export interface EditBillingAddressMainViewState {
imports: [AsyncPipe, RouterLink, IconComponent, NgForOf, ReactiveFormsModule, SelectModule, FormControlComponent, CheckboxComponent],
})
export class EditBillingAddressMainViewComponent extends ComponentStore<EditBillingAddressMainViewState> implements OnInit, OnDestroy {
private _customerService = inject(CrmCustomerService);
private _addressSelection = inject(AddressSelectionModalService);
private _store = inject(CustomerSearchStore);
private _navigation = inject(CustomerSearchNavigation);
private _activatedRoute = inject(ActivatedRoute);
private _modal = inject(UiModalService);
public genderSettings = inject(GenderSettingsService);
private _onDestroy$ = new Subject<void>();
detailsRoute$ = combineLatest([this._store.processId$, this._store.customerId$]).pipe(
@@ -70,15 +78,7 @@ export class EditBillingAddressMainViewComponent extends ComponentStore<EditBill
return this.get((s) => s.payer);
}
constructor(
private _customerService: CrmCustomerService,
private _addressSelection: AddressSelectionModalService,
private _store: CustomerSearchStore,
private _navigation: CustomerSearchNavigation,
private _activatedRoute: ActivatedRoute,
private _modal: UiModalService,
public genderSettings: GenderSettingsService,
) {
constructor() {
super({ payer: undefined });
}

View File

@@ -1,6 +1,6 @@
import { Location } from '@angular/common';
import { HttpErrorResponse } from '@angular/common/http';
import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
import { ChangeDetectorRef, Component, inject, OnInit } from '@angular/core';
import { AbstractControl, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
import { ActivatedRoute } from '@angular/router';
import { CrmCustomerService } from '@domain/crm';
@@ -18,6 +18,15 @@ import { GenderSettingsService } from '@shared/services/gender';
@Component({ template: '' })
export abstract class CustomerDataEditComponent implements OnInit {
private customerService = inject(CrmCustomerService);
private activatedRoute = inject(ActivatedRoute);
private fb = inject(UntypedFormBuilder);
private cdr = inject(ChangeDetectorRef);
private location = inject(Location);
private _store = inject(CustomerSearchStore);
private _navigation = inject(CustomerSearchNavigation);
public genderSettings = inject(GenderSettingsService);
customer$: Observable<CustomerDTO>;
customerId$: Observable<number>;
countries$: Observable<CountryDTO[]>;
@@ -37,17 +46,6 @@ export abstract class CustomerDataEditComponent implements OnInit {
afterInitForm?: (control: AbstractControl) => void;
constructor(
private customerService: CrmCustomerService,
private activatedRoute: ActivatedRoute,
private fb: UntypedFormBuilder,
private cdr: ChangeDetectorRef,
private location: Location,
private _store: CustomerSearchStore,
private _navigation: CustomerSearchNavigation,
public genderSettings: GenderSettingsService,
) {}
ngOnInit() {
this.customerId$ = this.activatedRoute.params.pipe(map((p) => Number(p['customerId'])));
this.customer$ = this.customerId$.pipe(

View File

@@ -42,6 +42,11 @@ export interface EditShippingAddressMainViewState {
})
export class EditShippingAddressMainViewComponent extends ComponentStore<EditShippingAddressMainViewState> implements OnInit, OnDestroy {
private _activatedRoute = inject(ActivatedRoute);
private _customerService = inject(CrmCustomerService);
private _addressSelection = inject(AddressSelectionModalService);
private _store = inject(CustomerSearchStore);
private _navigation = inject(CustomerSearchNavigation);
public genderSettings = inject(GenderSettingsService);
private _onDestroy = new Subject<void>();
@@ -81,13 +86,7 @@ export class EditShippingAddressMainViewComponent extends ComponentStore<EditShi
return this.get((s) => s.shippingAddress?.id);
}
constructor(
private _customerService: CrmCustomerService,
private _addressSelection: AddressSelectionModalService,
private _store: CustomerSearchStore,
private _navigation: CustomerSearchNavigation,
public genderSettings: GenderSettingsService,
) {
constructor() {
super({ shippingAddress: undefined });
}

View File

@@ -1,4 +1,4 @@
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { Component, ChangeDetectionStrategy, inject } from '@angular/core';
import { Location } from '@angular/common';
import { CustomerSearchStore } from '../store';
import { Filter } from '@shared/components/filter';
@@ -14,6 +14,11 @@ import { injectCancelSearch } from '@shared/services/cancel-subject';
host: { class: 'page-customer-filter-main-view' },
})
export class CustomerFilterMainViewComponent {
private _store = inject(CustomerSearchStore);
private _location = inject(Location);
private _router = inject(Router);
private _customerSearchNavigation = inject(CustomerSearchNavigation);
cancelSearch = injectCancelSearch();
fetchingFilterSettings$ = this._store.fetchingFilter$;
@@ -37,12 +42,7 @@ export class CustomerFilterMainViewComponent {
});
}
constructor(
private _store: CustomerSearchStore,
private _location: Location,
private _router: Router,
private _customerSearchNavigation: CustomerSearchNavigation,
) {
constructor() {
this.cancelSearch();
}

View File

@@ -1,6 +1,8 @@
import { Component, ChangeDetectionStrategy, AfterViewInit } from '@angular/core';
import { Component, ChangeDetectionStrategy, AfterViewInit, inject } from '@angular/core';
import { CrmCustomerService } from '@domain/crm';
import { ComponentStore, tapResponse } from '@ngrx/component-store';
import { ComponentStore } from '@ngrx/component-store';
import { tapResponse } from '@ngrx/operators';
import { HistoryDTO } from '@swagger/crm';
import { Observable, combineLatest } from 'rxjs';
import { map, switchMap, tap } from 'rxjs/operators';
@@ -19,6 +21,10 @@ export interface CustomerHistoryViewMainState {
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CustomerHistoryMainViewComponent extends ComponentStore<CustomerHistoryViewMainState> implements AfterViewInit {
private _store = inject(CustomerSearchStore);
private _customerService = inject(CrmCustomerService);
private _navigation = inject(CustomerSearchNavigation);
fetching$ = this.select((s) => s.fetching);
history$ = this.select((s) => s.history);
@@ -41,11 +47,7 @@ export class CustomerHistoryMainViewComponent extends ComponentStore<CustomerHis
customerNumber$ = this.customer$.pipe(map((customer) => customer?.customerNumber));
constructor(
private _store: CustomerSearchStore,
private _customerService: CrmCustomerService,
private _navigation: CustomerSearchNavigation,
) {
constructor() {
super({});
}

View File

@@ -1,4 +1,4 @@
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy } from '@angular/core';
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, inject } from '@angular/core';
import { CustomerSearchStore } from '../store';
import { ActivatedRoute, RouterLink } from '@angular/router';
import { Subject, combineLatest, of } from 'rxjs';
@@ -21,6 +21,11 @@ import { CustomerMenuComponent } from '../../components/customer-menu';
imports: [CustomerMenuComponent, KundenkarteComponent, NgFor, AsyncPipe, NgIf, IconComponent, RouterLink],
})
export class KundenkarteMainViewComponent implements OnInit, OnDestroy {
private _store = inject(CustomerSearchStore);
private _activatedRoute = inject(ActivatedRoute);
private _customerService = inject(CrmCustomerService);
private _navigation = inject(CustomerSearchNavigation);
private _onDestroy$ = new Subject<void>();
customerId$ = this._activatedRoute.params.pipe(map((params) => params.customerId));
@@ -47,13 +52,6 @@ export class KundenkarteMainViewComponent implements OnInit, OnDestroy {
map(([processId, customerId]) => this._navigation.detailsRoute({ processId, customerId })),
);
constructor(
private _store: CustomerSearchStore,
private _activatedRoute: ActivatedRoute,
private _customerService: CrmCustomerService,
private _navigation: CustomerSearchNavigation,
) {}
ngOnInit() {
this.customerId$.subscribe((customerId) => {
this._store.selectCustomer(customerId);

View File

@@ -1,5 +1,5 @@
import { AsyncPipe, CommonModule, NgIf } from '@angular/common';
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { Component, ChangeDetectionStrategy, inject } from '@angular/core';
import { RouterModule } from '@angular/router';
import { Filter, FilterModule } from '@shared/components/filter';
import { CustomerSearchStore } from '../store';
@@ -17,6 +17,9 @@ import { CustomerInfoDTO } from '@swagger/crm';
imports: [CommonModule, RouterModule, FilterModule, AsyncPipe, NgIf],
})
export class MainSideViewComponent {
private _store = inject(CustomerSearchStore);
private _customerCreateNavigation = inject(CustomerCreateNavigation);
message$ = this._store.message$;
filter$ = this._store.filter$;
@@ -45,11 +48,6 @@ export class MainSideViewComponent {
}),
);
constructor(
private _store: CustomerSearchStore,
private _customerCreateNavigation: CustomerCreateNavigation,
) {}
search(filter: Filter) {
this._store.setFilter(filter);
this._store.search({ resetScrollIndex: true, ignoreRestore: true });

View File

@@ -1,4 +1,4 @@
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { Component, ChangeDetectionStrategy, inject } from '@angular/core';
import { CustomerSearchStore } from '../store';
import { Filter, FilterModule } from '@shared/components/filter';
import { map } from 'rxjs/operators';
@@ -21,6 +21,11 @@ import { CustomerInfoDTO } from '@swagger/crm';
imports: [AsyncPipe, RouterLink, FilterModule, NgIf, IconComponent, CustomerFilterMainViewModule],
})
export class CustomerMainViewComponent {
private _searchNavigation = inject(CustomerSearchNavigation);
private _customerCreateNavigation = inject(CustomerCreateNavigation);
private _store = inject(CustomerSearchStore);
private _router = inject(Router);
filterRoute$ = combineLatest([this._store.processId$, this._store.filter$]).pipe(
map(([processId, filter]) => {
const route = this._searchNavigation.filterRoute({ processId, comingFrom: this._router.url?.split('?')[0] });
@@ -66,13 +71,6 @@ export class CustomerMainViewComponent {
message$ = this._store.message$;
constructor(
private _searchNavigation: CustomerSearchNavigation,
private _customerCreateNavigation: CustomerCreateNavigation,
private _store: CustomerSearchStore,
private _router: Router,
) {}
search(filter: Filter) {
this._store.setFilter(filter);
this._store.search({ resetScrollIndex: true, ignoreRestore: true });

View File

@@ -1,6 +1,7 @@
import { Component, ChangeDetectionStrategy, AfterViewInit, OnInit, OnDestroy } from '@angular/core';
import { CrmCustomerService } from '@domain/crm';
import { ComponentStore, tapResponse } from '@ngrx/component-store';
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, inject } from '@angular/core';
import { ComponentStore } from '@ngrx/component-store';
import { tapResponse } from '@ngrx/operators';
import { HistoryDTO } from '@swagger/crm';
import { Observable, Subject, combineLatest } from 'rxjs';
import { map, switchMap, takeUntil, tap } from 'rxjs/operators';
@@ -29,6 +30,11 @@ export class CustomerOrderDetailsHistoryMainViewComponent
extends ComponentStore<CustomerHistoryViewMainState>
implements OnInit, OnDestroy
{
private _activvatedRoute = inject(ActivatedRoute);
private _store = inject(CustomerSearchStore);
private _omsService = inject(DomainOmsService);
private _navigation = inject(CustomerSearchNavigation);
private _onDestroy$ = new Subject<void>();
fetching$ = this.select((s) => s.fetching);
@@ -61,13 +67,7 @@ export class CustomerOrderDetailsHistoryMainViewComponent
),
);
constructor(
private _activvatedRoute: ActivatedRoute,
private _store: CustomerSearchStore,
private _customerService: CrmCustomerService,
private _omsService: DomainOmsService,
private _navigation: CustomerSearchNavigation,
) {
constructor() {
super({});
}

View File

@@ -1,4 +1,4 @@
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy } from '@angular/core';
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, inject } from '@angular/core';
import { CustomerSearchStore } from '../store';
import { ActivatedRoute, RouterLink } from '@angular/router';
import { map, takeUntil, tap } from 'rxjs/operators';
@@ -40,6 +40,11 @@ import { EnvironmentService } from '@core/environment';
],
})
export class OrderDetailsMainViewComponent implements OnInit, OnDestroy {
private _activateRoute = inject(ActivatedRoute);
private _store = inject(CustomerSearchStore);
private _navigation = inject(CustomerSearchNavigation);
private _env = inject(EnvironmentService);
orderId$ = this._activateRoute.params.pipe(map((params) => Number(params.orderId)));
order$ = this._store.order$;
@@ -87,13 +92,6 @@ export class OrderDetailsMainViewComponent implements OnInit, OnDestroy {
showItemList$ = this.showSelectedItem$.pipe(map((showSelectedItem) => !showSelectedItem));
constructor(
private _activateRoute: ActivatedRoute,
private _store: CustomerSearchStore,
private _navigation: CustomerSearchNavigation,
private _env: EnvironmentService,
) {}
ngOnInit(): void {
this.orderId$.pipe(takeUntil(this._onDestroy)).subscribe((orderId) => {
this._store.selectOrder(orderId);

View File

@@ -1,5 +1,5 @@
import { AsyncPipe, CurrencyPipe, DatePipe, NgIf } from '@angular/common';
import { Component, ChangeDetectionStrategy, Input, OnDestroy, OnInit } from '@angular/core';
import { Component, ChangeDetectionStrategy, Input, OnDestroy, OnInit, inject } from '@angular/core';
import { ActivatedRoute, RouterLink } from '@angular/router';
import { ProductImagePipe } from '@cdn/product-image';
import { IconComponent } from '@shared/components/icon';
@@ -35,6 +35,10 @@ import { PaymentTypePipe } from '@shared/pipes/customer';
],
})
export class CustomerOrderItemListItemComponent implements OnInit, OnDestroy {
private _activatedRoute = inject(ActivatedRoute);
private _store = inject(CustomerSearchStore);
private _navigation = inject(CustomerSearchNavigation);
private _onDestroy = new Subject<void>();
private _orderItemSub = new BehaviorSubject<OrderItemDTO>(undefined);
@@ -69,12 +73,6 @@ export class CustomerOrderItemListItemComponent implements OnInit, OnDestroy {
processingStatus$ = this.orderItemSubsetItem$.pipe(map((subsetItem) => subsetItem?.processingStatus));
constructor(
private _activatedRoute: ActivatedRoute,
private _store: CustomerSearchStore,
private _navigation: CustomerSearchNavigation,
) {}
ngOnInit() {
this.customerId$.pipe(takeUntil(this._onDestroy)).subscribe((customerId) => {
this._store.selectCustomer({ customerId });

View File

@@ -24,10 +24,7 @@ export class OrderDetailsSideViewComponent {
selectedOrderItemId$ = this._store.selectedOrderItemId$;
constructor(
private _store: CustomerSearchStore,
private _navigation: CustomerSearchNavigation,
) {}
constructor(private _store: CustomerSearchStore) {}
selectOrderItem(orderItemId: number) {
this._store.selectOrderItemId(orderItemId);

View File

@@ -1,5 +1,5 @@
import { AsyncPipe, CurrencyPipe, DatePipe, NgIf } from '@angular/common';
import { Component, ChangeDetectionStrategy, Input, OnInit } from '@angular/core';
import { Component, ChangeDetectionStrategy, Input, inject } from '@angular/core';
import { OrderDestinationPipe, OrderProcessingStatusPipe } from '@shared/pipes/order';
import { AddressPipe } from '@shared/pipes/customer';
@@ -20,6 +20,9 @@ import { RouterLink } from '@angular/router';
imports: [DatePipe, OrderProcessingStatusPipe, OrderDestinationPipe, CurrencyPipe, NgIf, AddressPipe, AsyncPipe, RouterLink],
})
export class CustomerOrderListItemComponent {
private _store = inject(CustomerSearchStore);
private _navigation = inject(CustomerSearchNavigation);
private _orderListItemSub = new BehaviorSubject<OrderListItemDTO>(undefined);
@Input()
@@ -33,9 +36,4 @@ export class CustomerOrderListItemComponent {
orderDetailsRoute$ = combineLatest([this._store.processId$, this._store.customerId$, this._orderListItemSub]).pipe(
map(([processId, customerId, order]) => this._navigation.orderDetialsRoute({ processId, customerId, orderId: order?.id })),
);
constructor(
private _store: CustomerSearchStore,
private _navigation: CustomerSearchNavigation,
) {}
}

View File

@@ -1,4 +1,4 @@
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy } from '@angular/core';
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, inject } from '@angular/core';
import { CustomerSearchStore } from '../store';
import { Subject, combineLatest } from 'rxjs';
import { distinctUntilChanged, map, takeUntil } from 'rxjs/operators';
@@ -20,6 +20,9 @@ import { CustomerMenuComponent } from '../../components/customer-menu';
imports: [CustomerMenuComponent, AsyncPipe, NgFor, NgIf, RouterLink, IconComponent, LoaderComponent, CustomerOrderListItemComponent],
})
export class CustomerOrdersMainViewComponent implements OnInit, OnDestroy {
private _store = inject(CustomerSearchStore);
private _navigation = inject(CustomerSearchNavigation);
private _onDestroy = new Subject<void>();
processId$ = this._store.processId$;
@@ -42,11 +45,6 @@ export class CustomerOrdersMainViewComponent implements OnInit, OnDestroy {
map(([processId, customerId]) => this._navigation.detailsRoute({ processId, customerId })),
);
constructor(
private _store: CustomerSearchStore,
private _navigation: CustomerSearchNavigation,
) {}
ngOnInit(): void {
this._store.customer$
.pipe(

View File

@@ -1,4 +1,4 @@
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, AfterContentInit, ViewChild } from '@angular/core';
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, AfterContentInit, ViewChild, inject } from '@angular/core';
import { NavigationEnd, Router } from '@angular/router';
import { CustomerSearchStore } from '../store/customer-search.store';
import { BehaviorSubject, Subject, Subscription, combineLatest, race } from 'rxjs';
@@ -17,6 +17,11 @@ import { injectCancelSearch } from '@shared/services/cancel-subject';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CustomerResultsMainViewComponent implements OnInit, OnDestroy, AfterContentInit {
private _store = inject(CustomerSearchStore);
private _router = inject(Router);
private _navigation = inject(CustomerSearchNavigation);
private _environment = inject(EnvironmentService);
cancelSearch = injectCancelSearch();
processId$ = this._store.processId$;
@@ -72,13 +77,6 @@ export class CustomerResultsMainViewComponent implements OnInit, OnDestroy, Afte
private _onDestroy$ = new Subject<void>();
constructor(
private _store: CustomerSearchStore,
private _router: Router,
private _navigation: CustomerSearchNavigation,
private _environment: EnvironmentService,
) {}
ngOnInit(): void {
this.routerEventsSubscription = this._router.events.subscribe((event) => {
if (event instanceof NavigationEnd) {

View File

@@ -1,4 +1,4 @@
import { Component, ChangeDetectionStrategy, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { Component, ChangeDetectionStrategy, OnDestroy, OnInit, ViewChild, inject } from '@angular/core';
import { Router, NavigationEnd } from '@angular/router';
import { CustomerSearchStore } from '../store/customer-search.store';
import { Subscription, BehaviorSubject, combineLatest, Subject, race } from 'rxjs';
@@ -17,6 +17,11 @@ import { injectCancelSearch } from '@shared/services/cancel-subject';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CustomerResultsSideViewComponent implements OnInit, OnDestroy {
private _store = inject(CustomerSearchStore);
private _router = inject(Router);
private _navigation = inject(CustomerSearchNavigation);
private _location = inject(Location);
cancelSearch = injectCancelSearch();
processId$ = this._store.processId$;
@@ -75,13 +80,6 @@ export class CustomerResultsSideViewComponent implements OnInit, OnDestroy {
private _onDestroy$ = new Subject<void>();
constructor(
private _store: CustomerSearchStore,
private _router: Router,
private _navigation: CustomerSearchNavigation,
private _location: Location,
) {}
ngOnInit(): void {
this.routerEventsSubscription = this._router.events.subscribe((event) => {
if (event instanceof NavigationEnd) {

View File

@@ -1,7 +1,9 @@
import { ComponentStore, tapResponse, OnStoreInit } from '@ngrx/component-store';
import { ComponentStore, OnStoreInit } from '@ngrx/component-store';
import { tapResponse } from '@ngrx/operators';
import { CustomerSearchState } from './customer-search.state';
import * as S from './selectors';
import { Injectable, OnDestroy } from '@angular/core';
import { inject, Injectable, OnDestroy } from '@angular/core';
import { Observable, Subject } from 'rxjs';
import { distinctUntilChanged, filter, switchMap, takeUntil, tap, withLatestFrom, delayWhen, first, map } from 'rxjs/operators';
import { CrmCustomerService } from '@domain/crm';
@@ -16,6 +18,10 @@ import { injectCancelSearchSubject } from '@shared/services/cancel-subject';
@Injectable()
export class CustomerSearchStore extends ComponentStore<CustomerSearchState> implements OnStoreInit, OnDestroy {
private _customerService = inject(CrmCustomerService);
private _omsService = inject(DomainOmsService);
private _modal = inject(UiModalService);
private _cancelSearch = injectCancelSearchSubject();
get processId() {
@@ -164,11 +170,7 @@ export class CustomerSearchStore extends ComponentStore<CustomerSearchState> imp
selectedOrderItem$ = this.select(S.selectSelectedOrderItem);
constructor(
private _customerService: CrmCustomerService,
private _omsService: DomainOmsService,
private _modal: UiModalService,
) {
constructor() {
super({ customerListCount: 0 });
}

View File

@@ -1,15 +1,13 @@
import { Injectable } from '@angular/core';
import { inject, Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Params, Router, RouterStateSnapshot } from '@angular/router';
import { DomainCheckoutService } from '@domain/checkout';
import { CustomerCreateFormData, decodeFormData } from '../create-customer';
import { CustomerCreateNavigation } from '@shared/services/navigation';
@Injectable({ providedIn: 'root' })
export class CustomerCreateGuard {
constructor(
private router: Router,
private checkoutService: DomainCheckoutService,
private customerCreateNavigation: CustomerCreateNavigation,
) {}
private router = inject(Router);
private checkoutService = inject(DomainCheckoutService);
private customerCreateNavigation = inject(CustomerCreateNavigation);
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
// exit with true if canActivateChild will be called

View File

@@ -1,4 +1,4 @@
import { Injectable } from '@angular/core';
import { inject, Injectable } from '@angular/core';
import { CrmCustomerService } from '@domain/crm';
import { AddressDTO } from '@swagger/crm';
import { UiModalService } from '@ui/modal';
@@ -7,10 +7,9 @@ import { DeprecatedAddressSelectionModalComponent } from './address-selection-mo
@Injectable({ providedIn: 'root' })
export class AddressSelectionModalService {
constructor(
private customerService: CrmCustomerService,
private modal: UiModalService,
) {}
private modal = inject(UiModalService);
constructor(private customerService: CrmCustomerService) {}
async validateAddress(address: AddressDTO): Promise<AddressDTO> {
if (address.street && address.streetNumber && address.zipCode && address.city && address.country) {

View File

@@ -1,7 +1,9 @@
import { HttpErrorResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { DomainGoodsService } from '@domain/oms';
import { ComponentStore, tapResponse } from '@ngrx/component-store';
import { ComponentStore } from '@ngrx/component-store';
import { tapResponse } from '@ngrx/operators';
import { ListResponseArgsOfOrderItemListItemDTO, OrderItemListItemDTO } from '@swagger/oms';
import { isResponseArgs } from '@utils/object';
import { Subject } from 'rxjs';

View File

@@ -2,7 +2,9 @@ import { HttpErrorResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { DomainGoodsService } from '@domain/oms';
import { ComponentStore, tapResponse } from '@ngrx/component-store';
import { ComponentStore } from '@ngrx/component-store';
import { tapResponse } from '@ngrx/operators';
import { ListResponseArgsOfOrderItemListItemDTO, OrderItemListItemDTO, QueryTokenDTO } from '@swagger/oms';
import { UiFilter } from '@ui/filter';
import { isResponseArgs } from '@utils/object';

View File

@@ -1,7 +1,9 @@
import { HttpErrorResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { DomainGoodsService } from '@domain/oms';
import { ComponentStore, tapResponse } from '@ngrx/component-store';
import { ComponentStore } from '@ngrx/component-store';
import { tapResponse } from '@ngrx/operators';
import { ListResponseArgsOfOrderItemListItemDTO, OrderItemListItemDTO } from '@swagger/oms';
import { isResponseArgs } from '@utils/object';
import { Subject } from 'rxjs';

View File

@@ -2,7 +2,9 @@ import { HttpErrorResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { DomainAvailabilityService } from '@domain/availability';
import { DomainGoodsService } from '@domain/oms';
import { ComponentStore, tapResponse } from '@ngrx/component-store';
import { ComponentStore } from '@ngrx/component-store';
import { tapResponse } from '@ngrx/operators';
import { ListResponseArgsOfOrderItemListItemDTO, OrderItemListItemDTO, QueryTokenDTO } from '@swagger/oms';
import { isResponseArgs } from '@utils/object';
import { Subject } from 'rxjs';

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