mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-31 09:37:15 +01:00
✨ feat(isa-app): migrate to standalone component architecture
- Replace NgModule bootstrap with bootstrapApplication and ApplicationConfig - Convert app.module.ts to app.config.ts with provider functions - Convert routing module to standalone routes array - Remove domain NgModules (services now providedIn: 'root') - Remove NgRx application store in favor of signals - Update icon components and registries for modern patterns - Update page components for standalone compatibility
This commit is contained in:
@@ -1,19 +1,22 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { DevScanAdapter } from './dev.scan-adapter';
|
import { DevScanAdapter } from './dev.scan-adapter';
|
||||||
import { NativeScanAdapter } from './native.scan-adapter';
|
import { NativeScanAdapter } from './native.scan-adapter';
|
||||||
import { SCAN_ADAPTER } from './tokens';
|
import { SCAN_ADAPTER } from './tokens';
|
||||||
|
|
||||||
@NgModule({})
|
/**
|
||||||
export class ScanAdapterModule {
|
* @deprecated Use '@isa/shared/scanner' instead.
|
||||||
static forRoot() {
|
*/
|
||||||
return {
|
@NgModule({})
|
||||||
ngModule: ScanAdapterModule,
|
export class ScanAdapterModule {
|
||||||
providers: [
|
static forRoot() {
|
||||||
{ provide: SCAN_ADAPTER, useClass: NativeScanAdapter, multi: true },
|
return {
|
||||||
{ provide: SCAN_ADAPTER, useClass: DevScanAdapter, multi: true },
|
ngModule: ScanAdapterModule,
|
||||||
],
|
providers: [
|
||||||
// Use for testing:
|
{ provide: SCAN_ADAPTER, useClass: NativeScanAdapter, multi: true },
|
||||||
// providers: [{ provide: SCAN_ADAPTER, useClass: dev ? DevScanAdapter : NativeScanAdapter, multi: true }],
|
{ provide: SCAN_ADAPTER, useClass: DevScanAdapter, multi: true },
|
||||||
};
|
],
|
||||||
}
|
// Use for testing:
|
||||||
}
|
// providers: [{ provide: SCAN_ADAPTER, useClass: dev ? DevScanAdapter : NativeScanAdapter, multi: true }],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,20 +1,25 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
import { ScanditOverlayComponent } from './scandit-overlay.component';
|
import { ScanditOverlayComponent } from './scandit-overlay.component';
|
||||||
import { ScanditScanAdapter } from './scandit.scan-adapter';
|
import { ScanditScanAdapter } from './scandit.scan-adapter';
|
||||||
import { SCAN_ADAPTER } from '../tokens';
|
import { SCAN_ADAPTER } from '../tokens';
|
||||||
|
|
||||||
@NgModule({
|
/**
|
||||||
imports: [CommonModule],
|
* @deprecated Use @isa/shared/scanner instead.
|
||||||
exports: [ScanditOverlayComponent],
|
*/
|
||||||
declarations: [ScanditOverlayComponent],
|
@NgModule({
|
||||||
})
|
imports: [CommonModule],
|
||||||
export class ScanditScanAdapterModule {
|
exports: [ScanditOverlayComponent],
|
||||||
static forRoot() {
|
declarations: [ScanditOverlayComponent],
|
||||||
return {
|
})
|
||||||
ngModule: ScanditScanAdapterModule,
|
export class ScanditScanAdapterModule {
|
||||||
providers: [{ provide: SCAN_ADAPTER, useClass: ScanditScanAdapter, multi: true }],
|
static forRoot() {
|
||||||
};
|
return {
|
||||||
}
|
ngModule: ScanditScanAdapterModule,
|
||||||
}
|
providers: [
|
||||||
|
{ provide: SCAN_ADAPTER, useClass: ScanditScanAdapter, multi: true },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
import { NgModule } from '@angular/core';
|
|
||||||
import { DomainAvailabilityModule } from '@domain/availability';
|
|
||||||
import { DomainCatalogModule } from '@domain/catalog';
|
|
||||||
import { DomainIsaModule } from '@domain/isa';
|
|
||||||
import { DomainCheckoutModule } from '@domain/checkout';
|
|
||||||
import { DomainOmsModule } from '@domain/oms';
|
|
||||||
import { DomainRemissionModule } from '@domain/remission';
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
imports: [
|
|
||||||
DomainIsaModule.forRoot(),
|
|
||||||
DomainCatalogModule.forRoot(),
|
|
||||||
DomainAvailabilityModule.forRoot(),
|
|
||||||
DomainCheckoutModule.forRoot(),
|
|
||||||
DomainOmsModule.forRoot(),
|
|
||||||
DomainRemissionModule.forRoot(),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
export class AppDomainModule {}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
import { NgModule } from '@angular/core';
|
|
||||||
import { EffectsModule } from '@ngrx/effects';
|
|
||||||
import { ActionReducer, MetaReducer, StoreModule } from '@ngrx/store';
|
|
||||||
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
|
|
||||||
import { environment } from '../environments/environment';
|
|
||||||
import { rootReducer } from './store/root.reducer';
|
|
||||||
import { RootState } from './store/root.state';
|
|
||||||
|
|
||||||
export function storeInLocalStorage(
|
|
||||||
reducer: ActionReducer<any>,
|
|
||||||
): ActionReducer<any> {
|
|
||||||
return function (state, action) {
|
|
||||||
if (action.type === 'HYDRATE') {
|
|
||||||
return reducer(action['payload'], action);
|
|
||||||
}
|
|
||||||
return reducer(state, action);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export const metaReducers: MetaReducer<RootState>[] = !environment.production
|
|
||||||
? [storeInLocalStorage]
|
|
||||||
: [storeInLocalStorage];
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
imports: [
|
|
||||||
StoreModule.forRoot(rootReducer, { metaReducers }),
|
|
||||||
EffectsModule.forRoot([]),
|
|
||||||
StoreDevtoolsModule.instrument({
|
|
||||||
name: 'ISA Ngrx Application Store',
|
|
||||||
connectInZone: true,
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
export class AppStoreModule {}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
import { HttpInterceptorFn, provideHttpClient, withInterceptors } from '@angular/common/http';
|
|
||||||
import { NgModule } from '@angular/core';
|
|
||||||
import { Config } from '@core/config';
|
|
||||||
import { AvConfiguration } from '@generated/swagger/availability-api';
|
|
||||||
import { CatConfiguration } from '@generated/swagger/cat-search-api';
|
|
||||||
import { CheckoutConfiguration } from '@generated/swagger/checkout-api';
|
|
||||||
import { CrmConfiguration } from '@generated/swagger/crm-api';
|
|
||||||
import { EisConfiguration } from '@generated/swagger/eis-api';
|
|
||||||
import { IsaConfiguration } from '@generated/swagger/isa-api';
|
|
||||||
import { OmsConfiguration } from '@generated/swagger/oms-api';
|
|
||||||
import { PrintConfiguration } from '@generated/swagger/print-api';
|
|
||||||
import { RemiConfiguration } from '@generated/swagger/inventory-api';
|
|
||||||
import { WwsConfiguration } from '@generated/swagger/wws-api';
|
|
||||||
|
|
||||||
export function createConfigurationFactory(name: string) {
|
|
||||||
return function (config: Config): { rootUrl: string } {
|
|
||||||
return config.get(`@swagger/${name}`);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const serviceWorkerInterceptor: HttpInterceptorFn = (req, next) => {
|
|
||||||
return next(req.clone({ setHeaders: { 'ngsw-bypass': 'true' } }));
|
|
||||||
};
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
providers: [
|
|
||||||
provideHttpClient(withInterceptors([serviceWorkerInterceptor])),
|
|
||||||
{ provide: AvConfiguration, useFactory: createConfigurationFactory('av'), deps: [Config] },
|
|
||||||
{ provide: CatConfiguration, useFactory: createConfigurationFactory('cat'), deps: [Config] },
|
|
||||||
{ provide: CheckoutConfiguration, useFactory: createConfigurationFactory('checkout'), deps: [Config] },
|
|
||||||
{ provide: CrmConfiguration, useFactory: createConfigurationFactory('crm'), deps: [Config] },
|
|
||||||
{ provide: EisConfiguration, useFactory: createConfigurationFactory('eis'), deps: [Config] },
|
|
||||||
{ provide: IsaConfiguration, useFactory: createConfigurationFactory('isa'), deps: [Config] },
|
|
||||||
{ provide: OmsConfiguration, useFactory: createConfigurationFactory('oms'), deps: [Config] },
|
|
||||||
{ provide: PrintConfiguration, useFactory: createConfigurationFactory('print'), deps: [Config] },
|
|
||||||
{ provide: RemiConfiguration, useFactory: createConfigurationFactory('remi'), deps: [Config] },
|
|
||||||
{ provide: WwsConfiguration, useFactory: createConfigurationFactory('wws'), deps: [Config] },
|
|
||||||
],
|
|
||||||
})
|
|
||||||
export class AppSwaggerModule {}
|
|
||||||
@@ -1,28 +1,28 @@
|
|||||||
@if ($offlineBannerVisible()) {
|
<!-- @if ($offlineBannerVisible()) {
|
||||||
<div [@fadeInOut] class="bg-brand text-white text-center fixed inset-x-0 top-0 z-tooltip p-4">
|
<div [@fadeInOut] class="bg-brand text-white text-center fixed inset-x-0 top-0 z-tooltip p-4">
|
||||||
<h3 class="font-bold grid grid-flow-col items-center justify-center text-xl gap-4">
|
<h3 class="font-bold grid grid-flow-col items-center justify-center text-xl gap-4">
|
||||||
<div>
|
<div>
|
||||||
<ng-icon name="matWifiOff"></ng-icon>
|
<ng-icon name="matWifiOff"></ng-icon>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>Sie sind offline, keine Verbindung zum Netzwerk.</div>
|
<div>Sie sind offline, keine Verbindung zum Netzwerk.</div>
|
||||||
</h3>
|
</h3>
|
||||||
<p>Bereits geladene Ihnalte werden angezeigt, Interaktionen sind aktuell nicht möglich.</p>
|
<p>Bereits geladene Ihnalte werden angezeigt, Interaktionen sind aktuell nicht möglich.</p>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@if ($onlineBannerVisible()) {
|
@if ($onlineBannerVisible()) {
|
||||||
<div [@fadeInOut] class="bg-green-500 text-white text-center fixed inset-x-0 top-0 z-tooltip p-4">
|
<div [@fadeInOut] class="bg-green-500 text-white text-center fixed inset-x-0 top-0 z-tooltip p-4">
|
||||||
<h3 class="font-bold grid grid-flow-col items-center justify-center text-xl gap-4">
|
<h3 class="font-bold grid grid-flow-col items-center justify-center text-xl gap-4">
|
||||||
<div>
|
<div>
|
||||||
<ng-icon name="matWifi"></ng-icon>
|
<ng-icon name="matWifi"></ng-icon>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>Sie sind wieder online.</div>
|
<div>Sie sind wieder online.</div>
|
||||||
</h3>
|
</h3>
|
||||||
<button class="fixed top-2 right-4 text-3xl w-12 h-12" type="button" (click)="$onlineBannerVisible.set(false)">
|
<button class="fixed top-2 right-4 text-3xl w-12 h-12" type="button" (click)="$onlineBannerVisible.set(false)">
|
||||||
<ng-icon name="matClose"></ng-icon>
|
<ng-icon name="matClose"></ng-icon>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet> -->
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
:host {
|
|
||||||
@apply block;
|
|
||||||
}
|
|
||||||
@@ -1,137 +0,0 @@
|
|||||||
import { Spectator, createComponentFactory, SpyObject, createSpyObject } from '@ngneat/spectator';
|
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
|
||||||
import { AppComponent } from './app.component';
|
|
||||||
import { Config } from '@core/config';
|
|
||||||
import { ApplicationService } from '@core/application';
|
|
||||||
import { of } from 'rxjs';
|
|
||||||
import { Renderer2 } from '@angular/core';
|
|
||||||
import { CommonModule } from '@angular/common';
|
|
||||||
import { SwUpdate } from '@angular/service-worker';
|
|
||||||
import { NotificationsHub } from '@hub/notifications';
|
|
||||||
import { UserStateService } from '@generated/swagger/isa-api';
|
|
||||||
import { UiModalService } from '@ui/modal';
|
|
||||||
import { AuthService } from '@core/auth';
|
|
||||||
|
|
||||||
describe('AppComponent', () => {
|
|
||||||
let spectator: Spectator<AppComponent>;
|
|
||||||
let config: SpyObject<Config>;
|
|
||||||
let renderer: SpyObject<Renderer2>;
|
|
||||||
let applicationServiceMock: SpyObject<ApplicationService>;
|
|
||||||
let notificationsHubMock: SpyObject<NotificationsHub>;
|
|
||||||
let swUpdateMock: SpyObject<SwUpdate>;
|
|
||||||
const createComponent = createComponentFactory({
|
|
||||||
component: AppComponent,
|
|
||||||
imports: [CommonModule, RouterTestingModule],
|
|
||||||
providers: [],
|
|
||||||
mocks: [Config, SwUpdate, UserStateService, UiModalService, AuthService],
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
applicationServiceMock = createSpyObject(ApplicationService);
|
|
||||||
applicationServiceMock.getSection$.and.returnValue(of('customer'));
|
|
||||||
applicationServiceMock.getActivatedProcessId$.and.returnValue(of(undefined));
|
|
||||||
renderer = jasmine.createSpyObj('Renderer2', ['addClass', 'removeClass']);
|
|
||||||
|
|
||||||
notificationsHubMock = createSpyObject(NotificationsHub);
|
|
||||||
notificationsHubMock.notifications$ = of({});
|
|
||||||
swUpdateMock = createSpyObject(SwUpdate);
|
|
||||||
|
|
||||||
spectator = createComponent({
|
|
||||||
providers: [
|
|
||||||
{ provide: ApplicationService, useValue: applicationServiceMock },
|
|
||||||
{
|
|
||||||
provide: Renderer2,
|
|
||||||
useValue: renderer,
|
|
||||||
},
|
|
||||||
{ provide: NotificationsHub, useValue: notificationsHubMock },
|
|
||||||
{ provide: SwUpdate, useValue: swUpdateMock },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
config = spectator.inject(Config);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create the app', () => {
|
|
||||||
expect(spectator.component).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should have a router outlet', () => {
|
|
||||||
expect(spectator.query('router-outlet')).toExist();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('ngOnInit', () => {
|
|
||||||
it('should call setTitle', () => {
|
|
||||||
const spy = spyOn(spectator.component, 'setTitle');
|
|
||||||
spectator.component.ngOnInit();
|
|
||||||
expect(spy).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should call logVersion', () => {
|
|
||||||
const spy = spyOn(spectator.component, 'logVersion');
|
|
||||||
spectator.component.ngOnInit();
|
|
||||||
expect(spy).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('setTitle', () => {
|
|
||||||
it('should call Title.setTitle()', () => {
|
|
||||||
const spyTitleSetTitle = spyOn(spectator.component['_title'], 'setTitle');
|
|
||||||
config.get.and.returnValue('test');
|
|
||||||
spectator.component.setTitle();
|
|
||||||
expect(spyTitleSetTitle).toHaveBeenCalledWith('test');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('logVersion', () => {
|
|
||||||
it('should call console.log()', () => {
|
|
||||||
const spyConsoleLog = spyOn(console, 'log');
|
|
||||||
config.get.and.returnValue('test');
|
|
||||||
spectator.component.logVersion();
|
|
||||||
expect(spyConsoleLog).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// --------------------------------------------------------
|
|
||||||
// Unit Tests Implementation for Angular Version 13.x.x
|
|
||||||
|
|
||||||
// describe('updateClient()', () => {
|
|
||||||
// it('should call checkForUpdate() if SwUpdate.isEnabled is True', () => {
|
|
||||||
// spyOn(spectator.component, 'checkForUpdate');
|
|
||||||
// spyOn(spectator.component, 'initialCheckForUpdate');
|
|
||||||
// (swUpdateMock as any).isEnabled = true;
|
|
||||||
// spectator.component.updateClient();
|
|
||||||
// expect(spectator.component.initialCheckForUpdate).toHaveBeenCalled();
|
|
||||||
// expect(spectator.component.checkForUpdate).toHaveBeenCalled();
|
|
||||||
// });
|
|
||||||
|
|
||||||
// it('should not call checkForUpdate() if SwUpdate.isEnabled is False', () => {
|
|
||||||
// spyOn(spectator.component, 'checkForUpdate');
|
|
||||||
// spyOn(spectator.component, 'initialCheckForUpdate');
|
|
||||||
// (swUpdateMock as any).isEnabled = false;
|
|
||||||
// spectator.component.updateClient();
|
|
||||||
// expect(spectator.component.initialCheckForUpdate).not.toHaveBeenCalled();
|
|
||||||
// expect(spectator.component.checkForUpdate).not.toHaveBeenCalled();
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
|
||||||
// describe('checkForUpdate', () => {
|
|
||||||
// it('should call swUpdate.checkForUpdate() and notifications.updateNotification() every second', fakeAsync(() => {
|
|
||||||
// swUpdateMock.checkForUpdate.and.returnValue(Promise.resolve());
|
|
||||||
// spectator.component.checkForUpdates = 1000;
|
|
||||||
// spectator.component.checkForUpdate();
|
|
||||||
|
|
||||||
// spectator.detectChanges();
|
|
||||||
// tick(1100);
|
|
||||||
|
|
||||||
// expect(notificationsHubMock.updateNotification).toHaveBeenCalled();
|
|
||||||
// discardPeriodicTasks();
|
|
||||||
// }));
|
|
||||||
// });
|
|
||||||
|
|
||||||
// describe('initialCheckForUpdate', () => {
|
|
||||||
// it('should call swUpdate.checkForUpdate()', () => {
|
|
||||||
// swUpdateMock.checkForUpdate.and.returnValue(new Promise(undefined));
|
|
||||||
// spectator.component.initialCheckForUpdate();
|
|
||||||
// expect(swUpdateMock.checkForUpdate).toHaveBeenCalled();
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
});
|
|
||||||
@@ -1,206 +1,205 @@
|
|||||||
|
// import {
|
||||||
import {
|
// Component,
|
||||||
Component,
|
// effect,
|
||||||
effect,
|
// HostListener,
|
||||||
HostListener,
|
// inject,
|
||||||
inject,
|
// Inject,
|
||||||
Inject,
|
// Injector,
|
||||||
Injector,
|
// OnInit,
|
||||||
OnInit,
|
// Renderer2,
|
||||||
Renderer2,
|
// signal,
|
||||||
signal,
|
// untracked,
|
||||||
untracked,
|
// DOCUMENT
|
||||||
DOCUMENT
|
// } from '@angular/core';
|
||||||
} from '@angular/core';
|
// import { Title } from '@angular/platform-browser';
|
||||||
import { Title } from '@angular/platform-browser';
|
// import { SwUpdate } from '@angular/service-worker';
|
||||||
import { SwUpdate } from '@angular/service-worker';
|
// import { ApplicationService } from '@core/application';
|
||||||
import { ApplicationService } from '@core/application';
|
// import { Config } from '@core/config';
|
||||||
import { Config } from '@core/config';
|
// import { NotificationsHub } from '@hub/notifications';
|
||||||
import { NotificationsHub } from '@hub/notifications';
|
// import packageInfo from 'packageJson';
|
||||||
import packageInfo from 'packageJson';
|
// import { asapScheduler, interval, Subscription } from 'rxjs';
|
||||||
import { asapScheduler, interval, Subscription } from 'rxjs';
|
// import { UserStateService } from '@generated/swagger/isa-api';
|
||||||
import { UserStateService } from '@generated/swagger/isa-api';
|
// import { IsaLogProvider } from './providers';
|
||||||
import { IsaLogProvider } from './providers';
|
// import { EnvironmentService } from '@core/environment';
|
||||||
import { EnvironmentService } from '@core/environment';
|
// import { AuthService, LoginStrategy } from '@core/auth';
|
||||||
import { AuthService, LoginStrategy } from '@core/auth';
|
// import { UiMessageModalComponent, UiModalService } from '@ui/modal';
|
||||||
import { UiMessageModalComponent, UiModalService } from '@ui/modal';
|
// import { injectNetworkStatus } from '@isa/core/connectivity';
|
||||||
import { injectNetworkStatus } from '@isa/core/connectivity';
|
// import { animate, style, transition, trigger } from '@angular/animations';
|
||||||
import { animate, style, transition, trigger } from '@angular/animations';
|
|
||||||
|
// @Component({
|
||||||
@Component({
|
// selector: 'app-root',
|
||||||
selector: 'app-root',
|
// templateUrl: './app.component.html',
|
||||||
templateUrl: './app.component.html',
|
// styleUrls: ['./app.component.scss'],
|
||||||
styleUrls: ['./app.component.scss'],
|
// animations: [
|
||||||
animations: [
|
// trigger('fadeInOut', [
|
||||||
trigger('fadeInOut', [
|
// transition(':enter', [
|
||||||
transition(':enter', [
|
// // :enter wird ausgelöst, wenn das Element zum DOM hinzugefügt wird
|
||||||
// :enter wird ausgelöst, wenn das Element zum DOM hinzugefügt wird
|
// style({ opacity: 0, transform: 'translateY(-100%)' }),
|
||||||
style({ opacity: 0, transform: 'translateY(-100%)' }),
|
// animate('300ms', style({ opacity: 1, transform: 'translateY(0)' })),
|
||||||
animate('300ms', style({ opacity: 1, transform: 'translateY(0)' })),
|
// ]),
|
||||||
]),
|
// transition(':leave', [
|
||||||
transition(':leave', [
|
// // :leave wird ausgelöst, wenn das Element aus dem DOM entfernt wird
|
||||||
// :leave wird ausgelöst, wenn das Element aus dem DOM entfernt wird
|
// animate('300ms', style({ opacity: 0, transform: 'translateY(-100%)' })),
|
||||||
animate('300ms', style({ opacity: 0, transform: 'translateY(-100%)' })),
|
// ]),
|
||||||
]),
|
// ]),
|
||||||
]),
|
// ],
|
||||||
],
|
// standalone: false,
|
||||||
standalone: false,
|
// })
|
||||||
})
|
// export class AppComponent implements OnInit {
|
||||||
export class AppComponent implements OnInit {
|
// readonly injector = inject(Injector);
|
||||||
readonly injector = inject(Injector);
|
|
||||||
|
// $networkStatus = injectNetworkStatus();
|
||||||
$networkStatus = injectNetworkStatus();
|
|
||||||
|
// $offlineBannerVisible = signal(false);
|
||||||
$offlineBannerVisible = signal(false);
|
|
||||||
|
// $onlineBannerVisible = signal(false);
|
||||||
$onlineBannerVisible = signal(false);
|
|
||||||
|
// private onlineBannerDismissTimeout: any;
|
||||||
private onlineBannerDismissTimeout: any;
|
|
||||||
|
// onlineEffects = effect(() => {
|
||||||
onlineEffects = effect(() => {
|
// const status = this.$networkStatus();
|
||||||
const status = this.$networkStatus();
|
// const online = status === 'online';
|
||||||
const online = status === 'online';
|
// const offlineBannerVisible = this.$offlineBannerVisible();
|
||||||
const offlineBannerVisible = this.$offlineBannerVisible();
|
|
||||||
|
// untracked(() => {
|
||||||
untracked(() => {
|
// this.$offlineBannerVisible.set(!online);
|
||||||
this.$offlineBannerVisible.set(!online);
|
|
||||||
|
// if (!online) {
|
||||||
if (!online) {
|
// this.$onlineBannerVisible.set(false);
|
||||||
this.$onlineBannerVisible.set(false);
|
// clearTimeout(this.onlineBannerDismissTimeout);
|
||||||
clearTimeout(this.onlineBannerDismissTimeout);
|
// }
|
||||||
}
|
|
||||||
|
// if (offlineBannerVisible && online) {
|
||||||
if (offlineBannerVisible && online) {
|
// this.$onlineBannerVisible.set(true);
|
||||||
this.$onlineBannerVisible.set(true);
|
// this.onlineBannerDismissTimeout = setTimeout(() => this.$onlineBannerVisible.set(false), 5000);
|
||||||
this.onlineBannerDismissTimeout = setTimeout(() => this.$onlineBannerVisible.set(false), 5000);
|
// }
|
||||||
}
|
// });
|
||||||
});
|
// });
|
||||||
});
|
|
||||||
|
// private _checkForUpdates: number = this._config.get('checkForUpdates');
|
||||||
private _checkForUpdates: number = this._config.get('checkForUpdates');
|
|
||||||
|
// get checkForUpdates(): number {
|
||||||
get checkForUpdates(): number {
|
// return this._checkForUpdates ?? 60 * 60 * 1000; // default 1 hour
|
||||||
return this._checkForUpdates ?? 60 * 60 * 1000; // default 1 hour
|
// }
|
||||||
}
|
|
||||||
|
// // For Unit Testing
|
||||||
// For Unit Testing
|
// set checkForUpdates(time: number) {
|
||||||
set checkForUpdates(time: number) {
|
// this._checkForUpdates = time;
|
||||||
this._checkForUpdates = time;
|
// }
|
||||||
}
|
|
||||||
|
// subscriptions = new Subscription();
|
||||||
subscriptions = new Subscription();
|
|
||||||
|
// constructor(
|
||||||
constructor(
|
// private readonly _config: Config,
|
||||||
private readonly _config: Config,
|
// private readonly _title: Title,
|
||||||
private readonly _title: Title,
|
// private readonly _appService: ApplicationService,
|
||||||
private readonly _appService: ApplicationService,
|
// @Inject(DOCUMENT) private readonly _document: Document,
|
||||||
@Inject(DOCUMENT) private readonly _document: Document,
|
// private readonly _renderer: Renderer2,
|
||||||
private readonly _renderer: Renderer2,
|
// private readonly _swUpdate: SwUpdate,
|
||||||
private readonly _swUpdate: SwUpdate,
|
// private readonly _notifications: NotificationsHub,
|
||||||
private readonly _notifications: NotificationsHub,
|
// private infoService: UserStateService,
|
||||||
private infoService: UserStateService,
|
// private readonly _environment: EnvironmentService,
|
||||||
private readonly _environment: EnvironmentService,
|
// private readonly _authService: AuthService,
|
||||||
private readonly _authService: AuthService,
|
// private readonly _modal: UiModalService,
|
||||||
private readonly _modal: UiModalService,
|
// ) {
|
||||||
) {
|
// this.updateClient();
|
||||||
this.updateClient();
|
// IsaLogProvider.InfoService = this.infoService;
|
||||||
IsaLogProvider.InfoService = this.infoService;
|
// }
|
||||||
}
|
|
||||||
|
// ngOnInit() {
|
||||||
ngOnInit() {
|
// this.setTitle();
|
||||||
this.setTitle();
|
// this.logVersion();
|
||||||
this.logVersion();
|
// asapScheduler.schedule(() => this.determinePlatform(), 250);
|
||||||
asapScheduler.schedule(() => this.determinePlatform(), 250);
|
// this._appService.getSection$().subscribe(this.sectionChangeHandler.bind(this));
|
||||||
this._appService.getSection$().subscribe(this.sectionChangeHandler.bind(this));
|
|
||||||
|
// this.setupSilentRefresh();
|
||||||
this.setupSilentRefresh();
|
// }
|
||||||
}
|
|
||||||
|
// // Setup interval for silent refresh
|
||||||
// Setup interval for silent refresh
|
// setupSilentRefresh() {
|
||||||
setupSilentRefresh() {
|
// const silentRefreshInterval = this._config.get('silentRefresh.interval');
|
||||||
const silentRefreshInterval = this._config.get('silentRefresh.interval');
|
// if (silentRefreshInterval > 0) {
|
||||||
if (silentRefreshInterval > 0) {
|
// interval(silentRefreshInterval).subscribe(() => {
|
||||||
interval(silentRefreshInterval).subscribe(() => {
|
// if (this._authService.isAuthenticated()) {
|
||||||
if (this._authService.isAuthenticated()) {
|
// this._authService.refresh();
|
||||||
this._authService.refresh();
|
// }
|
||||||
}
|
// });
|
||||||
});
|
// }
|
||||||
}
|
// }
|
||||||
}
|
|
||||||
|
// setTitle() {
|
||||||
setTitle() {
|
// this._title.setTitle(this._config.get('title'));
|
||||||
this._title.setTitle(this._config.get('title'));
|
// }
|
||||||
}
|
|
||||||
|
// logVersion() {
|
||||||
logVersion() {
|
// console.log(
|
||||||
console.log(
|
// `%c${this._config.get('title')}\r\nVersion: ${packageInfo.version}`,
|
||||||
`%c${this._config.get('title')}\r\nVersion: ${packageInfo.version}`,
|
// 'font-weight: bold; font-size: 20px;',
|
||||||
'font-weight: bold; font-size: 20px;',
|
// );
|
||||||
);
|
// }
|
||||||
}
|
|
||||||
|
// determinePlatform() {
|
||||||
determinePlatform() {
|
// if (this._environment.isNative()) {
|
||||||
if (this._environment.isNative()) {
|
// this._renderer.addClass(this._document.body, 'tablet-native');
|
||||||
this._renderer.addClass(this._document.body, 'tablet-native');
|
// } else if (this._environment.isTablet()) {
|
||||||
} else if (this._environment.isTablet()) {
|
// this._renderer.addClass(this._document.body, 'tablet-browser');
|
||||||
this._renderer.addClass(this._document.body, 'tablet-browser');
|
// }
|
||||||
}
|
// if (this._environment.isTablet()) {
|
||||||
if (this._environment.isTablet()) {
|
// this._renderer.addClass(this._document.body, 'tablet');
|
||||||
this._renderer.addClass(this._document.body, 'tablet');
|
// }
|
||||||
}
|
// if (this._environment.isDesktop()) {
|
||||||
if (this._environment.isDesktop()) {
|
// this._renderer.addClass(this._document.body, 'desktop');
|
||||||
this._renderer.addClass(this._document.body, 'desktop');
|
// }
|
||||||
}
|
// }
|
||||||
}
|
|
||||||
|
// sectionChangeHandler(section: string) {
|
||||||
sectionChangeHandler(section: string) {
|
// if (section === 'customer') {
|
||||||
if (section === 'customer') {
|
// this._renderer.removeClass(this._document.body, 'branch');
|
||||||
this._renderer.removeClass(this._document.body, 'branch');
|
// this._renderer.addClass(this._document.body, 'customer');
|
||||||
this._renderer.addClass(this._document.body, 'customer');
|
// } else if (section === 'branch') {
|
||||||
} else if (section === 'branch') {
|
// this._renderer.removeClass(this._document.body, 'customer');
|
||||||
this._renderer.removeClass(this._document.body, 'customer');
|
// this._renderer.addClass(this._document.body, 'branch');
|
||||||
this._renderer.addClass(this._document.body, 'branch');
|
// }
|
||||||
}
|
// }
|
||||||
}
|
|
||||||
|
// updateClient() {
|
||||||
updateClient() {
|
// if (!this._swUpdate.isEnabled) {
|
||||||
if (!this._swUpdate.isEnabled) {
|
// return;
|
||||||
return;
|
// }
|
||||||
}
|
|
||||||
|
// this.initialCheckForUpdate();
|
||||||
this.initialCheckForUpdate();
|
// this.checkForUpdate();
|
||||||
this.checkForUpdate();
|
// }
|
||||||
}
|
|
||||||
|
// checkForUpdate() {
|
||||||
checkForUpdate() {
|
// interval(this._checkForUpdates).subscribe(() => {
|
||||||
interval(this._checkForUpdates).subscribe(() => {
|
// this._swUpdate.checkForUpdate().then((value) => {
|
||||||
this._swUpdate.checkForUpdate().then((value) => {
|
// console.log('check for update', value);
|
||||||
console.log('check for update', value);
|
// if (value) {
|
||||||
if (value) {
|
// this._notifications.updateNotification();
|
||||||
this._notifications.updateNotification();
|
// }
|
||||||
}
|
// });
|
||||||
});
|
// });
|
||||||
});
|
// }
|
||||||
}
|
|
||||||
|
// initialCheckForUpdate() {
|
||||||
initialCheckForUpdate() {
|
// this._swUpdate.checkForUpdate().then((value) => {
|
||||||
this._swUpdate.checkForUpdate().then((value) => {
|
// console.log('initial check for update', value);
|
||||||
console.log('initial check for update', value);
|
// if (value) {
|
||||||
if (value) {
|
// location.reload();
|
||||||
location.reload();
|
// }
|
||||||
}
|
// });
|
||||||
});
|
// }
|
||||||
}
|
|
||||||
|
// @HostListener('window:visibilitychange', ['$event'])
|
||||||
@HostListener('window:visibilitychange', ['$event'])
|
// onVisibilityChange(event: Event) {
|
||||||
onVisibilityChange(event: Event) {
|
// // refresh token when app is in background
|
||||||
// refresh token when app is in background
|
// if (this._document.hidden && this._authService.isAuthenticated()) {
|
||||||
if (this._document.hidden && this._authService.isAuthenticated()) {
|
// this._authService.refresh();
|
||||||
this._authService.refresh();
|
// } else if (!this._authService.isAuthenticated()) {
|
||||||
} else if (!this._authService.isAuthenticated()) {
|
// const strategy = this.injector.get(LoginStrategy);
|
||||||
const strategy = this.injector.get(LoginStrategy);
|
|
||||||
|
// return strategy.login('Sie sind nicht mehr angemeldet');
|
||||||
return strategy.login('Sie sind nicht mehr angemeldet');
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,51 +2,51 @@ import { version } from '../../../../package.json';
|
|||||||
import { IsaTitleStrategy } from '@isa/common/title-management';
|
import { IsaTitleStrategy } from '@isa/common/title-management';
|
||||||
import {
|
import {
|
||||||
HTTP_INTERCEPTORS,
|
HTTP_INTERCEPTORS,
|
||||||
|
HttpInterceptorFn,
|
||||||
provideHttpClient,
|
provideHttpClient,
|
||||||
|
withInterceptors,
|
||||||
withInterceptorsFromDi,
|
withInterceptorsFromDi,
|
||||||
} from '@angular/common/http';
|
} from '@angular/common/http';
|
||||||
import {
|
import {
|
||||||
|
ApplicationConfig,
|
||||||
DEFAULT_CURRENCY_CODE,
|
DEFAULT_CURRENCY_CODE,
|
||||||
ErrorHandler,
|
ErrorHandler,
|
||||||
|
importProvidersFrom,
|
||||||
Injector,
|
Injector,
|
||||||
LOCALE_ID,
|
LOCALE_ID,
|
||||||
NgModule,
|
|
||||||
inject,
|
inject,
|
||||||
provideAppInitializer,
|
provideAppInitializer,
|
||||||
|
provideZoneChangeDetection,
|
||||||
signal,
|
signal,
|
||||||
|
isDevMode,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
import {
|
||||||
import { PlatformModule } from '@angular/cdk/platform';
|
provideRouter,
|
||||||
|
TitleStrategy,
|
||||||
|
withComponentInputBinding,
|
||||||
|
} from '@angular/router';
|
||||||
|
import { ActionReducer, MetaReducer, provideStore } from '@ngrx/store';
|
||||||
|
import { provideStoreDevtools } from '@ngrx/store-devtools';
|
||||||
|
|
||||||
import { Config } from '@core/config';
|
import { Config } from '@core/config';
|
||||||
import { AuthModule, AuthService, LoginStrategy } from '@core/auth';
|
import { AuthModule, AuthService, LoginStrategy } from '@core/auth';
|
||||||
import { CoreCommandModule } from '@core/command';
|
import { CoreCommandModule } from '@core/command';
|
||||||
|
|
||||||
import { AppRoutingModule } from './app-routing.module';
|
import { routes } from './app.routes';
|
||||||
import { AppComponent } from './app.component';
|
|
||||||
import {
|
import { rootReducer } from './store/root.reducer';
|
||||||
ApplicationService,
|
import { RootState } from './store/root.state';
|
||||||
ApplicationServiceAdapter,
|
|
||||||
CoreApplicationModule,
|
|
||||||
} from '@core/application';
|
|
||||||
import { AppStoreModule } from './app-store.module';
|
|
||||||
import { ServiceWorkerModule } from '@angular/service-worker';
|
import { ServiceWorkerModule } from '@angular/service-worker';
|
||||||
import { environment } from '../environments/environment';
|
import { environment } from '../environments/environment';
|
||||||
import { AppSwaggerModule } from './app-swagger.module';
|
|
||||||
import { AppDomainModule } from './app-domain.module';
|
|
||||||
import { UiModalModule } from '@ui/modal';
|
import { UiModalModule } from '@ui/modal';
|
||||||
import {
|
import {
|
||||||
NotificationsHubModule,
|
NotificationsHubModule,
|
||||||
NOTIFICATIONS_HUB_OPTIONS,
|
NOTIFICATIONS_HUB_OPTIONS,
|
||||||
} from '@hub/notifications';
|
} from '@hub/notifications';
|
||||||
import { SignalRHubOptions } from '@core/signalr';
|
import { SignalRHubOptions } from '@core/signalr';
|
||||||
import { CoreBreadcrumbModule } from '@core/breadcrumb';
|
import { provideCoreBreadcrumb } from '@core/breadcrumb';
|
||||||
import { UiCommonModule } from '@ui/common';
|
import { UiCommonModule } from '@ui/common';
|
||||||
import { registerLocaleData } from '@angular/common';
|
|
||||||
|
|
||||||
import localeDe from '@angular/common/locales/de';
|
|
||||||
import localeDeExtra from '@angular/common/locales/extra/de';
|
|
||||||
import { HttpErrorInterceptor } from './interceptors';
|
import { HttpErrorInterceptor } from './interceptors';
|
||||||
import { CoreLoggerModule, LOG_PROVIDER } from '@core/logger';
|
import { CoreLoggerModule, LOG_PROVIDER } from '@core/logger';
|
||||||
import { IsaLogProvider } from './providers';
|
import { IsaLogProvider } from './providers';
|
||||||
@@ -59,7 +59,6 @@ import {
|
|||||||
import * as Commands from './commands';
|
import * as Commands from './commands';
|
||||||
import { NativeContainerService } from '@external/native-container';
|
import { NativeContainerService } from '@external/native-container';
|
||||||
import { ShellModule } from '@shared/shell';
|
import { ShellModule } from '@shared/shell';
|
||||||
import { MainComponent } from './main.component';
|
|
||||||
import { IconModule } from '@shared/components/icon';
|
import { IconModule } from '@shared/components/icon';
|
||||||
import { NgIconsModule } from '@ng-icons/core';
|
import { NgIconsModule } from '@ng-icons/core';
|
||||||
import {
|
import {
|
||||||
@@ -69,8 +68,7 @@ import {
|
|||||||
} from '@ng-icons/material-icons/baseline';
|
} from '@ng-icons/material-icons/baseline';
|
||||||
import { NetworkStatusService } from '@isa/core/connectivity';
|
import { NetworkStatusService } from '@isa/core/connectivity';
|
||||||
import { debounceTime, filter, firstValueFrom, switchMap } from 'rxjs';
|
import { debounceTime, filter, firstValueFrom, switchMap } from 'rxjs';
|
||||||
import { provideMatomo } from 'ngx-matomo-client';
|
import { provideMatomo, withRouter, withRouteData } from 'ngx-matomo-client';
|
||||||
import { withRouter, withRouteData } from 'ngx-matomo-client';
|
|
||||||
import {
|
import {
|
||||||
provideLogging,
|
provideLogging,
|
||||||
withLogLevel,
|
withLogLevel,
|
||||||
@@ -87,15 +85,58 @@ import {
|
|||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
import { OAuthService } from 'angular-oauth2-oidc';
|
import { OAuthService } from 'angular-oauth2-oidc';
|
||||||
import z from 'zod';
|
import z from 'zod';
|
||||||
import { TitleStrategy } from '@angular/router';
|
import { provideScrollPositionRestoration } from '@isa/utils/scroll-position';
|
||||||
import { TabNavigationService } from '@isa/core/tabs';
|
import { TabNavigationService } from '@isa/core/tabs';
|
||||||
|
|
||||||
registerLocaleData(localeDe, localeDeExtra);
|
// Domain modules
|
||||||
registerLocaleData(localeDe, 'de', localeDeExtra);
|
import { provideDomainCheckout } from '@domain/checkout';
|
||||||
|
|
||||||
export function _appInitializerFactory(config: Config, injector: Injector) {
|
// Swagger API configurations
|
||||||
|
import { AvConfiguration } from '@generated/swagger/availability-api';
|
||||||
|
import { CatConfiguration } from '@generated/swagger/cat-search-api';
|
||||||
|
import { CheckoutConfiguration } from '@generated/swagger/checkout-api';
|
||||||
|
import { CrmConfiguration } from '@generated/swagger/crm-api';
|
||||||
|
import { EisConfiguration } from '@generated/swagger/eis-api';
|
||||||
|
import { IsaConfiguration } from '@generated/swagger/isa-api';
|
||||||
|
import { OmsConfiguration } from '@generated/swagger/oms-api';
|
||||||
|
import { PrintConfiguration } from '@generated/swagger/print-api';
|
||||||
|
import { RemiConfiguration } from '@generated/swagger/inventory-api';
|
||||||
|
import { WwsConfiguration } from '@generated/swagger/wws-api';
|
||||||
|
import { UiIconModule } from '@ui/icon';
|
||||||
|
|
||||||
|
// --- Store Configuration ---
|
||||||
|
|
||||||
|
function storeHydrateMetaReducer(
|
||||||
|
reducer: ActionReducer<RootState>,
|
||||||
|
): ActionReducer<RootState> {
|
||||||
|
return function (state, action) {
|
||||||
|
if (action.type === 'HYDRATE') {
|
||||||
|
return reducer(action['payload'], action);
|
||||||
|
}
|
||||||
|
return reducer(state, action);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const metaReducers: MetaReducer<RootState>[] = [storeHydrateMetaReducer];
|
||||||
|
|
||||||
|
// --- Swagger Configuration ---
|
||||||
|
|
||||||
|
const swaggerConfigSchema = z.object({ rootUrl: z.string() });
|
||||||
|
|
||||||
|
function createSwaggerConfigFactory(name: string) {
|
||||||
|
return function () {
|
||||||
|
return inject(Config).get(`@swagger/${name}`, swaggerConfigSchema);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const serviceWorkerBypassInterceptor: HttpInterceptorFn = (req, next) => {
|
||||||
|
return next(req.clone({ setHeaders: { 'ngsw-bypass': 'true' } }));
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- App Initializer ---
|
||||||
|
|
||||||
|
function appInitializerFactory(_config: Config, injector: Injector) {
|
||||||
return async () => {
|
return async () => {
|
||||||
// Get logging service for initialization logging
|
|
||||||
const logger = loggerFactory(() => ({ service: 'AppInitializer' }));
|
const logger = loggerFactory(() => ({ service: 'AppInitializer' }));
|
||||||
const statusElement = document.querySelector('#init-status');
|
const statusElement = document.querySelector('#init-status');
|
||||||
const laoderElement = document.querySelector('#init-loader');
|
const laoderElement = document.querySelector('#init-loader');
|
||||||
@@ -162,7 +203,6 @@ export function _appInitializerFactory(config: Config, injector: Injector) {
|
|||||||
await userStorage.init();
|
await userStorage.init();
|
||||||
|
|
||||||
const store = injector.get(Store);
|
const store = injector.get(Store);
|
||||||
// Hydrate Ngrx Store
|
|
||||||
const state = userStorage.get('store');
|
const state = userStorage.get('store');
|
||||||
if (state && state['version'] === version) {
|
if (state && state['version'] === version) {
|
||||||
store.dispatch({ type: 'HYDRATE', payload: userStorage.get('store') });
|
store.dispatch({ type: 'HYDRATE', payload: userStorage.get('store') });
|
||||||
@@ -172,7 +212,7 @@ export function _appInitializerFactory(config: Config, injector: Injector) {
|
|||||||
reason: state ? 'version mismatch' : 'no stored state',
|
reason: state ? 'version mismatch' : 'no stored state',
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
// Subscribe on Store changes and save to user storage
|
|
||||||
auth.initialized$
|
auth.initialized$
|
||||||
.pipe(
|
.pipe(
|
||||||
filter((initialized) => initialized),
|
filter((initialized) => initialized),
|
||||||
@@ -183,7 +223,6 @@ export function _appInitializerFactory(config: Config, injector: Injector) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
logger.info('Application initialization completed');
|
logger.info('Application initialization completed');
|
||||||
// Inject tab navigation service to initialize it
|
|
||||||
injector.get(TabNavigationService).init();
|
injector.get(TabNavigationService).init();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Application initialization failed', error as Error, () => ({
|
logger.error('Application initialization failed', error as Error, () => ({
|
||||||
@@ -224,7 +263,7 @@ export function _appInitializerFactory(config: Config, injector: Injector) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function _notificationsHubOptionsFactory(
|
function notificationsHubOptionsFactory(
|
||||||
config: Config,
|
config: Config,
|
||||||
auth: AuthService,
|
auth: AuthService,
|
||||||
): SignalRHubOptions {
|
): SignalRHubOptions {
|
||||||
@@ -258,80 +297,151 @@ const USER_SUB_FACTORY = () => {
|
|||||||
return signal(validation.data);
|
return signal(validation.data);
|
||||||
};
|
};
|
||||||
|
|
||||||
@NgModule({
|
export const appConfig: ApplicationConfig = {
|
||||||
declarations: [AppComponent, MainComponent],
|
|
||||||
bootstrap: [AppComponent],
|
|
||||||
imports: [
|
|
||||||
BrowserModule,
|
|
||||||
BrowserAnimationsModule,
|
|
||||||
ShellModule.forRoot(),
|
|
||||||
AppRoutingModule,
|
|
||||||
AppSwaggerModule,
|
|
||||||
AppDomainModule,
|
|
||||||
CoreBreadcrumbModule.forRoot(),
|
|
||||||
CoreCommandModule.forRoot(Object.values(Commands)),
|
|
||||||
CoreLoggerModule.forRoot(),
|
|
||||||
AppStoreModule,
|
|
||||||
AuthModule.forRoot(),
|
|
||||||
CoreApplicationModule.forRoot(),
|
|
||||||
UiModalModule.forRoot(),
|
|
||||||
UiCommonModule.forRoot(),
|
|
||||||
NotificationsHubModule.forRoot(),
|
|
||||||
ServiceWorkerModule.register('ngsw-worker.js', {
|
|
||||||
enabled: environment.production,
|
|
||||||
registrationStrategy: 'registerWhenStable:30000',
|
|
||||||
}),
|
|
||||||
ScanAdapterModule.forRoot(),
|
|
||||||
ScanditScanAdapterModule.forRoot(),
|
|
||||||
PlatformModule,
|
|
||||||
IconModule.forRoot(),
|
|
||||||
NgIconsModule.withIcons({ matWifiOff, matClose, matWifi }),
|
|
||||||
],
|
|
||||||
providers: [
|
providers: [
|
||||||
|
provideZoneChangeDetection({ eventCoalescing: true }),
|
||||||
|
provideAnimationsAsync('animations'),
|
||||||
|
provideRouter(routes, withComponentInputBinding()),
|
||||||
|
provideHttpClient(
|
||||||
|
withInterceptorsFromDi(),
|
||||||
|
withInterceptors([serviceWorkerBypassInterceptor]),
|
||||||
|
),
|
||||||
|
provideScrollPositionRestoration(),
|
||||||
|
|
||||||
|
// NgRx Store
|
||||||
|
provideStore(rootReducer, { metaReducers }),
|
||||||
|
provideCoreBreadcrumb(),
|
||||||
|
provideDomainCheckout(),
|
||||||
|
provideStoreDevtools({
|
||||||
|
name: 'ISA Ngrx Application Store',
|
||||||
|
connectInZone: true,
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Swagger API configurations
|
||||||
|
{
|
||||||
|
provide: AvConfiguration,
|
||||||
|
useFactory: createSwaggerConfigFactory('av'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: CatConfiguration,
|
||||||
|
useFactory: createSwaggerConfigFactory('cat'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: CheckoutConfiguration,
|
||||||
|
useFactory: createSwaggerConfigFactory('checkout'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: CrmConfiguration,
|
||||||
|
useFactory: createSwaggerConfigFactory('crm'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: EisConfiguration,
|
||||||
|
useFactory: createSwaggerConfigFactory('eis'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: IsaConfiguration,
|
||||||
|
useFactory: createSwaggerConfigFactory('isa'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: OmsConfiguration,
|
||||||
|
useFactory: createSwaggerConfigFactory('oms'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: PrintConfiguration,
|
||||||
|
useFactory: createSwaggerConfigFactory('print'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: RemiConfiguration,
|
||||||
|
useFactory: createSwaggerConfigFactory('remi'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: WwsConfiguration,
|
||||||
|
useFactory: createSwaggerConfigFactory('wws'),
|
||||||
|
},
|
||||||
|
|
||||||
|
// App initializer
|
||||||
provideAppInitializer(() => {
|
provideAppInitializer(() => {
|
||||||
const initializerFn = _appInitializerFactory(
|
const initializerFn = appInitializerFactory(
|
||||||
inject(Config),
|
inject(Config),
|
||||||
inject(Injector),
|
inject(Injector),
|
||||||
);
|
);
|
||||||
return initializerFn();
|
return initializerFn();
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
// Notifications hub
|
||||||
{
|
{
|
||||||
provide: NOTIFICATIONS_HUB_OPTIONS,
|
provide: NOTIFICATIONS_HUB_OPTIONS,
|
||||||
useFactory: _notificationsHubOptionsFactory,
|
useFactory: notificationsHubOptionsFactory,
|
||||||
deps: [Config, AuthService],
|
deps: [Config, AuthService],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// HTTP interceptors
|
||||||
{
|
{
|
||||||
provide: HTTP_INTERCEPTORS,
|
provide: HTTP_INTERCEPTORS,
|
||||||
useClass: HttpErrorInterceptor,
|
useClass: HttpErrorInterceptor,
|
||||||
multi: true,
|
multi: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Logging
|
||||||
{
|
{
|
||||||
provide: LOG_PROVIDER,
|
provide: LOG_PROVIDER,
|
||||||
useClass: IsaLogProvider,
|
useClass: IsaLogProvider,
|
||||||
multi: true,
|
multi: true,
|
||||||
},
|
},
|
||||||
|
provideLogging(
|
||||||
|
withLogLevel(isDevMode() ? LogLevel.Debug : LogLevel.Info),
|
||||||
|
withSink(ConsoleLogSink),
|
||||||
|
),
|
||||||
|
|
||||||
|
// Error handling
|
||||||
{
|
{
|
||||||
provide: ErrorHandler,
|
provide: ErrorHandler,
|
||||||
useClass: IsaErrorHandler,
|
useClass: IsaErrorHandler,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
provide: ApplicationService,
|
// Locale settings
|
||||||
useClass: ApplicationServiceAdapter,
|
|
||||||
},
|
|
||||||
{ provide: LOCALE_ID, useValue: 'de-DE' },
|
{ provide: LOCALE_ID, useValue: 'de-DE' },
|
||||||
provideHttpClient(withInterceptorsFromDi()),
|
{ provide: DEFAULT_CURRENCY_CODE, useValue: 'EUR' },
|
||||||
|
|
||||||
|
// Analytics
|
||||||
provideMatomo(
|
provideMatomo(
|
||||||
{ trackerUrl: 'https://matomo.paragon-data.net', siteId: '1' },
|
{ trackerUrl: 'https://matomo.paragon-data.net', siteId: '1' },
|
||||||
withRouter(),
|
withRouter(),
|
||||||
withRouteData(),
|
withRouteData(),
|
||||||
),
|
),
|
||||||
provideLogging(withLogLevel(LogLevel.Debug), withSink(ConsoleLogSink)),
|
|
||||||
{
|
// User storage
|
||||||
provide: DEFAULT_CURRENCY_CODE,
|
|
||||||
useValue: 'EUR',
|
|
||||||
},
|
|
||||||
provideUserSubFactory(USER_SUB_FACTORY),
|
provideUserSubFactory(USER_SUB_FACTORY),
|
||||||
|
|
||||||
|
// Title strategy
|
||||||
{ provide: TitleStrategy, useClass: IsaTitleStrategy },
|
{ provide: TitleStrategy, useClass: IsaTitleStrategy },
|
||||||
|
|
||||||
|
// Import providers from NgModules
|
||||||
|
importProvidersFrom(
|
||||||
|
// Core modules
|
||||||
|
CoreCommandModule.forRoot(Object.values(Commands)),
|
||||||
|
CoreLoggerModule.forRoot(),
|
||||||
|
AuthModule.forRoot(),
|
||||||
|
|
||||||
|
// UI modules
|
||||||
|
UiModalModule.forRoot(),
|
||||||
|
UiCommonModule.forRoot(),
|
||||||
|
|
||||||
|
// Hub modules
|
||||||
|
NotificationsHubModule.forRoot(),
|
||||||
|
|
||||||
|
// Service Worker
|
||||||
|
ServiceWorkerModule.register('ngsw-worker.js', {
|
||||||
|
enabled: environment.production,
|
||||||
|
registrationStrategy: 'registerWhenStable:30000',
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Scan adapter
|
||||||
|
ScanAdapterModule.forRoot(),
|
||||||
|
ScanditScanAdapterModule.forRoot(),
|
||||||
|
|
||||||
|
UiIconModule.forRoot(),
|
||||||
|
IconModule.forRoot(),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
})
|
};
|
||||||
export class AppModule {}
|
|
||||||
0
apps/isa-app/src/app/app.css
Normal file
0
apps/isa-app/src/app/app.css
Normal file
1
apps/isa-app/src/app/app.html
Normal file
1
apps/isa-app/src/app/app.html
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<router-outlet />
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { Routes } from '@angular/router';
|
||||||
import { RouterModule, Routes } from '@angular/router';
|
|
||||||
import {
|
import {
|
||||||
CanActivateCartGuard,
|
CanActivateCartGuard,
|
||||||
CanActivateCartWithProcessIdGuard,
|
CanActivateCartWithProcessIdGuard,
|
||||||
@@ -11,13 +10,12 @@ import {
|
|||||||
CanActivateProductWithProcessIdGuard,
|
CanActivateProductWithProcessIdGuard,
|
||||||
IsAuthenticatedGuard,
|
IsAuthenticatedGuard,
|
||||||
} from './guards';
|
} from './guards';
|
||||||
import { MainComponent } from './main.component';
|
|
||||||
import {
|
import {
|
||||||
BranchSectionResolver,
|
BranchSectionResolver,
|
||||||
CustomerSectionResolver,
|
CustomerSectionResolver,
|
||||||
ProcessIdResolver,
|
ProcessIdResolver,
|
||||||
} from './resolvers';
|
} from './resolvers';
|
||||||
import { TokenLoginComponent, TokenLoginModule } from './token-login';
|
import { TokenLoginComponent } from './token-login';
|
||||||
import {
|
import {
|
||||||
ActivateProcessIdGuard,
|
ActivateProcessIdGuard,
|
||||||
ActivateProcessIdWithConfigKeyGuard,
|
ActivateProcessIdWithConfigKeyGuard,
|
||||||
@@ -28,9 +26,8 @@ import {
|
|||||||
processResolverFn,
|
processResolverFn,
|
||||||
hasTabIdGuard,
|
hasTabIdGuard,
|
||||||
} from '@isa/core/tabs';
|
} from '@isa/core/tabs';
|
||||||
import { provideScrollPositionRestoration } from '@isa/utils/scroll-position';
|
|
||||||
|
|
||||||
const routes: Routes = [
|
export const routes: Routes = [
|
||||||
{ path: '', redirectTo: 'kunde/dashboard', pathMatch: 'full' },
|
{ path: '', redirectTo: 'kunde/dashboard', pathMatch: 'full' },
|
||||||
{
|
{
|
||||||
path: 'login',
|
path: 'login',
|
||||||
@@ -45,7 +42,6 @@ const routes: Routes = [
|
|||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'kunde',
|
path: 'kunde',
|
||||||
component: MainComponent,
|
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'dashboard',
|
path: 'dashboard',
|
||||||
@@ -72,8 +68,6 @@ const routes: Routes = [
|
|||||||
processId: ProcessIdResolver,
|
processId: ProcessIdResolver,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// TODO: Check if order and :processId/order is still being used
|
|
||||||
// If not, remove these routes and the related guards and resolvers
|
|
||||||
{
|
{
|
||||||
path: 'order',
|
path: 'order',
|
||||||
loadChildren: () =>
|
loadChildren: () =>
|
||||||
@@ -122,7 +116,6 @@ const routes: Routes = [
|
|||||||
{
|
{
|
||||||
path: 'pickup-shelf',
|
path: 'pickup-shelf',
|
||||||
canActivate: [ActivateProcessIdGuard],
|
canActivate: [ActivateProcessIdGuard],
|
||||||
// NOTE: This is a workaround for the canActivate guard not being called
|
|
||||||
loadChildren: () =>
|
loadChildren: () =>
|
||||||
import('@page/pickup-shelf').then((m) => m.PickupShelfOutModule),
|
import('@page/pickup-shelf').then((m) => m.PickupShelfOutModule),
|
||||||
},
|
},
|
||||||
@@ -141,7 +134,6 @@ const routes: Routes = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'filiale',
|
path: 'filiale',
|
||||||
component: MainComponent,
|
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'task-calendar',
|
path: 'task-calendar',
|
||||||
@@ -154,7 +146,6 @@ const routes: Routes = [
|
|||||||
{
|
{
|
||||||
path: 'pickup-shelf',
|
path: 'pickup-shelf',
|
||||||
canActivate: [ActivateProcessIdWithConfigKeyGuard('pickupShelf')],
|
canActivate: [ActivateProcessIdWithConfigKeyGuard('pickupShelf')],
|
||||||
// NOTE: This is a workaround for the canActivate guard not being called
|
|
||||||
loadChildren: () =>
|
loadChildren: () =>
|
||||||
import('@page/pickup-shelf').then((m) => m.PickupShelfInModule),
|
import('@page/pickup-shelf').then((m) => m.PickupShelfInModule),
|
||||||
},
|
},
|
||||||
@@ -188,7 +179,6 @@ const routes: Routes = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: ':tabId',
|
path: ':tabId',
|
||||||
component: MainComponent,
|
|
||||||
resolve: { process: processResolverFn, tab: tabResolverFn },
|
resolve: { process: processResolverFn, tab: tabResolverFn },
|
||||||
canActivate: [IsAuthenticatedGuard, hasTabIdGuard],
|
canActivate: [IsAuthenticatedGuard, hasTabIdGuard],
|
||||||
children: [
|
children: [
|
||||||
@@ -218,7 +208,6 @@ const routes: Routes = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
path: 'return',
|
path: 'return',
|
||||||
loadChildren: () =>
|
loadChildren: () =>
|
||||||
@@ -246,16 +235,3 @@ const routes: Routes = [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
imports: [
|
|
||||||
RouterModule.forRoot(routes, {
|
|
||||||
bindToComponentInputs: true,
|
|
||||||
enableTracing: false,
|
|
||||||
}),
|
|
||||||
TokenLoginModule,
|
|
||||||
],
|
|
||||||
exports: [RouterModule],
|
|
||||||
providers: [provideScrollPositionRestoration()],
|
|
||||||
})
|
|
||||||
export class AppRoutingModule {}
|
|
||||||
10
apps/isa-app/src/app/app.ts
Normal file
10
apps/isa-app/src/app/app.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { RouterOutlet } from '@angular/router';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-root',
|
||||||
|
templateUrl: './app.html',
|
||||||
|
styleUrls: ['./app.css'],
|
||||||
|
imports: [RouterOutlet],
|
||||||
|
})
|
||||||
|
export class App {}
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
<shell-root>
|
|
||||||
<router-outlet></router-outlet>
|
|
||||||
</shell-root>
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import { Component, ChangeDetectionStrategy } from '@angular/core';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-main',
|
|
||||||
templateUrl: 'main.component.html',
|
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
||||||
standalone: false,
|
|
||||||
})
|
|
||||||
export class MainComponent {
|
|
||||||
constructor() {}
|
|
||||||
}
|
|
||||||
@@ -1,2 +1 @@
|
|||||||
export * from './token-login.component';
|
export * from './token-login.component';
|
||||||
export * from './token-login.module';
|
|
||||||
|
|||||||
@@ -1,29 +1,31 @@
|
|||||||
import { Component, ChangeDetectionStrategy, OnInit } from '@angular/core';
|
import { Component, ChangeDetectionStrategy, OnInit } from '@angular/core';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { AuthService } from '@core/auth';
|
import { AuthService } from '@core/auth';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-token-login',
|
selector: 'app-token-login',
|
||||||
templateUrl: 'token-login.component.html',
|
templateUrl: 'token-login.component.html',
|
||||||
styleUrls: ['token-login.component.scss'],
|
styleUrls: ['token-login.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
standalone: false,
|
})
|
||||||
})
|
export class TokenLoginComponent implements OnInit {
|
||||||
export class TokenLoginComponent implements OnInit {
|
constructor(
|
||||||
constructor(
|
private _route: ActivatedRoute,
|
||||||
private _route: ActivatedRoute,
|
private _authService: AuthService,
|
||||||
private _authService: AuthService,
|
private _router: Router,
|
||||||
private _router: Router,
|
) {}
|
||||||
) {}
|
|
||||||
|
ngOnInit() {
|
||||||
ngOnInit() {
|
if (
|
||||||
if (this._route.snapshot.params.token && !this._authService.isAuthenticated()) {
|
this._route.snapshot.params.token &&
|
||||||
this._authService.setKeyCardToken(this._route.snapshot.params.token);
|
!this._authService.isAuthenticated()
|
||||||
this._authService.login();
|
) {
|
||||||
} else if (!this._authService.isAuthenticated()) {
|
this._authService.setKeyCardToken(this._route.snapshot.params.token);
|
||||||
this._authService.login();
|
this._authService.login();
|
||||||
} else if (this._authService.isAuthenticated()) {
|
} else if (!this._authService.isAuthenticated()) {
|
||||||
this._router.navigate(['/']);
|
this._authService.login();
|
||||||
}
|
} else if (this._authService.isAuthenticated()) {
|
||||||
}
|
this._router.navigate(['/']);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
import { NgModule } from '@angular/core';
|
|
||||||
import { CommonModule } from '@angular/common';
|
|
||||||
|
|
||||||
import { TokenLoginComponent } from './token-login.component';
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
imports: [CommonModule],
|
|
||||||
exports: [TokenLoginComponent],
|
|
||||||
declarations: [TokenLoginComponent],
|
|
||||||
})
|
|
||||||
export class TokenLoginModule {}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
import { NgModule, ModuleWithProviders } from '@angular/core';
|
|
||||||
import { StoreModule } from '@ngrx/store';
|
|
||||||
import { applicationReducer } from './store';
|
|
||||||
import { ApplicationService } from './application.service';
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
declarations: [],
|
|
||||||
imports: [],
|
|
||||||
exports: [],
|
|
||||||
})
|
|
||||||
export class CoreApplicationModule {
|
|
||||||
static forRoot(): ModuleWithProviders<CoreApplicationModule> {
|
|
||||||
return {
|
|
||||||
ngModule: RootCoreApplicationModule,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
imports: [StoreModule.forFeature('core-application', applicationReducer)],
|
|
||||||
providers: [ApplicationService],
|
|
||||||
})
|
|
||||||
export class RootCoreApplicationModule {}
|
|
||||||
@@ -1,337 +0,0 @@
|
|||||||
import { inject, Injectable } from '@angular/core';
|
|
||||||
import { BehaviorSubject, Observable, of, firstValueFrom } from 'rxjs';
|
|
||||||
import { map, filter, withLatestFrom } from 'rxjs/operators';
|
|
||||||
import { BranchDTO } from '@generated/swagger/checkout-api';
|
|
||||||
import { isBoolean, isNumber } from '@utils/common';
|
|
||||||
import { ApplicationService } from './application.service';
|
|
||||||
import { TabService } from '@isa/core/tabs';
|
|
||||||
import { ApplicationProcess } from './defs/application-process';
|
|
||||||
import { Tab, TabMetadata } from '@isa/core/tabs';
|
|
||||||
import { toObservable } from '@angular/core/rxjs-interop';
|
|
||||||
import { Store } from '@ngrx/store';
|
|
||||||
import { removeProcess } from './store/application.actions';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adapter service that bridges the old ApplicationService interface with the new TabService.
|
|
||||||
*
|
|
||||||
* This adapter allows existing code that depends on ApplicationService to work with the new
|
|
||||||
* TabService without requiring immediate code changes. It maps ApplicationProcess concepts
|
|
||||||
* to Tab entities, storing process-specific data in tab metadata.
|
|
||||||
*
|
|
||||||
* Key mappings:
|
|
||||||
* - ApplicationProcess.id <-> Tab.id
|
|
||||||
* - ApplicationProcess.name <-> Tab.name
|
|
||||||
* - ApplicationProcess metadata (section, type, etc.) <-> Tab.metadata with 'process_' prefix
|
|
||||||
* - ApplicationProcess.data <-> Tab.metadata with 'data_' prefix
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* ```typescript
|
|
||||||
* // Inject the adapter instead of the original service
|
|
||||||
* constructor(private applicationService: ApplicationServiceAdapter) {}
|
|
||||||
*
|
|
||||||
* // Use the same API as before
|
|
||||||
* const process = await this.applicationService.createCustomerProcess();
|
|
||||||
* this.applicationService.activateProcess(process.id);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
@Injectable({ providedIn: 'root' })
|
|
||||||
export class ApplicationServiceAdapter extends ApplicationService {
|
|
||||||
#store = inject(Store);
|
|
||||||
|
|
||||||
#tabService = inject(TabService);
|
|
||||||
|
|
||||||
#activatedProcessId$ = toObservable(this.#tabService.activatedTabId);
|
|
||||||
|
|
||||||
#tabs$ = toObservable(this.#tabService.entities);
|
|
||||||
|
|
||||||
#processes$ = this.#tabs$.pipe(
|
|
||||||
map((tabs) => tabs.map((tab) => this.mapTabToProcess(tab))),
|
|
||||||
);
|
|
||||||
|
|
||||||
#section = new BehaviorSubject<'customer' | 'branch'>('customer');
|
|
||||||
|
|
||||||
readonly REGEX_PROCESS_NAME = /^Vorgang \d+$/;
|
|
||||||
|
|
||||||
get activatedProcessId() {
|
|
||||||
return this.#tabService.activatedTabId();
|
|
||||||
}
|
|
||||||
|
|
||||||
get activatedProcessId$() {
|
|
||||||
return this.#activatedProcessId$;
|
|
||||||
}
|
|
||||||
|
|
||||||
getProcesses$(
|
|
||||||
section?: 'customer' | 'branch',
|
|
||||||
): Observable<ApplicationProcess[]> {
|
|
||||||
return this.#processes$.pipe(
|
|
||||||
map((processes) =>
|
|
||||||
processes.filter((process) =>
|
|
||||||
section ? process.section === section : true,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
getProcessById$(processId: number): Observable<ApplicationProcess> {
|
|
||||||
return this.#processes$.pipe(
|
|
||||||
map((processes) => processes.find((process) => process.id === processId)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
getSection$(): Observable<'customer' | 'branch'> {
|
|
||||||
return this.#section.asObservable();
|
|
||||||
}
|
|
||||||
|
|
||||||
getTitle$(): Observable<'Kundenbereich' | 'Filialbereich'> {
|
|
||||||
return this.getSection$().pipe(
|
|
||||||
map((section) =>
|
|
||||||
section === 'customer' ? 'Kundenbereich' : 'Filialbereich',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @deprecated */
|
|
||||||
getActivatedProcessId$(): Observable<number> {
|
|
||||||
return this.activatedProcessId$;
|
|
||||||
}
|
|
||||||
|
|
||||||
activateProcess(activatedProcessId: number): void {
|
|
||||||
this.#tabService.activateTab(activatedProcessId);
|
|
||||||
}
|
|
||||||
|
|
||||||
removeProcess(processId: number): void {
|
|
||||||
this.#tabService.removeTab(processId);
|
|
||||||
this.#store.dispatch(removeProcess({ processId }));
|
|
||||||
}
|
|
||||||
|
|
||||||
patchProcess(processId: number, changes: Partial<ApplicationProcess>): void {
|
|
||||||
const tabChanges: {
|
|
||||||
name?: string;
|
|
||||||
tags?: string[];
|
|
||||||
metadata?: Record<string, unknown>;
|
|
||||||
} = {};
|
|
||||||
|
|
||||||
if (changes.name) {
|
|
||||||
tabChanges.name = changes.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store other ApplicationProcess properties in metadata
|
|
||||||
const metadataKeys = [
|
|
||||||
'section',
|
|
||||||
'type',
|
|
||||||
'closeable',
|
|
||||||
'confirmClosing',
|
|
||||||
'created',
|
|
||||||
'activated',
|
|
||||||
'data',
|
|
||||||
];
|
|
||||||
metadataKeys.forEach((key) => {
|
|
||||||
if (tabChanges.metadata === undefined) {
|
|
||||||
tabChanges.metadata = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changes[key as keyof ApplicationProcess] !== undefined) {
|
|
||||||
tabChanges.metadata[`process_${key}`] =
|
|
||||||
changes[key as keyof ApplicationProcess];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Apply the changes to the tab
|
|
||||||
this.#tabService.patchTab(processId, tabChanges);
|
|
||||||
}
|
|
||||||
|
|
||||||
patchProcessData(processId: number, data: Record<string, unknown>): void {
|
|
||||||
const currentProcess = this.#tabService.entityMap()[processId];
|
|
||||||
const currentData: TabMetadata =
|
|
||||||
(currentProcess?.metadata?.['process_data'] as TabMetadata) ?? {};
|
|
||||||
|
|
||||||
this.#tabService.patchTab(processId, {
|
|
||||||
metadata: { [`process_data`]: { ...currentData, ...data } },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getSelectedBranch$(): Observable<BranchDTO> {
|
|
||||||
return this.#processes$.pipe(
|
|
||||||
withLatestFrom(this.#activatedProcessId$),
|
|
||||||
map(([processes, activatedProcessId]) =>
|
|
||||||
processes.find((process) => process.id === activatedProcessId),
|
|
||||||
),
|
|
||||||
filter((process): process is ApplicationProcess => !!process),
|
|
||||||
map((process) => process.data?.selectedBranch as BranchDTO),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async createCustomerProcess(processId?: number): Promise<ApplicationProcess> {
|
|
||||||
const processes = await firstValueFrom(this.getProcesses$('customer'));
|
|
||||||
|
|
||||||
const processIds = processes
|
|
||||||
.filter((x) => this.REGEX_PROCESS_NAME.test(x.name))
|
|
||||||
.map((x) => +x.name.split(' ')[1]);
|
|
||||||
|
|
||||||
const maxId = processIds.length > 0 ? Math.max(...processIds) : 0;
|
|
||||||
|
|
||||||
const process: ApplicationProcess = {
|
|
||||||
id: processId ?? Date.now(),
|
|
||||||
type: 'cart',
|
|
||||||
name: `Vorgang ${maxId + 1}`,
|
|
||||||
section: 'customer',
|
|
||||||
closeable: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
await this.createProcess(process);
|
|
||||||
return process;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new ApplicationProcess by first creating a Tab and then storing
|
|
||||||
* process-specific properties in the tab's metadata.
|
|
||||||
*
|
|
||||||
* @param process - The ApplicationProcess to create
|
|
||||||
* @throws {Error} If process ID already exists or is invalid
|
|
||||||
*/
|
|
||||||
async createProcess(process: ApplicationProcess): Promise<void> {
|
|
||||||
const existingProcess = this.#tabService.entityMap()[process.id];
|
|
||||||
if (existingProcess?.id === process?.id) {
|
|
||||||
throw new Error('Process Id existiert bereits');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isNumber(process.id)) {
|
|
||||||
throw new Error('Process Id nicht gesetzt');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isBoolean(process.closeable)) {
|
|
||||||
process.closeable = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isBoolean(process.confirmClosing)) {
|
|
||||||
process.confirmClosing = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
process.created = this.createTimestamp();
|
|
||||||
process.activated = 0;
|
|
||||||
|
|
||||||
// Create tab with process data and preserve the process ID
|
|
||||||
this.#tabService.addTab({
|
|
||||||
id: process.id,
|
|
||||||
name: process.name,
|
|
||||||
tags: [process.section, process.type].filter(Boolean),
|
|
||||||
metadata: {
|
|
||||||
process_section: process.section,
|
|
||||||
process_type: process.type,
|
|
||||||
process_closeable: process.closeable,
|
|
||||||
process_confirmClosing: process.confirmClosing,
|
|
||||||
process_created: process.created,
|
|
||||||
process_activated: process.activated,
|
|
||||||
process_data: process.data,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
setSection(section: 'customer' | 'branch'): void {
|
|
||||||
this.#section.next(section);
|
|
||||||
}
|
|
||||||
|
|
||||||
getLastActivatedProcessWithSectionAndType$(
|
|
||||||
section: 'customer' | 'branch',
|
|
||||||
type: string,
|
|
||||||
): Observable<ApplicationProcess> {
|
|
||||||
return this.getProcesses$(section).pipe(
|
|
||||||
map((processes) =>
|
|
||||||
processes
|
|
||||||
?.filter((process) => process.type === type)
|
|
||||||
?.reduce((latest, current) => {
|
|
||||||
if (!latest) {
|
|
||||||
return current;
|
|
||||||
}
|
|
||||||
return latest?.activated > current?.activated ? latest : current;
|
|
||||||
}, undefined),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
getLastActivatedProcessWithSection$(
|
|
||||||
section: 'customer' | 'branch',
|
|
||||||
): Observable<ApplicationProcess> {
|
|
||||||
return this.getProcesses$(section).pipe(
|
|
||||||
map((processes) =>
|
|
||||||
processes?.reduce((latest, current) => {
|
|
||||||
if (!latest) {
|
|
||||||
return current;
|
|
||||||
}
|
|
||||||
return latest?.activated > current?.activated ? latest : current;
|
|
||||||
}, undefined),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maps Tab entities to ApplicationProcess objects by extracting process-specific
|
|
||||||
* metadata and combining it with tab properties.
|
|
||||||
*
|
|
||||||
* @param tab - The tab entity to convert
|
|
||||||
* @returns The corresponding ApplicationProcess object
|
|
||||||
*/
|
|
||||||
private mapTabToProcess(tab: Tab): ApplicationProcess {
|
|
||||||
return {
|
|
||||||
id: tab.id,
|
|
||||||
name: tab.name,
|
|
||||||
created:
|
|
||||||
this.getMetadataValue<number>(tab.metadata, 'process_created') ??
|
|
||||||
tab.createdAt,
|
|
||||||
activated:
|
|
||||||
this.getMetadataValue<number>(tab.metadata, 'process_activated') ??
|
|
||||||
tab.activatedAt ??
|
|
||||||
0,
|
|
||||||
section:
|
|
||||||
this.getMetadataValue<'customer' | 'branch'>(
|
|
||||||
tab.metadata,
|
|
||||||
'process_section',
|
|
||||||
) ?? 'customer',
|
|
||||||
type: this.getMetadataValue<string>(tab.metadata, 'process_type'),
|
|
||||||
closeable:
|
|
||||||
this.getMetadataValue<boolean>(tab.metadata, 'process_closeable') ??
|
|
||||||
true,
|
|
||||||
confirmClosing:
|
|
||||||
this.getMetadataValue<boolean>(
|
|
||||||
tab.metadata,
|
|
||||||
'process_confirmClosing',
|
|
||||||
) ?? true,
|
|
||||||
data: this.extractDataFromMetadata(tab.metadata),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extracts ApplicationProcess data properties from tab metadata.
|
|
||||||
* Data properties are stored with a 'data_' prefix in tab metadata.
|
|
||||||
*
|
|
||||||
* @param metadata - The tab metadata object
|
|
||||||
* @returns The extracted data object or undefined if no data properties exist
|
|
||||||
*/
|
|
||||||
private extractDataFromMetadata(
|
|
||||||
metadata: TabMetadata,
|
|
||||||
): Record<string, unknown> | undefined {
|
|
||||||
// Return the complete data object stored under 'process_data'
|
|
||||||
const processData = metadata?.['process_data'];
|
|
||||||
|
|
||||||
if (
|
|
||||||
processData &&
|
|
||||||
typeof processData === 'object' &&
|
|
||||||
processData !== null
|
|
||||||
) {
|
|
||||||
return processData as Record<string, unknown>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getMetadataValue<T>(
|
|
||||||
metadata: TabMetadata,
|
|
||||||
key: string,
|
|
||||||
): T | undefined {
|
|
||||||
return metadata?.[key] as T | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
private createTimestamp(): number {
|
|
||||||
return Date.now();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,233 +0,0 @@
|
|||||||
// import { createServiceFactory, SpectatorService, SpyObject } from '@ngneat/spectator';
|
|
||||||
// import { Store } from '@ngrx/store';
|
|
||||||
// import { Observable, of } from 'rxjs';
|
|
||||||
// import { first } from 'rxjs/operators';
|
|
||||||
// import { ApplicationProcess } from './defs';
|
|
||||||
|
|
||||||
// import { ApplicationService } from './application.service';
|
|
||||||
// import * as actions from './store/application.actions';
|
|
||||||
|
|
||||||
// describe('ApplicationService', () => {
|
|
||||||
// let spectator: SpectatorService<ApplicationService>;
|
|
||||||
// let store: SpyObject<Store>;
|
|
||||||
// const createService = createServiceFactory({
|
|
||||||
// service: ApplicationService,
|
|
||||||
// mocks: [Store],
|
|
||||||
// });
|
|
||||||
|
|
||||||
// beforeEach(() => {
|
|
||||||
// spectator = createService({});
|
|
||||||
// store = spectator.inject(Store);
|
|
||||||
// });
|
|
||||||
|
|
||||||
// it('should be created', () => {
|
|
||||||
// expect(spectator.service).toBeTruthy();
|
|
||||||
// });
|
|
||||||
|
|
||||||
// describe('activatedProcessId$', () => {
|
|
||||||
// it('should return an observable', () => {
|
|
||||||
// expect(spectator.service.activatedProcessId$).toBeInstanceOf(Observable);
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
|
||||||
// describe('activatedProcessId', () => {
|
|
||||||
// it('should return the process id as a number', () => {
|
|
||||||
// spyOnProperty(spectator.service['activatedProcessIdSubject'] as any, 'value').and.returnValue(2);
|
|
||||||
// expect(spectator.service.activatedProcessId).toBe(2);
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
|
||||||
// describe('getProcesses$()', () => {
|
|
||||||
// it('should call select on store and return all selected processes', async () => {
|
|
||||||
// const processes: ApplicationProcess[] = [
|
|
||||||
// { id: 1, name: 'Vorgang', type: 'cart', section: 'customer', data: { count: 1 } },
|
|
||||||
// { id: 2, name: 'Vorgang', type: 'task-calendar', section: 'branch' },
|
|
||||||
// ];
|
|
||||||
// store.select.and.returnValue(of(processes));
|
|
||||||
// const result = await spectator.service.getProcesses$().pipe(first()).toPromise();
|
|
||||||
// expect(result).toEqual(processes);
|
|
||||||
// expect(store.select).toHaveBeenCalled();
|
|
||||||
// });
|
|
||||||
|
|
||||||
// it('should call select on store and return all section customer processes', async () => {
|
|
||||||
// const processes: ApplicationProcess[] = [
|
|
||||||
// { id: 1, name: 'Vorgang', type: 'cart', section: 'customer', data: { count: 1 } },
|
|
||||||
// { id: 2, name: 'Vorgang', type: 'task-calendar', section: 'branch' },
|
|
||||||
// ];
|
|
||||||
// store.select.and.returnValue(of(processes));
|
|
||||||
// const result = await spectator.service.getProcesses$('customer').pipe(first()).toPromise();
|
|
||||||
// expect(result).toEqual([processes[0]]);
|
|
||||||
// expect(store.select).toHaveBeenCalled();
|
|
||||||
// });
|
|
||||||
|
|
||||||
// it('should call select on store and return all section branch processes', async () => {
|
|
||||||
// const processes: ApplicationProcess[] = [
|
|
||||||
// { id: 1, name: 'Vorgang', type: 'cart', section: 'customer', data: { count: 1 } },
|
|
||||||
// { id: 2, name: 'Vorgang', type: 'task-calendar', section: 'branch' },
|
|
||||||
// ];
|
|
||||||
// store.select.and.returnValue(of(processes));
|
|
||||||
// const result = await spectator.service.getProcesses$('branch').pipe(first()).toPromise();
|
|
||||||
// expect(result).toEqual([processes[1]]);
|
|
||||||
// expect(store.select).toHaveBeenCalled();
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
|
||||||
// describe('getProcessById$()', () => {
|
|
||||||
// it('should return the process by id', async () => {
|
|
||||||
// const processes: ApplicationProcess[] = [
|
|
||||||
// { id: 1, name: 'Vorgang 1', section: 'customer' },
|
|
||||||
// { id: 2, name: 'Vorgang 2', section: 'customer' },
|
|
||||||
// ];
|
|
||||||
// spyOn(spectator.service, 'getProcesses$').and.returnValue(of(processes));
|
|
||||||
|
|
||||||
// const process = await spectator.service.getProcessById$(1).toPromise();
|
|
||||||
// expect(process.id).toBe(1);
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
|
||||||
// describe('getSection$()', () => {
|
|
||||||
// it('should return the selected section branch', async () => {
|
|
||||||
// const section = 'branch';
|
|
||||||
// store.select.and.returnValue(of(section));
|
|
||||||
// const result = await spectator.service.getSection$().pipe(first()).toPromise();
|
|
||||||
// expect(result).toEqual(section);
|
|
||||||
// expect(store.select).toHaveBeenCalled();
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
|
||||||
// describe('getActivatedProcessId$', () => {
|
|
||||||
// it('should return the current selected activated process id', async () => {
|
|
||||||
// const activatedProcessId = 2;
|
|
||||||
// store.select.and.returnValue(of({ id: activatedProcessId }));
|
|
||||||
// const result = await spectator.service.getActivatedProcessId$().pipe(first()).toPromise();
|
|
||||||
// expect(result).toEqual(activatedProcessId);
|
|
||||||
// expect(store.select).toHaveBeenCalled();
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
|
||||||
// describe('activateProcess()', () => {
|
|
||||||
// it('should dispatch action setActivatedProcess with argument activatedProcessId and action type', () => {
|
|
||||||
// const activatedProcessId = 2;
|
|
||||||
// spectator.service.activateProcess(activatedProcessId);
|
|
||||||
// expect(store.dispatch).toHaveBeenCalledWith({ activatedProcessId, type: actions.setActivatedProcess.type });
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
|
||||||
// describe('removeProcess()', () => {
|
|
||||||
// it('should dispatch action removeProcess with argument processId and action type', () => {
|
|
||||||
// const processId = 2;
|
|
||||||
// spectator.service.removeProcess(processId);
|
|
||||||
// expect(store.dispatch).toHaveBeenCalledWith({ processId, type: actions.removeProcess.type });
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
|
||||||
// describe('createProcess()', () => {
|
|
||||||
// it('should dispatch action addProcess with process', async () => {
|
|
||||||
// const process: ApplicationProcess = {
|
|
||||||
// id: 1,
|
|
||||||
// name: 'Vorgang 1',
|
|
||||||
// section: 'customer',
|
|
||||||
// type: 'cart',
|
|
||||||
// };
|
|
||||||
|
|
||||||
// const timestamp = 100;
|
|
||||||
// spyOn(spectator.service as any, '_createTimestamp').and.returnValue(timestamp);
|
|
||||||
// spyOn(spectator.service, 'getProcessById$').and.returnValue(of(undefined));
|
|
||||||
// await spectator.service.createProcess(process);
|
|
||||||
|
|
||||||
// expect(store.dispatch).toHaveBeenCalledWith({
|
|
||||||
// type: actions.addProcess.type,
|
|
||||||
// process: {
|
|
||||||
// ...process,
|
|
||||||
// activated: 0,
|
|
||||||
// created: timestamp,
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
|
||||||
// it('should throw an error if the process id is already existing', async () => {
|
|
||||||
// const process: ApplicationProcess = {
|
|
||||||
// id: 1,
|
|
||||||
// name: 'Vorgang 1',
|
|
||||||
// section: 'customer',
|
|
||||||
// type: 'cart',
|
|
||||||
// };
|
|
||||||
// spyOn(spectator.service, 'getProcessById$').and.returnValue(of(process));
|
|
||||||
// await expectAsync(spectator.service.createProcess(process)).toBeRejectedWithError('Process Id existiert bereits');
|
|
||||||
// });
|
|
||||||
|
|
||||||
// it('should throw an error if the process id is not a number', async () => {
|
|
||||||
// const process: ApplicationProcess = {
|
|
||||||
// id: undefined,
|
|
||||||
// name: 'Vorgang 1',
|
|
||||||
// section: 'customer',
|
|
||||||
// type: 'cart',
|
|
||||||
// };
|
|
||||||
// spyOn(spectator.service, 'getProcessById$').and.returnValue(of({ id: 5, name: 'Vorgang 2', section: 'customer' }));
|
|
||||||
// await expectAsync(spectator.service.createProcess(process)).toBeRejectedWithError('Process Id nicht gesetzt');
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
|
||||||
// describe('patchProcess', () => {
|
|
||||||
// it('should dispatch action patchProcess with changes', async () => {
|
|
||||||
// const process: ApplicationProcess = {
|
|
||||||
// id: 1,
|
|
||||||
// name: 'Vorgang 1',
|
|
||||||
// section: 'customer',
|
|
||||||
// type: 'cart',
|
|
||||||
// };
|
|
||||||
|
|
||||||
// await spectator.service.patchProcess(process.id, process);
|
|
||||||
|
|
||||||
// expect(store.dispatch).toHaveBeenCalledWith({
|
|
||||||
// type: actions.patchProcess.type,
|
|
||||||
// processId: process.id,
|
|
||||||
// changes: {
|
|
||||||
// ...process,
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
|
||||||
// describe('setSection()', () => {
|
|
||||||
// it('should dispatch action setSection with argument section and action type', () => {
|
|
||||||
// const section = 'customer';
|
|
||||||
// spectator.service.setSection(section);
|
|
||||||
// expect(store.dispatch).toHaveBeenCalledWith({ section, type: actions.setSection.type });
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
|
||||||
// describe('getLastActivatedProcessWithSectionAndType()', () => {
|
|
||||||
// it('should return the last activated process by section and type', async () => {
|
|
||||||
// const processes: ApplicationProcess[] = [
|
|
||||||
// { id: 1, name: 'Vorgang 1', section: 'customer', type: 'cart', activated: 100 },
|
|
||||||
// { id: 2, name: 'Vorgang 2', section: 'customer', type: 'cart', activated: 200 },
|
|
||||||
// { id: 3, name: 'Vorgang 3', section: 'customer', type: 'goodsOut', activated: 300 },
|
|
||||||
// ];
|
|
||||||
// spyOn(spectator.service, 'getProcesses$').and.returnValue(of(processes));
|
|
||||||
|
|
||||||
// expect(await spectator.service.getLastActivatedProcessWithSectionAndType$('customer', 'cart').pipe(first()).toPromise()).toBe(
|
|
||||||
// processes[1]
|
|
||||||
// );
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
|
||||||
// describe('getLastActivatedProcessWithSection()', () => {
|
|
||||||
// it('should return the last activated process by section', async () => {
|
|
||||||
// const processes: ApplicationProcess[] = [
|
|
||||||
// { id: 1, name: 'Vorgang 1', section: 'customer', activated: 100 },
|
|
||||||
// { id: 2, name: 'Vorgang 2', section: 'customer', activated: 200 },
|
|
||||||
// { id: 3, name: 'Vorgang 3', section: 'customer', activated: 300 },
|
|
||||||
// ];
|
|
||||||
// spyOn(spectator.service, 'getProcesses$').and.returnValue(of(processes));
|
|
||||||
|
|
||||||
// expect(await spectator.service.getLastActivatedProcessWithSection$('customer').pipe(first()).toPromise()).toBe(processes[2]);
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
|
||||||
// describe('_createTimestamp', () => {
|
|
||||||
// it('should return the current timestamp in ms', () => {
|
|
||||||
// expect(spectator.service['_createTimestamp']()).toBeCloseTo(Date.now());
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
@@ -1,41 +1,68 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { inject, Injectable } from '@angular/core';
|
||||||
import { Store } from '@ngrx/store';
|
import { BehaviorSubject, Observable, of, firstValueFrom } from 'rxjs';
|
||||||
|
import { map, filter, withLatestFrom } from 'rxjs/operators';
|
||||||
import { BranchDTO } from '@generated/swagger/checkout-api';
|
import { BranchDTO } from '@generated/swagger/checkout-api';
|
||||||
import { isBoolean, isNumber } from '@utils/common';
|
import { isBoolean, isNumber } from '@utils/common';
|
||||||
import { BehaviorSubject, Observable } from 'rxjs';
|
import { TabService } from '@isa/core/tabs';
|
||||||
import { first, map, switchMap } from 'rxjs/operators';
|
import { ApplicationProcess } from './defs/application-process';
|
||||||
import { ApplicationProcess } from './defs';
|
import { Tab, TabMetadata } from '@isa/core/tabs';
|
||||||
import {
|
import { toObservable } from '@angular/core/rxjs-interop';
|
||||||
removeProcess,
|
import { Store } from '@ngrx/store';
|
||||||
selectSection,
|
import { removeProcess } from './store/application.actions';
|
||||||
selectProcesses,
|
|
||||||
setSection,
|
|
||||||
addProcess,
|
|
||||||
setActivatedProcess,
|
|
||||||
selectActivatedProcess,
|
|
||||||
patchProcess,
|
|
||||||
patchProcessData,
|
|
||||||
selectTitle,
|
|
||||||
setTitle,
|
|
||||||
} from './store';
|
|
||||||
|
|
||||||
@Injectable()
|
/**
|
||||||
|
* Adapter service that bridges the old ApplicationService interface with the new TabService.
|
||||||
|
*
|
||||||
|
* This adapter allows existing code that depends on ApplicationService to work with the new
|
||||||
|
* TabService without requiring immediate code changes. It maps ApplicationProcess concepts
|
||||||
|
* to Tab entities, storing process-specific data in tab metadata.
|
||||||
|
*
|
||||||
|
* Key mappings:
|
||||||
|
* - ApplicationProcess.id <-> Tab.id
|
||||||
|
* - ApplicationProcess.name <-> Tab.name
|
||||||
|
* - ApplicationProcess metadata (section, type, etc.) <-> Tab.metadata with 'process_' prefix
|
||||||
|
* - ApplicationProcess.data <-> Tab.metadata with 'data_' prefix
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* // Inject the adapter instead of the original service
|
||||||
|
* constructor(private applicationService: ApplicationServiceAdapter) {}
|
||||||
|
*
|
||||||
|
* // Use the same API as before
|
||||||
|
* const process = await this.applicationService.createCustomerProcess();
|
||||||
|
* this.applicationService.activateProcess(process.id);
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
export class ApplicationService {
|
export class ApplicationService {
|
||||||
private activatedProcessIdSubject = new BehaviorSubject<number>(undefined);
|
#store = inject(Store);
|
||||||
|
|
||||||
|
#tabService = inject(TabService);
|
||||||
|
|
||||||
|
#activatedProcessId$ = toObservable(this.#tabService.activatedTabId);
|
||||||
|
|
||||||
|
#tabs$ = toObservable(this.#tabService.entities);
|
||||||
|
|
||||||
|
#processes$ = this.#tabs$.pipe(
|
||||||
|
map((tabs) => tabs.map((tab) => this.mapTabToProcess(tab))),
|
||||||
|
);
|
||||||
|
|
||||||
|
#section = new BehaviorSubject<'customer' | 'branch'>('customer');
|
||||||
|
|
||||||
|
readonly REGEX_PROCESS_NAME = /^Vorgang \d+$/;
|
||||||
|
|
||||||
get activatedProcessId() {
|
get activatedProcessId() {
|
||||||
return this.activatedProcessIdSubject.value;
|
return this.#tabService.activatedTabId();
|
||||||
}
|
}
|
||||||
|
|
||||||
get activatedProcessId$() {
|
get activatedProcessId$() {
|
||||||
return this.activatedProcessIdSubject.asObservable();
|
return this.#activatedProcessId$;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(private store: Store) {}
|
getProcesses$(
|
||||||
|
section?: 'customer' | 'branch',
|
||||||
getProcesses$(section?: 'customer' | 'branch') {
|
): Observable<ApplicationProcess[]> {
|
||||||
const processes$ = this.store.select(selectProcesses);
|
return this.#processes$.pipe(
|
||||||
return processes$.pipe(
|
|
||||||
map((processes) =>
|
map((processes) =>
|
||||||
processes.filter((process) =>
|
processes.filter((process) =>
|
||||||
section ? process.section === section : true,
|
section ? process.section === section : true,
|
||||||
@@ -45,69 +72,96 @@ export class ApplicationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getProcessById$(processId: number): Observable<ApplicationProcess> {
|
getProcessById$(processId: number): Observable<ApplicationProcess> {
|
||||||
return this.getProcesses$().pipe(
|
return this.#processes$.pipe(
|
||||||
map((processes) => processes.find((process) => process.id === processId)),
|
map((processes) => processes.find((process) => process.id === processId)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getSection$() {
|
getSection$(): Observable<'customer' | 'branch'> {
|
||||||
return this.store.select(selectSection);
|
return this.#section.asObservable();
|
||||||
}
|
}
|
||||||
|
|
||||||
getTitle$() {
|
getTitle$(): Observable<'Kundenbereich' | 'Filialbereich'> {
|
||||||
return this.getSection$().pipe(
|
return this.getSection$().pipe(
|
||||||
map((section) => {
|
map((section) =>
|
||||||
return section === 'customer' ? 'Kundenbereich' : 'Filialbereich';
|
section === 'customer' ? 'Kundenbereich' : 'Filialbereich',
|
||||||
}),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @deprecated */
|
/** @deprecated */
|
||||||
getActivatedProcessId$() {
|
getActivatedProcessId$(): Observable<number> {
|
||||||
return this.store
|
return this.activatedProcessId$;
|
||||||
.select(selectActivatedProcess)
|
|
||||||
.pipe(map((process) => process?.id));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
activateProcess(activatedProcessId: number) {
|
activateProcess(activatedProcessId: number): void {
|
||||||
this.store.dispatch(setActivatedProcess({ activatedProcessId }));
|
this.#tabService.activateTab(activatedProcessId);
|
||||||
this.activatedProcessIdSubject.next(activatedProcessId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
removeProcess(processId: number) {
|
removeProcess(processId: number): void {
|
||||||
this.store.dispatch(removeProcess({ processId }));
|
this.#tabService.removeTab(processId);
|
||||||
|
this.#store.dispatch(removeProcess({ processId }));
|
||||||
}
|
}
|
||||||
|
|
||||||
patchProcess(processId: number, changes: Partial<ApplicationProcess>) {
|
patchProcess(processId: number, changes: Partial<ApplicationProcess>): void {
|
||||||
this.store.dispatch(patchProcess({ processId, changes }));
|
const tabChanges: {
|
||||||
}
|
name?: string;
|
||||||
|
tags?: string[];
|
||||||
|
metadata?: Record<string, unknown>;
|
||||||
|
} = {};
|
||||||
|
|
||||||
patchProcessData(processId: number, data: Record<string, any>) {
|
if (changes.name) {
|
||||||
this.store.dispatch(patchProcessData({ processId, data }));
|
tabChanges.name = changes.name;
|
||||||
}
|
|
||||||
|
|
||||||
getSelectedBranch$(processId?: number): Observable<BranchDTO> {
|
|
||||||
if (!processId) {
|
|
||||||
return this.activatedProcessId$.pipe(
|
|
||||||
switchMap((processId) =>
|
|
||||||
this.getProcessById$(processId).pipe(
|
|
||||||
map((process) => process?.data?.selectedBranch),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.getProcessById$(processId).pipe(
|
// Store other ApplicationProcess properties in metadata
|
||||||
map((process) => process?.data?.selectedBranch),
|
const metadataKeys = [
|
||||||
|
'section',
|
||||||
|
'type',
|
||||||
|
'closeable',
|
||||||
|
'confirmClosing',
|
||||||
|
'created',
|
||||||
|
'activated',
|
||||||
|
'data',
|
||||||
|
];
|
||||||
|
metadataKeys.forEach((key) => {
|
||||||
|
if (tabChanges.metadata === undefined) {
|
||||||
|
tabChanges.metadata = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changes[key as keyof ApplicationProcess] !== undefined) {
|
||||||
|
tabChanges.metadata[`process_${key}`] =
|
||||||
|
changes[key as keyof ApplicationProcess];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Apply the changes to the tab
|
||||||
|
this.#tabService.patchTab(processId, tabChanges);
|
||||||
|
}
|
||||||
|
|
||||||
|
patchProcessData(processId: number, data: Record<string, unknown>): void {
|
||||||
|
const currentProcess = this.#tabService.entityMap()[processId];
|
||||||
|
const currentData: TabMetadata =
|
||||||
|
(currentProcess?.metadata?.['process_data'] as TabMetadata) ?? {};
|
||||||
|
|
||||||
|
this.#tabService.patchTab(processId, {
|
||||||
|
metadata: { [`process_data`]: { ...currentData, ...data } },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getSelectedBranch$(): Observable<BranchDTO> {
|
||||||
|
return this.#processes$.pipe(
|
||||||
|
withLatestFrom(this.#activatedProcessId$),
|
||||||
|
map(([processes, activatedProcessId]) =>
|
||||||
|
processes.find((process) => process.id === activatedProcessId),
|
||||||
|
),
|
||||||
|
filter((process): process is ApplicationProcess => !!process),
|
||||||
|
map((process) => process.data?.selectedBranch as BranchDTO),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly REGEX_PROCESS_NAME = /^Vorgang \d+$/;
|
|
||||||
|
|
||||||
async createCustomerProcess(processId?: number): Promise<ApplicationProcess> {
|
async createCustomerProcess(processId?: number): Promise<ApplicationProcess> {
|
||||||
const processes = await this.getProcesses$('customer')
|
const processes = await firstValueFrom(this.getProcesses$('customer'));
|
||||||
.pipe(first())
|
|
||||||
.toPromise();
|
|
||||||
|
|
||||||
const processIds = processes
|
const processIds = processes
|
||||||
.filter((x) => this.REGEX_PROCESS_NAME.test(x.name))
|
.filter((x) => this.REGEX_PROCESS_NAME.test(x.name))
|
||||||
@@ -124,14 +178,18 @@ export class ApplicationService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
await this.createProcess(process);
|
await this.createProcess(process);
|
||||||
|
|
||||||
return process;
|
return process;
|
||||||
}
|
}
|
||||||
|
|
||||||
async createProcess(process: ApplicationProcess) {
|
/**
|
||||||
const existingProcess = await this.getProcessById$(process?.id)
|
* Creates a new ApplicationProcess by first creating a Tab and then storing
|
||||||
.pipe(first())
|
* process-specific properties in the tab's metadata.
|
||||||
.toPromise();
|
*
|
||||||
|
* @param process - The ApplicationProcess to create
|
||||||
|
* @throws {Error} If process ID already exists or is invalid
|
||||||
|
*/
|
||||||
|
async createProcess(process: ApplicationProcess): Promise<void> {
|
||||||
|
const existingProcess = this.#tabService.entityMap()[process.id];
|
||||||
if (existingProcess?.id === process?.id) {
|
if (existingProcess?.id === process?.id) {
|
||||||
throw new Error('Process Id existiert bereits');
|
throw new Error('Process Id existiert bereits');
|
||||||
}
|
}
|
||||||
@@ -148,13 +206,28 @@ export class ApplicationService {
|
|||||||
process.confirmClosing = true;
|
process.confirmClosing = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
process.created = this._createTimestamp();
|
process.created = this.createTimestamp();
|
||||||
process.activated = 0;
|
process.activated = 0;
|
||||||
this.store.dispatch(addProcess({ process }));
|
|
||||||
|
// Create tab with process data and preserve the process ID
|
||||||
|
this.#tabService.addTab({
|
||||||
|
id: process.id,
|
||||||
|
name: process.name,
|
||||||
|
tags: [process.section, process.type].filter(Boolean),
|
||||||
|
metadata: {
|
||||||
|
process_section: process.section,
|
||||||
|
process_type: process.type,
|
||||||
|
process_closeable: process.closeable,
|
||||||
|
process_confirmClosing: process.confirmClosing,
|
||||||
|
process_created: process.created,
|
||||||
|
process_activated: process.activated,
|
||||||
|
process_data: process.data,
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
setSection(section: 'customer' | 'branch') {
|
setSection(section: 'customer' | 'branch'): void {
|
||||||
this.store.dispatch(setSection({ section }));
|
this.#section.next(section);
|
||||||
}
|
}
|
||||||
|
|
||||||
getLastActivatedProcessWithSectionAndType$(
|
getLastActivatedProcessWithSectionAndType$(
|
||||||
@@ -190,7 +263,74 @@ export class ApplicationService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createTimestamp() {
|
/**
|
||||||
|
* Maps Tab entities to ApplicationProcess objects by extracting process-specific
|
||||||
|
* metadata and combining it with tab properties.
|
||||||
|
*
|
||||||
|
* @param tab - The tab entity to convert
|
||||||
|
* @returns The corresponding ApplicationProcess object
|
||||||
|
*/
|
||||||
|
private mapTabToProcess(tab: Tab): ApplicationProcess {
|
||||||
|
return {
|
||||||
|
id: tab.id,
|
||||||
|
name: tab.name,
|
||||||
|
created:
|
||||||
|
this.getMetadataValue<number>(tab.metadata, 'process_created') ??
|
||||||
|
tab.createdAt,
|
||||||
|
activated:
|
||||||
|
this.getMetadataValue<number>(tab.metadata, 'process_activated') ??
|
||||||
|
tab.activatedAt ??
|
||||||
|
0,
|
||||||
|
section:
|
||||||
|
this.getMetadataValue<'customer' | 'branch'>(
|
||||||
|
tab.metadata,
|
||||||
|
'process_section',
|
||||||
|
) ?? 'customer',
|
||||||
|
type: this.getMetadataValue<string>(tab.metadata, 'process_type'),
|
||||||
|
closeable:
|
||||||
|
this.getMetadataValue<boolean>(tab.metadata, 'process_closeable') ??
|
||||||
|
true,
|
||||||
|
confirmClosing:
|
||||||
|
this.getMetadataValue<boolean>(
|
||||||
|
tab.metadata,
|
||||||
|
'process_confirmClosing',
|
||||||
|
) ?? true,
|
||||||
|
data: this.extractDataFromMetadata(tab.metadata),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts ApplicationProcess data properties from tab metadata.
|
||||||
|
* Data properties are stored with a 'data_' prefix in tab metadata.
|
||||||
|
*
|
||||||
|
* @param metadata - The tab metadata object
|
||||||
|
* @returns The extracted data object or undefined if no data properties exist
|
||||||
|
*/
|
||||||
|
private extractDataFromMetadata(
|
||||||
|
metadata: TabMetadata,
|
||||||
|
): Record<string, unknown> | undefined {
|
||||||
|
// Return the complete data object stored under 'process_data'
|
||||||
|
const processData = metadata?.['process_data'];
|
||||||
|
|
||||||
|
if (
|
||||||
|
processData &&
|
||||||
|
typeof processData === 'object' &&
|
||||||
|
processData !== null
|
||||||
|
) {
|
||||||
|
return processData as Record<string, unknown>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getMetadataValue<T>(
|
||||||
|
metadata: TabMetadata,
|
||||||
|
key: string,
|
||||||
|
): T | undefined {
|
||||||
|
return metadata?.[key] as T | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
private createTimestamp(): number {
|
||||||
return Date.now();
|
return Date.now();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
export * from './application.module';
|
export * from './application.service';
|
||||||
export * from './application.service';
|
export * from './defs';
|
||||||
export * from './application.service-adapter';
|
export * from './store/application.actions';
|
||||||
export * from './defs';
|
|
||||||
export * from './store';
|
|
||||||
|
|||||||
@@ -1,27 +1,8 @@
|
|||||||
import { createAction, props } from '@ngrx/store';
|
import { createAction, props } from '@ngrx/store';
|
||||||
import { ApplicationProcess } from '..';
|
|
||||||
|
const prefix = '[CORE-APPLICATION]';
|
||||||
const prefix = '[CORE-APPLICATION]';
|
|
||||||
|
export const removeProcess = createAction(
|
||||||
export const setTitle = createAction(`${prefix} Set Title`, props<{ title: string }>());
|
`${prefix} Remove Process`,
|
||||||
|
props<{ processId: number }>(),
|
||||||
export const setSection = createAction(`${prefix} Set Section`, props<{ section: 'customer' | 'branch' }>());
|
);
|
||||||
|
|
||||||
export const addProcess = createAction(`${prefix} Add Process`, props<{ process: ApplicationProcess }>());
|
|
||||||
|
|
||||||
export const removeProcess = createAction(`${prefix} Remove Process`, props<{ processId: number }>());
|
|
||||||
|
|
||||||
export const setActivatedProcess = createAction(
|
|
||||||
`${prefix} Set Activated Process`,
|
|
||||||
props<{ activatedProcessId: number }>(),
|
|
||||||
);
|
|
||||||
|
|
||||||
export const patchProcess = createAction(
|
|
||||||
`${prefix} Patch Process`,
|
|
||||||
props<{ processId: number; changes: Partial<ApplicationProcess> }>(),
|
|
||||||
);
|
|
||||||
|
|
||||||
export const patchProcessData = createAction(
|
|
||||||
`${prefix} Patch Process Data`,
|
|
||||||
props<{ processId: number; data: Record<string, any> }>(),
|
|
||||||
);
|
|
||||||
|
|||||||
@@ -1,200 +0,0 @@
|
|||||||
import { INITIAL_APPLICATION_STATE } from './application.state';
|
|
||||||
import * as actions from './application.actions';
|
|
||||||
import { applicationReducer } from './application.reducer';
|
|
||||||
import { ApplicationProcess } from '../defs';
|
|
||||||
import { ApplicationState } from './application.state';
|
|
||||||
|
|
||||||
describe('applicationReducer', () => {
|
|
||||||
describe('setSection()', () => {
|
|
||||||
it('should return modified state with section customer', () => {
|
|
||||||
const initialState = INITIAL_APPLICATION_STATE;
|
|
||||||
|
|
||||||
const action = actions.setSection({ section: 'customer' });
|
|
||||||
const state = applicationReducer(initialState, action);
|
|
||||||
|
|
||||||
expect(state).toEqual({
|
|
||||||
...initialState,
|
|
||||||
section: 'customer',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return modified state with section branch', () => {
|
|
||||||
const initialState = INITIAL_APPLICATION_STATE;
|
|
||||||
|
|
||||||
const action = actions.setSection({ section: 'branch' });
|
|
||||||
const state = applicationReducer(initialState, action);
|
|
||||||
|
|
||||||
expect(state).toEqual({
|
|
||||||
...initialState,
|
|
||||||
section: 'branch',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('addProcess()', () => {
|
|
||||||
it('should return modified state with new process if no processes are registered in the state', () => {
|
|
||||||
const initialState = INITIAL_APPLICATION_STATE;
|
|
||||||
|
|
||||||
const process: ApplicationProcess = {
|
|
||||||
id: 1,
|
|
||||||
name: 'Vorgang',
|
|
||||||
section: 'customer',
|
|
||||||
type: 'cart',
|
|
||||||
data: {},
|
|
||||||
};
|
|
||||||
|
|
||||||
const action = actions.addProcess({ process });
|
|
||||||
const state = applicationReducer(initialState, action);
|
|
||||||
expect(state.processes[0]).toEqual(process);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('patchProcess()', () => {
|
|
||||||
it('should return modified state with updated process when id is found', () => {
|
|
||||||
const initialState = INITIAL_APPLICATION_STATE;
|
|
||||||
|
|
||||||
const process: ApplicationProcess = {
|
|
||||||
id: 1,
|
|
||||||
name: 'Vorgang',
|
|
||||||
section: 'customer',
|
|
||||||
type: 'cart',
|
|
||||||
};
|
|
||||||
|
|
||||||
const action = actions.patchProcess({ processId: process.id, changes: { ...process, name: 'Test' } });
|
|
||||||
const state = applicationReducer(
|
|
||||||
{
|
|
||||||
...initialState,
|
|
||||||
processes: [process],
|
|
||||||
},
|
|
||||||
action,
|
|
||||||
);
|
|
||||||
expect(state.processes[0].name).toEqual('Test');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return unmodified state when id is not existing', () => {
|
|
||||||
const initialState = INITIAL_APPLICATION_STATE;
|
|
||||||
|
|
||||||
const process: ApplicationProcess = {
|
|
||||||
id: 1,
|
|
||||||
name: 'Vorgang',
|
|
||||||
section: 'customer',
|
|
||||||
type: 'cart',
|
|
||||||
};
|
|
||||||
|
|
||||||
const action = actions.patchProcess({ processId: process.id, changes: { ...process, id: 2 } });
|
|
||||||
const state = applicationReducer(
|
|
||||||
{
|
|
||||||
...initialState,
|
|
||||||
processes: [process],
|
|
||||||
},
|
|
||||||
action,
|
|
||||||
);
|
|
||||||
expect(state.processes).toEqual([process]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('removeProcess()', () => {
|
|
||||||
it('should return initial state if no processes are registered in the state', () => {
|
|
||||||
const initialState = INITIAL_APPLICATION_STATE;
|
|
||||||
|
|
||||||
const action = actions.removeProcess({ processId: 2 });
|
|
||||||
const state = applicationReducer(initialState, action);
|
|
||||||
expect(state).toEqual(initialState);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return the unmodified state if processId not found', () => {
|
|
||||||
const initialState = INITIAL_APPLICATION_STATE;
|
|
||||||
const modifiedState: ApplicationState = {
|
|
||||||
...initialState,
|
|
||||||
section: 'customer',
|
|
||||||
processes: [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
name: 'Vorgang',
|
|
||||||
section: 'customer',
|
|
||||||
type: 'cart',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 4,
|
|
||||||
name: 'Vorgang',
|
|
||||||
section: 'customer',
|
|
||||||
type: 'goods-out',
|
|
||||||
},
|
|
||||||
] as ApplicationProcess[],
|
|
||||||
};
|
|
||||||
|
|
||||||
const action = actions.removeProcess({ processId: 2 });
|
|
||||||
const state = applicationReducer(modifiedState, action);
|
|
||||||
expect(state).toEqual(modifiedState);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return modified state, after process gets removed', () => {
|
|
||||||
const initialState = INITIAL_APPLICATION_STATE;
|
|
||||||
const modifiedState: ApplicationState = {
|
|
||||||
...initialState,
|
|
||||||
section: 'customer',
|
|
||||||
processes: [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
name: 'Vorgang',
|
|
||||||
section: 'customer',
|
|
||||||
type: 'cart',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
name: 'Vorgang',
|
|
||||||
section: 'customer',
|
|
||||||
type: 'goods-out',
|
|
||||||
},
|
|
||||||
] as ApplicationProcess[],
|
|
||||||
};
|
|
||||||
|
|
||||||
const action = actions.removeProcess({ processId: 2 });
|
|
||||||
const state = applicationReducer(modifiedState, action);
|
|
||||||
expect(state.processes).toEqual([
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
name: 'Vorgang',
|
|
||||||
section: 'customer',
|
|
||||||
type: 'cart',
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('setActivatedProcess()', () => {
|
|
||||||
it('should return modified state with process.activated value', () => {
|
|
||||||
const process: ApplicationProcess = {
|
|
||||||
id: 3,
|
|
||||||
name: 'Vorgang 3',
|
|
||||||
section: 'customer',
|
|
||||||
};
|
|
||||||
const initialState: ApplicationState = {
|
|
||||||
...INITIAL_APPLICATION_STATE,
|
|
||||||
processes: [process],
|
|
||||||
};
|
|
||||||
|
|
||||||
const action = actions.setActivatedProcess({ activatedProcessId: 3 });
|
|
||||||
const state = applicationReducer(initialState, action);
|
|
||||||
|
|
||||||
expect(state.processes[0].activated).toBeDefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return modified state without process.activated value when activatedProcessId doesnt exist', () => {
|
|
||||||
const process: ApplicationProcess = {
|
|
||||||
id: 1,
|
|
||||||
name: 'Vorgang 3',
|
|
||||||
section: 'customer',
|
|
||||||
};
|
|
||||||
const initialState: ApplicationState = {
|
|
||||||
...INITIAL_APPLICATION_STATE,
|
|
||||||
processes: [process],
|
|
||||||
};
|
|
||||||
|
|
||||||
const action = actions.setActivatedProcess({ activatedProcessId: 3 });
|
|
||||||
const state = applicationReducer(initialState, action);
|
|
||||||
|
|
||||||
expect(state.processes[0].activated).toBeUndefined();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
import { Action, createReducer, on } from '@ngrx/store';
|
|
||||||
import {
|
|
||||||
setSection,
|
|
||||||
addProcess,
|
|
||||||
removeProcess,
|
|
||||||
setActivatedProcess,
|
|
||||||
patchProcess,
|
|
||||||
patchProcessData,
|
|
||||||
setTitle,
|
|
||||||
} from './application.actions';
|
|
||||||
import { ApplicationState, INITIAL_APPLICATION_STATE } from './application.state';
|
|
||||||
|
|
||||||
const _applicationReducer = createReducer(
|
|
||||||
INITIAL_APPLICATION_STATE,
|
|
||||||
on(setTitle, (state, { title }) => ({ ...state, title })),
|
|
||||||
on(setSection, (state, { section }) => ({ ...state, section })),
|
|
||||||
on(addProcess, (state, { process }) => ({ ...state, processes: [...state.processes, { data: {}, ...process }] })),
|
|
||||||
on(removeProcess, (state, { processId }) => {
|
|
||||||
const processes = state?.processes?.filter((process) => process.id !== processId) || [];
|
|
||||||
return { ...state, processes };
|
|
||||||
}),
|
|
||||||
on(setActivatedProcess, (state, { activatedProcessId }) => {
|
|
||||||
const processes = state.processes.map((process) => {
|
|
||||||
if (process.id === activatedProcessId) {
|
|
||||||
return { ...process, activated: Date.now() };
|
|
||||||
}
|
|
||||||
return process;
|
|
||||||
});
|
|
||||||
|
|
||||||
return { ...state, processes };
|
|
||||||
}),
|
|
||||||
on(patchProcess, (state, { processId, changes }) => {
|
|
||||||
const processes = state.processes.map((process) => {
|
|
||||||
if (process.id === processId) {
|
|
||||||
return { ...process, ...changes, id: processId };
|
|
||||||
}
|
|
||||||
return process;
|
|
||||||
});
|
|
||||||
|
|
||||||
return { ...state, processes };
|
|
||||||
}),
|
|
||||||
on(patchProcessData, (state, { processId, data }) => {
|
|
||||||
const processes = state.processes.map((process) => {
|
|
||||||
if (process.id === processId) {
|
|
||||||
return { ...process, data: { ...(process.data || {}), ...data } };
|
|
||||||
}
|
|
||||||
return process;
|
|
||||||
});
|
|
||||||
|
|
||||||
return { ...state, processes };
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
export function applicationReducer(state: ApplicationState, action: Action) {
|
|
||||||
return _applicationReducer(state, action);
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
// import { ApplicationState } from './application.state';
|
|
||||||
// import { ApplicationProcess } from '../defs';
|
|
||||||
// import * as selectors from './application.selectors';
|
|
||||||
|
|
||||||
// describe('applicationSelectors', () => {
|
|
||||||
// it('should select the processes', () => {
|
|
||||||
// const processes: ApplicationProcess[] = [{ id: 1, name: 'Vorgang 1', section: 'customer' }];
|
|
||||||
// const state: ApplicationState = {
|
|
||||||
// processes,
|
|
||||||
// section: 'customer',
|
|
||||||
// };
|
|
||||||
// expect(selectors.selectProcesses.projector(state)).toEqual(processes);
|
|
||||||
// });
|
|
||||||
|
|
||||||
// it('should select the section', () => {
|
|
||||||
// const state: ApplicationState = {
|
|
||||||
// processes: [],
|
|
||||||
// section: 'customer',
|
|
||||||
// };
|
|
||||||
// expect(selectors.selectSection.projector(state)).toEqual('customer');
|
|
||||||
// });
|
|
||||||
|
|
||||||
// it('should select the activatedProcess', () => {
|
|
||||||
// const processes: ApplicationProcess[] = [
|
|
||||||
// { id: 1, name: 'Vorgang 1', section: 'customer', activated: 100 },
|
|
||||||
// { id: 2, name: 'Vorgang 2', section: 'customer', activated: 300 },
|
|
||||||
// { id: 3, name: 'Vorgang 3', section: 'customer', activated: 200 },
|
|
||||||
// ];
|
|
||||||
// const state: ApplicationState = {
|
|
||||||
// processes,
|
|
||||||
// section: 'customer',
|
|
||||||
// };
|
|
||||||
// expect(selectors.selectActivatedProcess.projector(state)).toEqual(processes[1]);
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import { createFeatureSelector, createSelector } from '@ngrx/store';
|
|
||||||
import { ApplicationState } from './application.state';
|
|
||||||
export const selectApplicationState = createFeatureSelector<ApplicationState>('core-application');
|
|
||||||
|
|
||||||
export const selectTitle = createSelector(selectApplicationState, (s) => s.title);
|
|
||||||
|
|
||||||
export const selectSection = createSelector(selectApplicationState, (s) => s.section);
|
|
||||||
|
|
||||||
export const selectProcesses = createSelector(selectApplicationState, (s) => s.processes);
|
|
||||||
|
|
||||||
export const selectActivatedProcess = createSelector(selectApplicationState, (s) =>
|
|
||||||
s?.processes?.reduce((process, current) => {
|
|
||||||
if (!process) {
|
|
||||||
return current;
|
|
||||||
}
|
|
||||||
return process.activated > current.activated ? process : current;
|
|
||||||
}, undefined),
|
|
||||||
);
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import { ApplicationProcess } from '../defs';
|
|
||||||
|
|
||||||
export interface ApplicationState {
|
|
||||||
title: string;
|
|
||||||
processes: ApplicationProcess[];
|
|
||||||
section: 'customer' | 'branch';
|
|
||||||
}
|
|
||||||
|
|
||||||
export const INITIAL_APPLICATION_STATE: ApplicationState = {
|
|
||||||
title: '',
|
|
||||||
processes: [],
|
|
||||||
section: 'customer',
|
|
||||||
};
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
// start:ng42.barrel
|
|
||||||
export * from './application.actions';
|
|
||||||
export * from './application.reducer';
|
|
||||||
export * from './application.selectors';
|
|
||||||
export * from './application.state';
|
|
||||||
// end:ng42.barrel
|
|
||||||
@@ -1,22 +1,15 @@
|
|||||||
import { ModuleWithProviders, NgModule } from '@angular/core';
|
import { EnvironmentProviders, makeEnvironmentProviders } from '@angular/core';
|
||||||
import { EffectsModule } from '@ngrx/effects';
|
import { provideEffects } from '@ngrx/effects';
|
||||||
import { StoreModule } from '@ngrx/store';
|
import { provideState } from '@ngrx/store';
|
||||||
import { BreadcrumbService } from './breadcrumb.service';
|
import { BreadcrumbService } from './breadcrumb.service';
|
||||||
import { BreadcrumbEffects } from './store/breadcrumb.effect';
|
import { BreadcrumbEffects } from './store/breadcrumb.effect';
|
||||||
import { breadcrumbReducer } from './store/breadcrumb.reducer';
|
import { breadcrumbReducer } from './store/breadcrumb.reducer';
|
||||||
import { featureName } from './store/breadcrumb.state';
|
import { featureName } from './store/breadcrumb.state';
|
||||||
|
|
||||||
@NgModule()
|
export function provideCoreBreadcrumb(): EnvironmentProviders {
|
||||||
export class CoreBreadcrumbModule {
|
return makeEnvironmentProviders([
|
||||||
static forRoot(): ModuleWithProviders<CoreBreadcrumbModule> {
|
provideState({ name: featureName, reducer: breadcrumbReducer }),
|
||||||
return {
|
provideEffects(BreadcrumbEffects),
|
||||||
ngModule: CoreBreadcrumbForRootModule,
|
BreadcrumbService,
|
||||||
};
|
]);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
imports: [StoreModule.forFeature(featureName, breadcrumbReducer), EffectsModule.forFeature([BreadcrumbEffects])],
|
|
||||||
providers: [BreadcrumbService],
|
|
||||||
})
|
|
||||||
export class CoreBreadcrumbForRootModule {}
|
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
import { ModuleWithProviders, NgModule } from '@angular/core';
|
|
||||||
import { DomainAvailabilityService } from './availability.service';
|
|
||||||
|
|
||||||
@NgModule()
|
|
||||||
export class DomainAvailabilityModule {
|
|
||||||
static forRoot(): ModuleWithProviders<DomainAvailabilityModule> {
|
|
||||||
return {
|
|
||||||
ngModule: DomainAvailabilityModule,
|
|
||||||
providers: [DomainAvailabilityService],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,3 @@
|
|||||||
export * from './availability.module';
|
|
||||||
export * from './availability.service';
|
export * from './availability.service';
|
||||||
export * from './defs';
|
export * from './defs';
|
||||||
export * from './in-stock.service';
|
export * from './in-stock.service';
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
import { ModuleWithProviders, NgModule } from '@angular/core';
|
|
||||||
import { DomainCatalogService } from './catalog.service';
|
|
||||||
import { ThumbnailUrlPipe } from './thumbnail-url.pipe';
|
|
||||||
import { DomainCatalogThumbnailService } from './thumbnail.service';
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
declarations: [ThumbnailUrlPipe],
|
|
||||||
imports: [],
|
|
||||||
exports: [ThumbnailUrlPipe],
|
|
||||||
})
|
|
||||||
export class DomainCatalogModule {
|
|
||||||
static forRoot(): ModuleWithProviders<DomainCatalogModule> {
|
|
||||||
return {
|
|
||||||
ngModule: DomainCatalogModule,
|
|
||||||
providers: [DomainCatalogService, DomainCatalogThumbnailService],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,105 +1,111 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { ApplicationService } from '@core/application';
|
import { ApplicationService } from '@core/application';
|
||||||
import {
|
import {
|
||||||
AutocompleteTokenDTO,
|
AutocompleteTokenDTO,
|
||||||
PromotionService,
|
PromotionService,
|
||||||
QueryTokenDTO,
|
QueryTokenDTO,
|
||||||
SearchService,
|
SearchService,
|
||||||
} from '@generated/swagger/cat-search-api';
|
} from '@generated/swagger/cat-search-api';
|
||||||
import { memorize } from '@utils/common';
|
import { memorize } from '@utils/common';
|
||||||
import { map, share, shareReplay } from 'rxjs/operators';
|
import { map, share, shareReplay } from 'rxjs/operators';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable({ providedIn: 'root' })
|
||||||
export class DomainCatalogService {
|
export class DomainCatalogService {
|
||||||
constructor(
|
constructor(
|
||||||
private searchService: SearchService,
|
private searchService: SearchService,
|
||||||
private promotionService: PromotionService,
|
private promotionService: PromotionService,
|
||||||
private applicationService: ApplicationService,
|
private applicationService: ApplicationService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@memorize()
|
@memorize()
|
||||||
getFilters() {
|
getFilters() {
|
||||||
return this.searchService.SearchSearchFilter().pipe(
|
return this.searchService.SearchSearchFilter().pipe(
|
||||||
map((res) => res.result),
|
map((res) => res.result),
|
||||||
shareReplay(),
|
shareReplay(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@memorize()
|
@memorize()
|
||||||
getOrderBy() {
|
getOrderBy() {
|
||||||
return this.searchService.SearchSearchSort().pipe(
|
return this.searchService.SearchSearchSort().pipe(
|
||||||
map((res) => res.result),
|
map((res) => res.result),
|
||||||
shareReplay(),
|
shareReplay(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getSearchHistory({ take }: { take: number }) {
|
getSearchHistory({ take }: { take: number }) {
|
||||||
return this.searchService.SearchHistory(take ?? 5).pipe(map((res) => res.result));
|
return this.searchService
|
||||||
}
|
.SearchHistory(take ?? 5)
|
||||||
|
.pipe(map((res) => res.result));
|
||||||
@memorize({ ttl: 120000 })
|
}
|
||||||
search({ queryToken }: { queryToken: QueryTokenDTO }) {
|
|
||||||
return this.searchService
|
@memorize({ ttl: 120000 })
|
||||||
.SearchSearch({
|
search({ queryToken }: { queryToken: QueryTokenDTO }) {
|
||||||
...queryToken,
|
return this.searchService
|
||||||
stockId: null,
|
.SearchSearch({
|
||||||
})
|
...queryToken,
|
||||||
.pipe(share());
|
stockId: null,
|
||||||
}
|
})
|
||||||
|
.pipe(share());
|
||||||
@memorize({ ttl: 120000 })
|
}
|
||||||
searchWithStockId({ queryToken }: { queryToken: QueryTokenDTO }) {
|
|
||||||
return this.searchService
|
@memorize({ ttl: 120000 })
|
||||||
.SearchSearch2({
|
searchWithStockId({ queryToken }: { queryToken: QueryTokenDTO }) {
|
||||||
queryToken,
|
return this.searchService
|
||||||
stockId: queryToken?.stockId ?? null,
|
.SearchSearch2({
|
||||||
})
|
queryToken,
|
||||||
.pipe(share());
|
stockId: queryToken?.stockId ?? null,
|
||||||
}
|
})
|
||||||
|
.pipe(share());
|
||||||
getDetailsById({ id }: { id: number }) {
|
}
|
||||||
return this.searchService.SearchDetail({
|
|
||||||
id,
|
getDetailsById({ id }: { id: number }) {
|
||||||
});
|
return this.searchService.SearchDetail({
|
||||||
}
|
id,
|
||||||
|
});
|
||||||
getDetailsByEan({ ean }: { ean: string }) {
|
}
|
||||||
return this.searchService.SearchDetailByEAN(ean);
|
|
||||||
}
|
getDetailsByEan({ ean }: { ean: string }) {
|
||||||
|
return this.searchService.SearchDetailByEAN(ean);
|
||||||
searchByIds({ ids }: { ids: number[] }) {
|
}
|
||||||
return this.searchService.SearchById(ids);
|
|
||||||
}
|
searchByIds({ ids }: { ids: number[] }) {
|
||||||
|
return this.searchService.SearchById(ids);
|
||||||
searchByEans({ eans }: { eans: string[] }) {
|
}
|
||||||
return this.searchService.SearchByEAN(eans);
|
|
||||||
}
|
searchByEans({ eans }: { eans: string[] }) {
|
||||||
|
return this.searchService.SearchByEAN(eans);
|
||||||
searchTop({ queryToken }: { queryToken: QueryTokenDTO }) {
|
}
|
||||||
return this.searchService.SearchTop(queryToken);
|
|
||||||
}
|
searchTop({ queryToken }: { queryToken: QueryTokenDTO }) {
|
||||||
|
return this.searchService.SearchTop(queryToken);
|
||||||
searchComplete({ queryToken }: { queryToken: AutocompleteTokenDTO }) {
|
}
|
||||||
return this.searchService.SearchAutocomplete(queryToken);
|
|
||||||
}
|
searchComplete({ queryToken }: { queryToken: AutocompleteTokenDTO }) {
|
||||||
|
return this.searchService.SearchAutocomplete(queryToken);
|
||||||
@memorize()
|
}
|
||||||
getPromotionPoints({ items }: { items: { id: number; quantity: number; price?: number }[] }) {
|
|
||||||
return this.promotionService.PromotionLesepunkte(items).pipe(shareReplay());
|
@memorize()
|
||||||
}
|
getPromotionPoints({
|
||||||
|
items,
|
||||||
@memorize()
|
}: {
|
||||||
getSettings() {
|
items: { id: number; quantity: number; price?: number }[];
|
||||||
return this.searchService.SearchSettings().pipe(
|
}) {
|
||||||
map((res) => res.result),
|
return this.promotionService.PromotionLesepunkte(items).pipe(shareReplay());
|
||||||
shareReplay(),
|
}
|
||||||
);
|
|
||||||
}
|
@memorize()
|
||||||
|
getSettings() {
|
||||||
getRecommendations({ digId }: { digId: number }) {
|
return this.searchService.SearchSettings().pipe(
|
||||||
return this.searchService.SearchGetRecommendations({
|
map((res) => res.result),
|
||||||
digId: digId + '',
|
shareReplay(),
|
||||||
sessionId: this.applicationService.activatedProcessId + '',
|
);
|
||||||
});
|
}
|
||||||
}
|
|
||||||
}
|
getRecommendations({ digId }: { digId: number }) {
|
||||||
|
return this.searchService.SearchGetRecommendations({
|
||||||
|
digId: digId + '',
|
||||||
|
sessionId: this.applicationService.activatedProcessId + '',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
export * from './catalog.module';
|
|
||||||
export * from './catalog.service';
|
export * from './catalog.service';
|
||||||
export * from './thumbnail-url.pipe';
|
export * from './thumbnail-url.pipe';
|
||||||
export * from './thumbnail.service';
|
export * from './thumbnail.service';
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { DomainCatalogThumbnailService } from './thumbnail.service';
|
|||||||
@Pipe({
|
@Pipe({
|
||||||
name: 'thumbnailUrl',
|
name: 'thumbnailUrl',
|
||||||
pure: false,
|
pure: false,
|
||||||
standalone: false,
|
standalone: true,
|
||||||
})
|
})
|
||||||
export class ThumbnailUrlPipe implements PipeTransform, OnDestroy {
|
export class ThumbnailUrlPipe implements PipeTransform, OnDestroy {
|
||||||
private input$ = new BehaviorSubject<{ width?: number; height?: number; ean?: string }>(undefined);
|
private input$ = new BehaviorSubject<{ width?: number; height?: number; ean?: string }>(undefined);
|
||||||
|
|||||||
@@ -1,20 +1,28 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { memorize } from '@utils/common';
|
import { memorize } from '@utils/common';
|
||||||
import { map, shareReplay } from 'rxjs/operators';
|
import { map, shareReplay } from 'rxjs/operators';
|
||||||
import { DomainCatalogService } from './catalog.service';
|
import { DomainCatalogService } from './catalog.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable({ providedIn: 'root' })
|
||||||
export class DomainCatalogThumbnailService {
|
export class DomainCatalogThumbnailService {
|
||||||
constructor(private domainCatalogService: DomainCatalogService) {}
|
constructor(private domainCatalogService: DomainCatalogService) {}
|
||||||
|
|
||||||
@memorize()
|
@memorize()
|
||||||
getThumnaulUrl({ ean, height, width }: { width?: number; height?: number; ean?: string }) {
|
getThumnaulUrl({
|
||||||
return this.domainCatalogService.getSettings().pipe(
|
ean,
|
||||||
map((settings) => {
|
height,
|
||||||
let thumbnailUrl = settings.imageUrl.replace(/{ean}/, ean);
|
width,
|
||||||
return thumbnailUrl;
|
}: {
|
||||||
}),
|
width?: number;
|
||||||
shareReplay(),
|
height?: number;
|
||||||
);
|
ean?: string;
|
||||||
}
|
}) {
|
||||||
}
|
return this.domainCatalogService.getSettings().pipe(
|
||||||
|
map((settings) => {
|
||||||
|
const thumbnailUrl = settings.imageUrl.replace(/{ean}/, ean);
|
||||||
|
return thumbnailUrl;
|
||||||
|
}),
|
||||||
|
shareReplay(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,29 +1,15 @@
|
|||||||
import { ModuleWithProviders, NgModule } from '@angular/core';
|
import { EnvironmentProviders, makeEnvironmentProviders } from '@angular/core';
|
||||||
import { StoreModule } from '@ngrx/store';
|
import { provideEffects } from '@ngrx/effects';
|
||||||
import { DomainCheckoutService } from './checkout.service';
|
import { provideState } from '@ngrx/store';
|
||||||
import { domainCheckoutReducer } from './store/domain-checkout.reducer';
|
import { DomainCheckoutService } from './checkout.service';
|
||||||
import { storeFeatureName } from './store/domain-checkout.state';
|
import { DomainCheckoutEffects } from './store/domain-checkout.effects';
|
||||||
import { EffectsModule } from '@ngrx/effects';
|
import { domainCheckoutReducer } from './store/domain-checkout.reducer';
|
||||||
import { DomainCheckoutEffects } from './store/domain-checkout.effects';
|
import { storeFeatureName } from './store/domain-checkout.state';
|
||||||
|
|
||||||
@NgModule({
|
export function provideDomainCheckout(): EnvironmentProviders {
|
||||||
declarations: [],
|
return makeEnvironmentProviders([
|
||||||
imports: [StoreModule.forFeature(storeFeatureName, domainCheckoutReducer)],
|
provideState({ name: storeFeatureName, reducer: domainCheckoutReducer }),
|
||||||
providers: [DomainCheckoutService],
|
provideEffects(DomainCheckoutEffects),
|
||||||
})
|
DomainCheckoutService,
|
||||||
export class DomainCheckoutModule {
|
]);
|
||||||
static forRoot(): ModuleWithProviders<DomainCheckoutModule> {
|
}
|
||||||
return {
|
|
||||||
ngModule: RootDomainCheckoutModule,
|
|
||||||
providers: [DomainCheckoutService],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
imports: [
|
|
||||||
StoreModule.forFeature(storeFeatureName, domainCheckoutReducer),
|
|
||||||
EffectsModule.forFeature([DomainCheckoutEffects]),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
export class RootDomainCheckoutModule {}
|
|
||||||
|
|||||||
@@ -1071,7 +1071,7 @@ export class DomainCheckoutService {
|
|||||||
});
|
});
|
||||||
} else if (orderType === 'B2B-Versand') {
|
} else if (orderType === 'B2B-Versand') {
|
||||||
const branch = await this.applicationService
|
const branch = await this.applicationService
|
||||||
.getSelectedBranch$(processId)
|
.getSelectedBranch$()
|
||||||
.pipe(first())
|
.pipe(first())
|
||||||
.toPromise();
|
.toPromise();
|
||||||
availability$ =
|
availability$ =
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { InfoService } from '@generated/swagger/isa-api';
|
import { InfoService } from '@generated/swagger/isa-api';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable({ providedIn: 'root' })
|
||||||
export class DomainDashboardService {
|
export class DomainDashboardService {
|
||||||
constructor(private readonly _infoService: InfoService) {}
|
constructor(private readonly _infoService: InfoService) {}
|
||||||
|
|
||||||
feed() {
|
feed() {
|
||||||
return this._infoService.InfoInfo({});
|
return this._infoService.InfoInfo({});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
import { ModuleWithProviders, NgModule } from '@angular/core';
|
|
||||||
import { DomainDashboardService } from './dashboard.service';
|
|
||||||
|
|
||||||
@NgModule({})
|
|
||||||
export class DomainIsaModule {
|
|
||||||
static forRoot(): ModuleWithProviders<DomainIsaModule> {
|
|
||||||
return {
|
|
||||||
ngModule: DomainIsaModule,
|
|
||||||
providers: [DomainDashboardService],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,2 @@
|
|||||||
export * from './dashboard.service';
|
export * from './dashboard.service';
|
||||||
export * from './defs';
|
export * from './defs';
|
||||||
export * from './domain-isa.module';
|
|
||||||
|
|||||||
@@ -1,116 +1,130 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { AbholfachService, AutocompleteTokenDTO, QueryTokenDTO } from '@generated/swagger/oms-api';
|
import {
|
||||||
import { DateAdapter } from '@ui/common';
|
AbholfachService,
|
||||||
import { memorize } from '@utils/common';
|
AutocompleteTokenDTO,
|
||||||
import { shareReplay } from 'rxjs/operators';
|
QueryTokenDTO,
|
||||||
@Injectable()
|
} from '@generated/swagger/oms-api';
|
||||||
export class DomainGoodsService {
|
import { DateAdapter } from '@ui/common';
|
||||||
constructor(
|
import { memorize } from '@utils/common';
|
||||||
private abholfachService: AbholfachService,
|
import { shareReplay } from 'rxjs/operators';
|
||||||
private dateAdapter: DateAdapter,
|
@Injectable({ providedIn: 'root' })
|
||||||
) {}
|
export class DomainGoodsService {
|
||||||
|
constructor(
|
||||||
searchWareneingang(queryToken: QueryTokenDTO) {
|
private abholfachService: AbholfachService,
|
||||||
return this.abholfachService.AbholfachWareneingang(queryToken);
|
private dateAdapter: DateAdapter,
|
||||||
}
|
) {}
|
||||||
|
|
||||||
searchWarenausgabe(queryToken: QueryTokenDTO) {
|
searchWareneingang(queryToken: QueryTokenDTO) {
|
||||||
return this.abholfachService.AbholfachWarenausgabe(queryToken);
|
return this.abholfachService.AbholfachWareneingang(queryToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
wareneingangComplete(autocompleteToken: AutocompleteTokenDTO) {
|
searchWarenausgabe(queryToken: QueryTokenDTO) {
|
||||||
return this.abholfachService.AbholfachWareneingangAutocomplete(autocompleteToken);
|
return this.abholfachService.AbholfachWarenausgabe(queryToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
warenausgabeComplete(autocompleteToken: AutocompleteTokenDTO) {
|
wareneingangComplete(autocompleteToken: AutocompleteTokenDTO) {
|
||||||
return this.abholfachService.AbholfachWarenausgabeAutocomplete(autocompleteToken);
|
return this.abholfachService.AbholfachWareneingangAutocomplete(
|
||||||
}
|
autocompleteToken,
|
||||||
|
);
|
||||||
getWareneingangItemByOrderNumber(orderNumber: string) {
|
}
|
||||||
return this.abholfachService.AbholfachWareneingang({
|
|
||||||
filter: { all_branches: 'true', archive: 'true' },
|
warenausgabeComplete(autocompleteToken: AutocompleteTokenDTO) {
|
||||||
input: {
|
return this.abholfachService.AbholfachWarenausgabeAutocomplete(
|
||||||
qs: orderNumber,
|
autocompleteToken,
|
||||||
},
|
);
|
||||||
});
|
}
|
||||||
}
|
|
||||||
|
getWareneingangItemByOrderNumber(orderNumber: string) {
|
||||||
getWarenausgabeItemByOrderNumber(orderNumber: string, archive: boolean) {
|
return this.abholfachService.AbholfachWareneingang({
|
||||||
return this.abholfachService.AbholfachWarenausgabe({
|
filter: { all_branches: 'true', archive: 'true' },
|
||||||
filter: { all_branches: 'true', archive: `${archive}` },
|
input: {
|
||||||
input: {
|
qs: orderNumber,
|
||||||
qs: orderNumber,
|
},
|
||||||
},
|
});
|
||||||
});
|
}
|
||||||
}
|
|
||||||
|
getWarenausgabeItemByOrderNumber(orderNumber: string, archive: boolean) {
|
||||||
getWarenausgabeItemByCompartment(compartmentCode: string, archive: boolean) {
|
return this.abholfachService.AbholfachWarenausgabe({
|
||||||
return this.abholfachService.AbholfachWarenausgabe({
|
filter: { all_branches: 'true', archive: `${archive}` },
|
||||||
filter: { all_branches: 'true', archive: `${archive}` },
|
input: {
|
||||||
input: {
|
qs: orderNumber,
|
||||||
qs: compartmentCode,
|
},
|
||||||
},
|
});
|
||||||
});
|
}
|
||||||
}
|
|
||||||
|
getWarenausgabeItemByCompartment(compartmentCode: string, archive: boolean) {
|
||||||
getWareneingangItemByCustomerNumber(customerNumber: string) {
|
return this.abholfachService.AbholfachWarenausgabe({
|
||||||
// Suche anhand der Kundennummer mit Status Bestellt, nachbestellt, eingetroffen, weitergeleitet intern
|
filter: { all_branches: 'true', archive: `${archive}` },
|
||||||
return this.abholfachService.AbholfachWareneingang({
|
input: {
|
||||||
filter: { orderitemprocessingstatus: '16;128;8192;1048576' },
|
qs: compartmentCode,
|
||||||
input: {
|
},
|
||||||
customer_no: customerNumber,
|
});
|
||||||
},
|
}
|
||||||
});
|
|
||||||
}
|
getWareneingangItemByCustomerNumber(customerNumber: string) {
|
||||||
|
// Suche anhand der Kundennummer mit Status Bestellt, nachbestellt, eingetroffen, weitergeleitet intern
|
||||||
list() {
|
return this.abholfachService.AbholfachWareneingang({
|
||||||
const base = this.dateAdapter.today();
|
filter: { orderitemprocessingstatus: '16;128;8192;1048576' },
|
||||||
const startDate = this.dateAdapter.addCalendarDays(base, -5);
|
input: {
|
||||||
const endDate = this.dateAdapter.addCalendarDays(base, 1);
|
customer_no: customerNumber,
|
||||||
const queryToken: QueryTokenDTO = {
|
},
|
||||||
filter: {
|
});
|
||||||
orderitemprocessingstatus: '16;8192;1024;512;2048',
|
}
|
||||||
estimatedshippingdate: `"${startDate.toJSON()}"-"${endDate.toJSON()}"`,
|
|
||||||
},
|
list() {
|
||||||
orderBy: [{ by: 'estimatedshippingdate' }],
|
const base = this.dateAdapter.today();
|
||||||
skip: 0,
|
const startDate = this.dateAdapter.addCalendarDays(base, -5);
|
||||||
take: 20,
|
const endDate = this.dateAdapter.addCalendarDays(base, 1);
|
||||||
};
|
const queryToken: QueryTokenDTO = {
|
||||||
return this.searchWareneingang(queryToken);
|
filter: {
|
||||||
}
|
orderitemprocessingstatus: '16;8192;1024;512;2048',
|
||||||
|
estimatedshippingdate: `"${startDate.toJSON()}"-"${endDate.toJSON()}"`,
|
||||||
@memorize()
|
},
|
||||||
goodsInQuerySettings() {
|
orderBy: [{ by: 'estimatedshippingdate' }],
|
||||||
return this.abholfachService.AbholfachWareneingangQuerySettings().pipe(shareReplay());
|
skip: 0,
|
||||||
}
|
take: 20,
|
||||||
|
};
|
||||||
@memorize()
|
return this.searchWareneingang(queryToken);
|
||||||
goodsOutQuerySettings() {
|
}
|
||||||
return this.abholfachService.AbholfachWarenausgabeQuerySettings().pipe(shareReplay());
|
|
||||||
}
|
@memorize()
|
||||||
|
goodsInQuerySettings() {
|
||||||
goodsInList(queryToken: QueryTokenDTO) {
|
return this.abholfachService
|
||||||
return this.abholfachService.AbholfachWareneingangsliste(queryToken);
|
.AbholfachWareneingangQuerySettings()
|
||||||
}
|
.pipe(shareReplay());
|
||||||
|
}
|
||||||
@memorize()
|
|
||||||
goodsInListQuerySettings() {
|
@memorize()
|
||||||
return this.abholfachService.AbholfachWareneingangslisteQuerySettings().pipe(shareReplay());
|
goodsOutQuerySettings() {
|
||||||
}
|
return this.abholfachService
|
||||||
|
.AbholfachWarenausgabeQuerySettings()
|
||||||
goodsInCleanupList() {
|
.pipe(shareReplay());
|
||||||
return this.abholfachService.AbholfachAbholfachbereinigungsliste();
|
}
|
||||||
}
|
|
||||||
|
goodsInList(queryToken: QueryTokenDTO) {
|
||||||
goodsInReservationList(queryToken: QueryTokenDTO) {
|
return this.abholfachService.AbholfachWareneingangsliste(queryToken);
|
||||||
return this.abholfachService.AbholfachReservierungen(queryToken);
|
}
|
||||||
}
|
|
||||||
|
@memorize()
|
||||||
goodsInRemissionPreviewList() {
|
goodsInListQuerySettings() {
|
||||||
return this.abholfachService.AbholfachAbholfachremissionsvorschau();
|
return this.abholfachService
|
||||||
}
|
.AbholfachWareneingangslisteQuerySettings()
|
||||||
|
.pipe(shareReplay());
|
||||||
createGoodsInRemissionFromPreviewList() {
|
}
|
||||||
return this.abholfachService.AbholfachCreateAbholfachremission();
|
|
||||||
}
|
goodsInCleanupList() {
|
||||||
}
|
return this.abholfachService.AbholfachAbholfachbereinigungsliste();
|
||||||
|
}
|
||||||
|
|
||||||
|
goodsInReservationList(queryToken: QueryTokenDTO) {
|
||||||
|
return this.abholfachService.AbholfachReservierungen(queryToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
goodsInRemissionPreviewList() {
|
||||||
|
return this.abholfachService.AbholfachAbholfachremissionsvorschau();
|
||||||
|
}
|
||||||
|
|
||||||
|
createGoodsInRemissionFromPreviewList() {
|
||||||
|
return this.abholfachService.AbholfachCreateAbholfachremission();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,5 @@ export * from './action-handler-services';
|
|||||||
export * from './action-handlers';
|
export * from './action-handlers';
|
||||||
export * from './customer-order.service';
|
export * from './customer-order.service';
|
||||||
export * from './goods.service';
|
export * from './goods.service';
|
||||||
export * from './oms.module';
|
|
||||||
export * from './oms.service';
|
export * from './oms.service';
|
||||||
export * from './receipt.service';
|
export * from './receipt.service';
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
import { ModuleWithProviders, NgModule } from '@angular/core';
|
|
||||||
import { DomainGoodsService } from './goods.service';
|
|
||||||
import { DomainOmsService } from './oms.service';
|
|
||||||
import { DomainReceiptService } from './receipt.service';
|
|
||||||
|
|
||||||
@NgModule()
|
|
||||||
export class DomainOmsModule {
|
|
||||||
static forRoot(): ModuleWithProviders<DomainOmsModule> {
|
|
||||||
return {
|
|
||||||
ngModule: DomainOmsModule,
|
|
||||||
providers: [DomainOmsService, DomainGoodsService, DomainReceiptService],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,316 +1,381 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
BranchService,
|
BranchService,
|
||||||
BuyerDTO,
|
BuyerDTO,
|
||||||
ChangeStockStatusCodeValues,
|
ChangeStockStatusCodeValues,
|
||||||
HistoryDTO,
|
HistoryDTO,
|
||||||
NotificationChannel,
|
NotificationChannel,
|
||||||
OrderCheckoutService,
|
OrderCheckoutService,
|
||||||
OrderDTO,
|
OrderDTO,
|
||||||
OrderItemDTO,
|
OrderItemDTO,
|
||||||
OrderItemSubsetDTO,
|
OrderItemSubsetDTO,
|
||||||
OrderListItemDTO,
|
OrderListItemDTO,
|
||||||
OrderService,
|
OrderService,
|
||||||
ReceiptService,
|
ReceiptService,
|
||||||
StatusValues,
|
StatusValues,
|
||||||
StockStatusCodeService,
|
StockStatusCodeService,
|
||||||
ValueTupleOfLongAndReceiptTypeAndEntityDTOContainerOfReceiptDTO,
|
ValueTupleOfLongAndReceiptTypeAndEntityDTOContainerOfReceiptDTO,
|
||||||
ValueTupleOfOrderItemSubsetDTOAndOrderItemSubsetDTO,
|
ValueTupleOfOrderItemSubsetDTOAndOrderItemSubsetDTO,
|
||||||
VATService,
|
VATService,
|
||||||
} from '@generated/swagger/oms-api';
|
} from '@generated/swagger/oms-api';
|
||||||
import { memorize } from '@utils/common';
|
import { memorize } from '@utils/common';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { map, shareReplay } from 'rxjs/operators';
|
import { map, shareReplay } from 'rxjs/operators';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable({ providedIn: 'root' })
|
||||||
export class DomainOmsService {
|
export class DomainOmsService {
|
||||||
constructor(
|
constructor(
|
||||||
private orderService: OrderService,
|
private orderService: OrderService,
|
||||||
private receiptService: ReceiptService,
|
private receiptService: ReceiptService,
|
||||||
private branchService: BranchService,
|
private branchService: BranchService,
|
||||||
private vatService: VATService,
|
private vatService: VATService,
|
||||||
private stockStatusCodeService: StockStatusCodeService,
|
private stockStatusCodeService: StockStatusCodeService,
|
||||||
private _orderCheckoutService: OrderCheckoutService,
|
private _orderCheckoutService: OrderCheckoutService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
getOrderItemsByCustomerNumber(customerNumber: string, skip: number): Observable<OrderListItemDTO[]> {
|
getOrderItemsByCustomerNumber(
|
||||||
return this.orderService
|
customerNumber: string,
|
||||||
.OrderGetOrdersByBuyerNumber({ buyerNumber: customerNumber, take: 20, skip })
|
skip: number,
|
||||||
.pipe(map((orders) => orders.result));
|
): Observable<OrderListItemDTO[]> {
|
||||||
}
|
return this.orderService
|
||||||
|
.OrderGetOrdersByBuyerNumber({
|
||||||
getOrder(orderId: number): Observable<OrderDTO> {
|
buyerNumber: customerNumber,
|
||||||
return this.orderService.OrderGetOrder(orderId).pipe(map((o) => o.result));
|
take: 20,
|
||||||
}
|
skip,
|
||||||
|
})
|
||||||
getBranches() {
|
.pipe(map((orders) => orders.result));
|
||||||
return this.branchService.BranchGetBranches({});
|
}
|
||||||
}
|
|
||||||
|
getOrder(orderId: number): Observable<OrderDTO> {
|
||||||
getHistory(orderItemSubsetId: number): Observable<HistoryDTO[]> {
|
return this.orderService.OrderGetOrder(orderId).pipe(map((o) => o.result));
|
||||||
return this.orderService
|
}
|
||||||
.OrderGetOrderItemStatusHistory({ orderItemSubsetId })
|
|
||||||
.pipe(map((response) => response.result));
|
getBranches() {
|
||||||
}
|
return this.branchService.BranchGetBranches({});
|
||||||
|
}
|
||||||
getReceipts(
|
|
||||||
orderItemSubsetIds: number[],
|
getHistory(orderItemSubsetId: number): Observable<HistoryDTO[]> {
|
||||||
): Observable<ValueTupleOfLongAndReceiptTypeAndEntityDTOContainerOfReceiptDTO[]> {
|
return this.orderService
|
||||||
return this.receiptService
|
.OrderGetOrderItemStatusHistory({ orderItemSubsetId })
|
||||||
.ReceiptGetReceiptsByOrderItemSubset({
|
.pipe(map((response) => response.result));
|
||||||
payload: {
|
}
|
||||||
receiptType: 65 as unknown as any,
|
|
||||||
ids: orderItemSubsetIds,
|
getReceipts(
|
||||||
eagerLoading: 1,
|
orderItemSubsetIds: number[],
|
||||||
},
|
): Observable<
|
||||||
})
|
ValueTupleOfLongAndReceiptTypeAndEntityDTOContainerOfReceiptDTO[]
|
||||||
.pipe(map((response) => response.result));
|
> {
|
||||||
}
|
return this.receiptService
|
||||||
|
.ReceiptGetReceiptsByOrderItemSubset({
|
||||||
getReorderReasons() {
|
payload: {
|
||||||
return this._orderCheckoutService.OrderCheckoutGetReorderReasons().pipe(map((response) => response.result));
|
receiptType: 65 as unknown as any,
|
||||||
}
|
ids: orderItemSubsetIds,
|
||||||
|
eagerLoading: 1,
|
||||||
@memorize()
|
},
|
||||||
getVATs() {
|
})
|
||||||
return this.vatService.VATGetVATs({}).pipe(map((response) => response.result));
|
.pipe(map((response) => response.result));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ttl 4 Stunden
|
getReorderReasons() {
|
||||||
@memorize({ ttl: 14400000 })
|
return this._orderCheckoutService
|
||||||
getStockStatusCodes({ supplierId, eagerLoading = 0 }: { supplierId: number; eagerLoading?: number }) {
|
.OrderCheckoutGetReorderReasons()
|
||||||
return this.stockStatusCodeService.StockStatusCodeGetStockStatusCodes({ supplierId, eagerLoading }).pipe(
|
.pipe(map((response) => response.result));
|
||||||
map((response) => response.result),
|
}
|
||||||
shareReplay(),
|
|
||||||
);
|
@memorize()
|
||||||
}
|
getVATs() {
|
||||||
|
return this.vatService
|
||||||
patchOrderItem(payload: { orderItemId: number; orderId: number; orderItem: Partial<OrderItemDTO> }) {
|
.VATGetVATs({})
|
||||||
return this.orderService.OrderPatchOrderItem(payload).pipe(map((response) => response.result));
|
.pipe(map((response) => response.result));
|
||||||
}
|
}
|
||||||
|
|
||||||
patchOrderItemSubset(payload: {
|
// ttl 4 Stunden
|
||||||
orderItemSubsetId: number;
|
@memorize({ ttl: 14400000 })
|
||||||
orderItemId: number;
|
getStockStatusCodes({
|
||||||
orderId: number;
|
supplierId,
|
||||||
orderItemSubset: Partial<OrderItemSubsetDTO>;
|
eagerLoading = 0,
|
||||||
}) {
|
}: {
|
||||||
return this.orderService.OrderPatchOrderItemSubset(payload).pipe(map((response) => response.result));
|
supplierId: number;
|
||||||
}
|
eagerLoading?: number;
|
||||||
|
}) {
|
||||||
patchComment({
|
return this.stockStatusCodeService
|
||||||
orderId,
|
.StockStatusCodeGetStockStatusCodes({ supplierId, eagerLoading })
|
||||||
orderItemId,
|
.pipe(
|
||||||
orderItemSubsetId,
|
map((response) => response.result),
|
||||||
specialComment,
|
shareReplay(),
|
||||||
}: {
|
);
|
||||||
orderId: number;
|
}
|
||||||
orderItemId: number;
|
|
||||||
orderItemSubsetId: number;
|
patchOrderItem(payload: {
|
||||||
specialComment: string;
|
orderItemId: number;
|
||||||
}) {
|
orderId: number;
|
||||||
return this.orderService
|
orderItem: Partial<OrderItemDTO>;
|
||||||
.OrderPatchOrderItemSubset({
|
}) {
|
||||||
orderId,
|
return this.orderService
|
||||||
orderItemId,
|
.OrderPatchOrderItem(payload)
|
||||||
orderItemSubsetId,
|
.pipe(map((response) => response.result));
|
||||||
orderItemSubset: {
|
}
|
||||||
specialComment,
|
|
||||||
},
|
patchOrderItemSubset(payload: {
|
||||||
})
|
orderItemSubsetId: number;
|
||||||
.pipe(map((response) => response.result));
|
orderItemId: number;
|
||||||
}
|
orderId: number;
|
||||||
|
orderItemSubset: Partial<OrderItemSubsetDTO>;
|
||||||
changeOrderStatus(
|
}) {
|
||||||
orderId: number,
|
return this.orderService
|
||||||
orderItemId: number,
|
.OrderPatchOrderItemSubset(payload)
|
||||||
orderItemSubsetId: number,
|
.pipe(map((response) => response.result));
|
||||||
data: StatusValues,
|
}
|
||||||
): Observable<ValueTupleOfOrderItemSubsetDTOAndOrderItemSubsetDTO> {
|
|
||||||
return this.orderService
|
patchComment({
|
||||||
.OrderChangeStatus({
|
orderId,
|
||||||
data,
|
orderItemId,
|
||||||
orderId,
|
orderItemSubsetId,
|
||||||
orderItemId,
|
specialComment,
|
||||||
orderItemSubsetId,
|
}: {
|
||||||
})
|
orderId: number;
|
||||||
.pipe(map((o) => o.result));
|
orderItemId: number;
|
||||||
}
|
orderItemSubsetId: number;
|
||||||
|
specialComment: string;
|
||||||
setEstimatedShippingDate(
|
}) {
|
||||||
orderId: number,
|
return this.orderService
|
||||||
orderItemId: number,
|
.OrderPatchOrderItemSubset({
|
||||||
orderItemSubsetId: number,
|
orderId,
|
||||||
estimatedShippingDate: Date | string,
|
orderItemId,
|
||||||
) {
|
orderItemSubsetId,
|
||||||
return this.orderService
|
orderItemSubset: {
|
||||||
.OrderPatchOrderItemSubset({
|
specialComment,
|
||||||
orderId,
|
},
|
||||||
orderItemId,
|
})
|
||||||
orderItemSubsetId,
|
.pipe(map((response) => response.result));
|
||||||
orderItemSubset: {
|
}
|
||||||
estimatedShippingDate:
|
|
||||||
estimatedShippingDate instanceof Date ? estimatedShippingDate.toJSON() : estimatedShippingDate,
|
changeOrderStatus(
|
||||||
},
|
orderId: number,
|
||||||
})
|
orderItemId: number,
|
||||||
.pipe(map((response) => response.result));
|
orderItemSubsetId: number,
|
||||||
}
|
data: StatusValues,
|
||||||
|
): Observable<ValueTupleOfOrderItemSubsetDTOAndOrderItemSubsetDTO> {
|
||||||
setPickUpDeadline(orderId: number, orderItemId: number, orderItemSubsetId: number, pickUpDeadline: string) {
|
return this.orderService
|
||||||
return this.orderService
|
.OrderChangeStatus({
|
||||||
.OrderPatchOrderItemSubset({
|
data,
|
||||||
orderId,
|
orderId,
|
||||||
orderItemId,
|
orderItemId,
|
||||||
orderItemSubsetId,
|
orderItemSubsetId,
|
||||||
orderItemSubset: {
|
})
|
||||||
compartmentStop: pickUpDeadline,
|
.pipe(map((o) => o.result));
|
||||||
},
|
}
|
||||||
})
|
|
||||||
.pipe(map((response) => response.result));
|
setEstimatedShippingDate(
|
||||||
}
|
orderId: number,
|
||||||
|
orderItemId: number,
|
||||||
setPreferredPickUpDate({ data }: { data: { [key: string]: string } }) {
|
orderItemSubsetId: number,
|
||||||
return this.orderService.OrderSetPreferredPickUpDate({ data });
|
estimatedShippingDate: Date | string,
|
||||||
}
|
) {
|
||||||
|
return this.orderService
|
||||||
changeOrderItemStatus(data: OrderService.OrderChangeStatusParams) {
|
.OrderPatchOrderItemSubset({
|
||||||
return this.orderService.OrderChangeStatus(data);
|
orderId,
|
||||||
}
|
orderItemId,
|
||||||
|
orderItemSubsetId,
|
||||||
changeStockStatusCode(payload: ChangeStockStatusCodeValues[]) {
|
orderItemSubset: {
|
||||||
return this.orderService.OrderChangeStockStatusCode(payload).pipe(map((response) => response.result));
|
estimatedShippingDate:
|
||||||
}
|
estimatedShippingDate instanceof Date
|
||||||
|
? estimatedShippingDate.toJSON()
|
||||||
orderAtSupplier({
|
: estimatedShippingDate,
|
||||||
orderId,
|
},
|
||||||
orderItemId,
|
})
|
||||||
orderItemSubsetId,
|
.pipe(map((response) => response.result));
|
||||||
}: {
|
}
|
||||||
orderId: number;
|
|
||||||
orderItemId: number;
|
setPickUpDeadline(
|
||||||
orderItemSubsetId: number;
|
orderId: number,
|
||||||
}) {
|
orderItemId: number,
|
||||||
return this._orderCheckoutService.OrderCheckoutOrderSubsetItemAtSupplier({
|
orderItemSubsetId: number,
|
||||||
orderId,
|
pickUpDeadline: string,
|
||||||
orderItemId,
|
) {
|
||||||
orderItemSubsetId,
|
return this.orderService
|
||||||
});
|
.OrderPatchOrderItemSubset({
|
||||||
}
|
orderId,
|
||||||
|
orderItemId,
|
||||||
getNotifications(orderId: number): Observable<{ selected: NotificationChannel; email: string; mobile: string }> {
|
orderItemSubsetId,
|
||||||
return this.getOrder(orderId).pipe(
|
orderItemSubset: {
|
||||||
map((order) => ({
|
compartmentStop: pickUpDeadline,
|
||||||
selected: order.notificationChannels,
|
},
|
||||||
email: order.buyer?.communicationDetails?.email,
|
})
|
||||||
mobile: order.buyer?.communicationDetails?.mobile,
|
.pipe(map((response) => response.result));
|
||||||
})),
|
}
|
||||||
);
|
|
||||||
}
|
setPreferredPickUpDate({ data }: { data: { [key: string]: string } }) {
|
||||||
|
return this.orderService.OrderSetPreferredPickUpDate({ data });
|
||||||
getOrderSource(orderId: number): Observable<string> {
|
}
|
||||||
return this.getOrder(orderId).pipe(map((order) => order?.features?.orderSource));
|
|
||||||
}
|
changeOrderItemStatus(data: OrderService.OrderChangeStatusParams) {
|
||||||
|
return this.orderService.OrderChangeStatus(data);
|
||||||
updateNotifications(orderId: number, changes: { selected: NotificationChannel; email: string; mobile: string }) {
|
}
|
||||||
const communicationDetails = {
|
|
||||||
email: changes.email,
|
changeStockStatusCode(payload: ChangeStockStatusCodeValues[]) {
|
||||||
mobile: changes.mobile,
|
return this.orderService
|
||||||
};
|
.OrderChangeStockStatusCode(payload)
|
||||||
|
.pipe(map((response) => response.result));
|
||||||
if (!(changes.selected & 1)) {
|
}
|
||||||
delete communicationDetails.email;
|
|
||||||
}
|
orderAtSupplier({
|
||||||
if (!(changes.selected & 2)) {
|
orderId,
|
||||||
delete communicationDetails.mobile;
|
orderItemId,
|
||||||
}
|
orderItemSubsetId,
|
||||||
|
}: {
|
||||||
return this.updateOrder({ orderId, notificationChannels: changes.selected, communicationDetails });
|
orderId: number;
|
||||||
}
|
orderItemId: number;
|
||||||
|
orderItemSubsetId: number;
|
||||||
updateOrder({
|
}) {
|
||||||
orderId,
|
return this._orderCheckoutService.OrderCheckoutOrderSubsetItemAtSupplier({
|
||||||
notificationChannels,
|
orderId,
|
||||||
communicationDetails,
|
orderItemId,
|
||||||
firstName,
|
orderItemSubsetId,
|
||||||
lastName,
|
});
|
||||||
organisation,
|
}
|
||||||
}: {
|
|
||||||
orderId: number;
|
getNotifications(
|
||||||
notificationChannels?: NotificationChannel;
|
orderId: number,
|
||||||
communicationDetails?: { email?: string; mobile?: string };
|
): Observable<{
|
||||||
lastName?: string;
|
selected: NotificationChannel;
|
||||||
firstName?: string;
|
email: string;
|
||||||
organisation?: string;
|
mobile: string;
|
||||||
}) {
|
}> {
|
||||||
const buyer: BuyerDTO = {};
|
return this.getOrder(orderId).pipe(
|
||||||
|
map((order) => ({
|
||||||
if (communicationDetails) {
|
selected: order.notificationChannels,
|
||||||
buyer.communicationDetails = { ...communicationDetails };
|
email: order.buyer?.communicationDetails?.email,
|
||||||
}
|
mobile: order.buyer?.communicationDetails?.mobile,
|
||||||
|
})),
|
||||||
if (!!lastName || !!firstName) {
|
);
|
||||||
buyer.lastName = lastName;
|
}
|
||||||
buyer.firstName = firstName;
|
|
||||||
}
|
getOrderSource(orderId: number): Observable<string> {
|
||||||
|
return this.getOrder(orderId).pipe(
|
||||||
if (!!organisation && !!buyer.organisation) {
|
map((order) => order?.features?.orderSource),
|
||||||
buyer.organisation = {
|
);
|
||||||
name: organisation,
|
}
|
||||||
};
|
|
||||||
}
|
updateNotifications(
|
||||||
|
orderId: number,
|
||||||
return this.orderService
|
changes: { selected: NotificationChannel; email: string; mobile: string },
|
||||||
.OrderPatchOrder({
|
) {
|
||||||
orderId: orderId,
|
const communicationDetails = {
|
||||||
order: {
|
email: changes.email,
|
||||||
notificationChannels,
|
mobile: changes.mobile,
|
||||||
buyer,
|
};
|
||||||
},
|
|
||||||
})
|
if (!(changes.selected & 1)) {
|
||||||
.pipe(map((res) => res.result));
|
delete communicationDetails.email;
|
||||||
}
|
}
|
||||||
|
if (!(changes.selected & 2)) {
|
||||||
generateNotifications({ orderId, taskTypes }: { orderId: number; taskTypes: string[] }) {
|
delete communicationDetails.mobile;
|
||||||
return this.orderService.OrderRegenerateOrderItemStatusTasks({
|
}
|
||||||
orderId,
|
|
||||||
taskTypes,
|
return this.updateOrder({
|
||||||
});
|
orderId,
|
||||||
}
|
notificationChannels: changes.selected,
|
||||||
|
communicationDetails,
|
||||||
getCompletedTasks({
|
});
|
||||||
orderId,
|
}
|
||||||
orderItemId,
|
|
||||||
orderItemSubsetId,
|
updateOrder({
|
||||||
take,
|
orderId,
|
||||||
skip,
|
notificationChannels,
|
||||||
}: {
|
communicationDetails,
|
||||||
orderId: number;
|
firstName,
|
||||||
orderItemId: number;
|
lastName,
|
||||||
orderItemSubsetId: number;
|
organisation,
|
||||||
take?: number;
|
}: {
|
||||||
skip?: number;
|
orderId: number;
|
||||||
}): Observable<Record<string, Date[]>> {
|
notificationChannels?: NotificationChannel;
|
||||||
return this.orderService
|
communicationDetails?: { email?: string; mobile?: string };
|
||||||
.OrderGetOrderItemSubsetTasks({
|
lastName?: string;
|
||||||
orderId,
|
firstName?: string;
|
||||||
orderItemId,
|
organisation?: string;
|
||||||
orderItemSubsetId,
|
}) {
|
||||||
completed: new Date(0).toISOString(),
|
const buyer: BuyerDTO = {};
|
||||||
take,
|
|
||||||
skip,
|
if (communicationDetails) {
|
||||||
})
|
buyer.communicationDetails = { ...communicationDetails };
|
||||||
.pipe(
|
}
|
||||||
map((res) =>
|
|
||||||
res.result
|
if (!!lastName || !!firstName) {
|
||||||
.sort((a, b) => new Date(b.completed).getTime() - new Date(a.completed).getTime())
|
buyer.lastName = lastName;
|
||||||
.reduce(
|
buyer.firstName = firstName;
|
||||||
(data, result) => {
|
}
|
||||||
(data[result.name] = data[result.name] || []).push(new Date(result.completed));
|
|
||||||
return data;
|
if (!!organisation && !!buyer.organisation) {
|
||||||
},
|
buyer.organisation = {
|
||||||
{} as Record<string, Date[]>,
|
name: organisation,
|
||||||
),
|
};
|
||||||
),
|
}
|
||||||
);
|
|
||||||
}
|
return this.orderService
|
||||||
}
|
.OrderPatchOrder({
|
||||||
|
orderId: orderId,
|
||||||
|
order: {
|
||||||
|
notificationChannels,
|
||||||
|
buyer,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.pipe(map((res) => res.result));
|
||||||
|
}
|
||||||
|
|
||||||
|
generateNotifications({
|
||||||
|
orderId,
|
||||||
|
taskTypes,
|
||||||
|
}: {
|
||||||
|
orderId: number;
|
||||||
|
taskTypes: string[];
|
||||||
|
}) {
|
||||||
|
return this.orderService.OrderRegenerateOrderItemStatusTasks({
|
||||||
|
orderId,
|
||||||
|
taskTypes,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getCompletedTasks({
|
||||||
|
orderId,
|
||||||
|
orderItemId,
|
||||||
|
orderItemSubsetId,
|
||||||
|
take,
|
||||||
|
skip,
|
||||||
|
}: {
|
||||||
|
orderId: number;
|
||||||
|
orderItemId: number;
|
||||||
|
orderItemSubsetId: number;
|
||||||
|
take?: number;
|
||||||
|
skip?: number;
|
||||||
|
}): Observable<Record<string, Date[]>> {
|
||||||
|
return this.orderService
|
||||||
|
.OrderGetOrderItemSubsetTasks({
|
||||||
|
orderId,
|
||||||
|
orderItemId,
|
||||||
|
orderItemSubsetId,
|
||||||
|
completed: new Date(0).toISOString(),
|
||||||
|
take,
|
||||||
|
skip,
|
||||||
|
})
|
||||||
|
.pipe(
|
||||||
|
map((res) =>
|
||||||
|
res.result
|
||||||
|
.sort(
|
||||||
|
(a, b) =>
|
||||||
|
new Date(b.completed).getTime() -
|
||||||
|
new Date(a.completed).getTime(),
|
||||||
|
)
|
||||||
|
.reduce(
|
||||||
|
(data, result) => {
|
||||||
|
(data[result.name] = data[result.name] || []).push(
|
||||||
|
new Date(result.completed),
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
{} as Record<string, Date[]>,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,22 +1,25 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { ReceiptOrderItemSubsetReferenceValues, ReceiptService } from '@generated/swagger/oms-api';
|
import {
|
||||||
import { memorize } from '@utils/common';
|
ReceiptOrderItemSubsetReferenceValues,
|
||||||
import { shareReplay } from 'rxjs/operators';
|
ReceiptService,
|
||||||
|
} from '@generated/swagger/oms-api';
|
||||||
@Injectable()
|
import { memorize } from '@utils/common';
|
||||||
export class DomainReceiptService {
|
import { shareReplay } from 'rxjs/operators';
|
||||||
constructor(private receiptService: ReceiptService) {}
|
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
createShippingNotes(params: ReceiptService.ReceiptCreateShippingNote2Params) {
|
export class DomainReceiptService {
|
||||||
return this.receiptService.ReceiptCreateShippingNote2(params);
|
constructor(private receiptService: ReceiptService) {}
|
||||||
}
|
|
||||||
|
createShippingNotes(params: ReceiptService.ReceiptCreateShippingNote2Params) {
|
||||||
@memorize({ ttl: 1000 })
|
return this.receiptService.ReceiptCreateShippingNote2(params);
|
||||||
getReceipts(payload: ReceiptOrderItemSubsetReferenceValues) {
|
}
|
||||||
return this.receiptService
|
|
||||||
.ReceiptGetReceiptsByOrderItemSubset({
|
@memorize({ ttl: 1000 })
|
||||||
payload: payload,
|
getReceipts(payload: ReceiptOrderItemSubsetReferenceValues) {
|
||||||
})
|
return this.receiptService
|
||||||
.pipe(shareReplay(1));
|
.ReceiptGetReceiptsByOrderItemSubset({
|
||||||
}
|
payload: payload,
|
||||||
}
|
})
|
||||||
|
.pipe(shareReplay(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
export * from './defs';
|
export * from './defs';
|
||||||
export * from './mappings';
|
export * from './mappings';
|
||||||
export * from './remission.module';
|
|
||||||
export * from './remission.service';
|
export * from './remission.service';
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
import { ModuleWithProviders, NgModule } from '@angular/core';
|
|
||||||
import { DomainRemissionService } from './remission.service';
|
|
||||||
|
|
||||||
@NgModule({})
|
|
||||||
export class DomainRemissionModule {
|
|
||||||
static forRoot(): ModuleWithProviders<DomainRemissionModule> {
|
|
||||||
return {
|
|
||||||
ngModule: RootDomainRemissionModule,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
imports: [],
|
|
||||||
providers: [DomainRemissionService],
|
|
||||||
})
|
|
||||||
export class RootDomainRemissionModule {}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,32 +1,43 @@
|
|||||||
import { enableProdMode, isDevMode } from "@angular/core";
|
import { enableProdMode, isDevMode } from '@angular/core';
|
||||||
import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
|
import { CONFIG_DATA } from '@isa/core/config';
|
||||||
import { CONFIG_DATA } from "@isa/core/config";
|
import { setDefaultOptions } from 'date-fns';
|
||||||
import { setDefaultOptions } from "date-fns";
|
import { de } from 'date-fns/locale';
|
||||||
import { de } from "date-fns/locale";
|
import localeDe from '@angular/common/locales/de';
|
||||||
import * as moment from "moment";
|
import localeDeExtra from '@angular/common/locales/extra/de';
|
||||||
import "moment/locale/de";
|
import * as moment from 'moment';
|
||||||
|
import 'moment/locale/de';
|
||||||
setDefaultOptions({ locale: de });
|
|
||||||
moment.locale("de");
|
setDefaultOptions({ locale: de });
|
||||||
|
moment.locale('de');
|
||||||
import { AppModule } from "./app/app.module";
|
|
||||||
|
registerLocaleData(localeDe, localeDeExtra);
|
||||||
if (!isDevMode()) {
|
registerLocaleData(localeDe, 'de', localeDeExtra);
|
||||||
enableProdMode();
|
|
||||||
}
|
import { App } from './app/app';
|
||||||
|
import { appConfig } from './app/app.config';
|
||||||
async function bootstrap() {
|
import { bootstrapApplication } from '@angular/platform-browser';
|
||||||
const configRes = await fetch("/config/config.json");
|
import { registerLocaleData } from '@angular/common';
|
||||||
|
|
||||||
const config = await configRes.json();
|
if (!isDevMode()) {
|
||||||
|
enableProdMode();
|
||||||
platformBrowserDynamic([
|
}
|
||||||
{ provide: CONFIG_DATA, useValue: config },
|
|
||||||
]).bootstrapModule(AppModule);
|
async function bootstrap() {
|
||||||
}
|
const configRes = await fetch('/config/config.json');
|
||||||
|
|
||||||
try {
|
const config = await configRes.json();
|
||||||
bootstrap();
|
|
||||||
} catch (error) {
|
await bootstrapApplication(App, {
|
||||||
console.error(error);
|
...appConfig,
|
||||||
}
|
providers: [
|
||||||
|
{ provide: CONFIG_DATA, useValue: config },
|
||||||
|
...appConfig.providers,
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
bootstrap();
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export class ModalAvailabilitiesComponent {
|
|||||||
item = this.modalRef.data.item;
|
item = this.modalRef.data.item;
|
||||||
itemId = this.modalRef.data.itemId || this.modalRef.data.item.id;
|
itemId = this.modalRef.data.itemId || this.modalRef.data.item.id;
|
||||||
userbranch$ = combineLatest([
|
userbranch$ = combineLatest([
|
||||||
this.applicationService.getSelectedBranch$(this.applicationService.activatedProcessId),
|
this.applicationService.getSelectedBranch$(),
|
||||||
this.domainAvailabilityService.getDefaultBranch(),
|
this.domainAvailabilityService.getDefaultBranch(),
|
||||||
]).pipe(map(([selectedBranch, defaultBranch]) => selectedBranch || defaultBranch));
|
]).pipe(map(([selectedBranch, defaultBranch]) => selectedBranch || defaultBranch));
|
||||||
|
|
||||||
|
|||||||
@@ -192,11 +192,7 @@ export class ArticleDetailsComponent implements OnInit, OnDestroy {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
selectedBranchId$ = this.applicationService.activatedProcessId$.pipe(
|
selectedBranchId$ = this.applicationService.getSelectedBranch$();
|
||||||
switchMap((processId) =>
|
|
||||||
this.applicationService.getSelectedBranch$(processId),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
get isTablet$() {
|
get isTablet$() {
|
||||||
return this._environment.matchTablet$;
|
return this._environment.matchTablet$;
|
||||||
@@ -328,7 +324,7 @@ export class ArticleDetailsComponent implements OnInit, OnDestroy {
|
|||||||
debounceTime(0),
|
debounceTime(0),
|
||||||
switchMap((params) =>
|
switchMap((params) =>
|
||||||
this.applicationService
|
this.applicationService
|
||||||
.getSelectedBranch$(Number(params.processId))
|
.getSelectedBranch$()
|
||||||
.pipe(map((selectedBranch) => ({ params, selectedBranch }))),
|
.pipe(map((selectedBranch) => ({ params, selectedBranch }))),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -98,11 +98,9 @@ export class ArticleSearchMainComponent implements OnInit, OnDestroy {
|
|||||||
);
|
);
|
||||||
|
|
||||||
this.subscriptions.add(
|
this.subscriptions.add(
|
||||||
this.application.activatedProcessId$
|
this.application
|
||||||
.pipe(
|
.getSelectedBranch$()
|
||||||
debounceTime(0),
|
.pipe(debounceTime(0))
|
||||||
switchMap((processId) => this.application.getSelectedBranch$(processId)),
|
|
||||||
)
|
|
||||||
.subscribe((selectedBranch) => {
|
.subscribe((selectedBranch) => {
|
||||||
const branchChanged = selectedBranch?.id !== this.searchService?.selectedBranch?.id;
|
const branchChanged = selectedBranch?.id !== this.searchService?.selectedBranch?.id;
|
||||||
if (branchChanged) {
|
if (branchChanged) {
|
||||||
@@ -143,7 +141,7 @@ export class ArticleSearchMainComponent implements OnInit, OnDestroy {
|
|||||||
const clean = { ...params };
|
const clean = { ...params };
|
||||||
|
|
||||||
for (const key in clean) {
|
for (const key in clean) {
|
||||||
if (key === 'main_qs' || key?.includes('order_by')) {
|
if (key === 'main_qs') {
|
||||||
clean[key] = undefined;
|
clean[key] = undefined;
|
||||||
} else if (key?.includes('order_by')) {
|
} else if (key?.includes('order_by')) {
|
||||||
delete clean[key];
|
delete clean[key];
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export class SearchResultItemComponent extends ComponentStore<SearchResultItemCo
|
|||||||
|
|
||||||
readonly item$ = this.select((s) => s.item);
|
readonly item$ = this.select((s) => s.item);
|
||||||
|
|
||||||
@Input() selected: boolean = false;
|
@Input() selected = false;
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
get selectable() {
|
get selectable() {
|
||||||
@@ -91,9 +91,7 @@ export class SearchResultItemComponent extends ComponentStore<SearchResultItemCo
|
|||||||
|
|
||||||
defaultBranch$ = this._availability.getDefaultBranch();
|
defaultBranch$ = this._availability.getDefaultBranch();
|
||||||
|
|
||||||
selectedBranchId$ = this.applicationService.activatedProcessId$.pipe(
|
selectedBranchId$ = this.applicationService.getSelectedBranch$();
|
||||||
switchMap((processId) => this.applicationService.getSelectedBranch$(processId)),
|
|
||||||
);
|
|
||||||
|
|
||||||
isOrderBranch$ = combineLatest([this.defaultBranch$, this.selectedBranchId$]).pipe(
|
isOrderBranch$ = combineLatest([this.defaultBranch$, this.selectedBranchId$]).pipe(
|
||||||
map(([defaultBranch, selectedBranch]) => {
|
map(([defaultBranch, selectedBranch]) => {
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ export class ArticleSearchResultsComponent
|
|||||||
.pipe(
|
.pipe(
|
||||||
debounceTime(0),
|
debounceTime(0),
|
||||||
switchMap(([processId, queryParams]) =>
|
switchMap(([processId, queryParams]) =>
|
||||||
this.application.getSelectedBranch$(processId).pipe(
|
this.application.getSelectedBranch$().pipe(
|
||||||
map((selectedBranch) => ({
|
map((selectedBranch) => ({
|
||||||
processId,
|
processId,
|
||||||
queryParams,
|
queryParams,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { CommonModule } from '@angular/common';
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
import { RouterModule } from '@angular/router';
|
import { RouterModule } from '@angular/router';
|
||||||
import { DomainCatalogModule } from '@domain/catalog';
|
import { ThumbnailUrlPipe } from '@domain/catalog';
|
||||||
import { UiCommonModule } from '@ui/common';
|
import { UiCommonModule } from '@ui/common';
|
||||||
import { UiIconModule } from '@ui/icon';
|
import { UiIconModule } from '@ui/icon';
|
||||||
import { UiSelectBulletModule } from '@ui/select-bullet';
|
import { UiSelectBulletModule } from '@ui/select-bullet';
|
||||||
@@ -26,7 +26,7 @@ import { MatomoModule } from 'ngx-matomo-client';
|
|||||||
CommonModule,
|
CommonModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
RouterModule,
|
RouterModule,
|
||||||
DomainCatalogModule,
|
ThumbnailUrlPipe,
|
||||||
UiCommonModule,
|
UiCommonModule,
|
||||||
UiIconModule,
|
UiIconModule,
|
||||||
UiSelectBulletModule,
|
UiSelectBulletModule,
|
||||||
|
|||||||
@@ -77,9 +77,7 @@ export class PageCatalogComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.activatedProcessId$ = this.application.activatedProcessId$.pipe(map((processId) => String(processId)));
|
this.activatedProcessId$ = this.application.activatedProcessId$.pipe(map((processId) => String(processId)));
|
||||||
|
|
||||||
this.selectedBranch$ = this.activatedProcessId$.pipe(
|
this.selectedBranch$ = this.application.getSelectedBranch$();
|
||||||
switchMap((processId) => this.application.getSelectedBranch$(Number(processId))),
|
|
||||||
);
|
|
||||||
|
|
||||||
this.stockTooltipText$ = combineLatest([this.defaultBranch$, this.selectedBranch$]).pipe(
|
this.stockTooltipText$ = combineLatest([this.defaultBranch$, this.selectedBranch$]).pipe(
|
||||||
map(([defaultBranch, selectedBranch]) => {
|
map(([defaultBranch, selectedBranch]) => {
|
||||||
|
|||||||
@@ -1,28 +1,28 @@
|
|||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { BranchSelectorComponent } from '@shared/components/branch-selector';
|
import { BranchSelectorComponent } from '@shared/components/branch-selector';
|
||||||
import { BreadcrumbModule } from '@shared/components/breadcrumb';
|
import { BreadcrumbModule } from '@shared/components/breadcrumb';
|
||||||
import { ArticleDetailsModule } from './article-details/article-details.module';
|
import { ArticleDetailsModule } from './article-details/article-details.module';
|
||||||
import { ArticleSearchModule } from './article-search/article-search.module';
|
import { ArticleSearchModule } from './article-search/article-search.module';
|
||||||
import { PageCatalogRoutingModule } from './page-catalog-routing.module';
|
import { PageCatalogRoutingModule } from './page-catalog-routing.module';
|
||||||
import { PageCatalogComponent } from './page-catalog.component';
|
import { PageCatalogComponent } from './page-catalog.component';
|
||||||
import { SharedSplitscreenComponent } from '@shared/components/splitscreen';
|
import { SharedSplitscreenComponent } from '@shared/components/splitscreen';
|
||||||
import { UiCommonModule } from '@ui/common';
|
import { UiCommonModule } from '@ui/common';
|
||||||
import { UiTooltipModule } from '@ui/tooltip';
|
import { UiTooltipModule } from '@ui/tooltip';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
PageCatalogRoutingModule,
|
PageCatalogRoutingModule,
|
||||||
ArticleSearchModule,
|
ArticleDetailsModule,
|
||||||
ArticleDetailsModule,
|
ArticleSearchModule,
|
||||||
BreadcrumbModule,
|
BreadcrumbModule,
|
||||||
BranchSelectorComponent,
|
BranchSelectorComponent,
|
||||||
SharedSplitscreenComponent,
|
SharedSplitscreenComponent,
|
||||||
UiCommonModule,
|
UiCommonModule,
|
||||||
UiTooltipModule,
|
UiTooltipModule,
|
||||||
],
|
],
|
||||||
exports: [],
|
exports: [],
|
||||||
declarations: [PageCatalogComponent],
|
declarations: [PageCatalogComponent],
|
||||||
})
|
})
|
||||||
export class PageCatalogModule {}
|
export class PageCatalogModule {}
|
||||||
|
|||||||
@@ -73,11 +73,9 @@ export class CustomerOrderSearchMainComponent implements OnInit, OnDestroy {
|
|||||||
);
|
);
|
||||||
|
|
||||||
this._subscriptions.add(
|
this._subscriptions.add(
|
||||||
this._application.activatedProcessId$
|
this._application
|
||||||
.pipe(
|
.getSelectedBranch$()
|
||||||
debounceTime(0),
|
.pipe(debounceTime(0))
|
||||||
switchMap((processId) => this._application.getSelectedBranch$(processId)),
|
|
||||||
)
|
|
||||||
.subscribe((selectedBranch) => {
|
.subscribe((selectedBranch) => {
|
||||||
const branchChanged = selectedBranch?.id !== this._customerOrderSearchStore?.selectedBranch?.id;
|
const branchChanged = selectedBranch?.id !== this._customerOrderSearchStore?.selectedBranch?.id;
|
||||||
if (branchChanged) {
|
if (branchChanged) {
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ export class CustomerOrderSearchResultsComponent
|
|||||||
debounceTime(150),
|
debounceTime(150),
|
||||||
switchMap(([processId, params]) =>
|
switchMap(([processId, params]) =>
|
||||||
this._application
|
this._application
|
||||||
.getSelectedBranch$(processId)
|
.getSelectedBranch$()
|
||||||
.pipe(map((selectedBranch) => ({ processId, params, selectedBranch }))),
|
.pipe(map((selectedBranch) => ({ processId, params, selectedBranch }))),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -49,9 +49,7 @@ export class CustomerOrderComponent implements OnInit, AfterViewInit, OnDestroy
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.selectedBranch$ = this.application.activatedProcessId$.pipe(
|
this.selectedBranch$ = this.application.getSelectedBranch$();
|
||||||
switchMap((processId) => this.application.getSelectedBranch$(Number(processId))),
|
|
||||||
);
|
|
||||||
|
|
||||||
/* Ticket #4544 - Suchrequest abbrechen bei Prozesswechsel
|
/* Ticket #4544 - Suchrequest abbrechen bei Prozesswechsel
|
||||||
/ um zu verhindern, dass die Suche in einen anderen Kundenbestellungen Prozess übernommen wird
|
/ um zu verhindern, dass die Suche in einen anderen Kundenbestellungen Prozess übernommen wird
|
||||||
|
|||||||
@@ -1,110 +1,113 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Icon, IconAlias, IconConfig } from './interfaces';
|
import { Icon, IconAlias, IconConfig } from './interfaces';
|
||||||
import { IconLoader } from './loader';
|
import { IconLoader } from './loader';
|
||||||
import { Observable, Subject, isObservable } from 'rxjs';
|
import { Observable, Subject, isObservable } from 'rxjs';
|
||||||
|
|
||||||
@Injectable()
|
/**
|
||||||
export class IconRegistry {
|
* @deprecated Use UiIconModule from '@isa/ui/icon' instead.
|
||||||
private _icons = new Map<string, Icon>();
|
*/
|
||||||
private _aliases = new Map<string, string>();
|
@Injectable({ providedIn: 'root' })
|
||||||
private _fallback: string;
|
export class IconRegistry {
|
||||||
private _viewBox: string;
|
private _icons = new Map<string, Icon>();
|
||||||
|
private _aliases = new Map<string, string>();
|
||||||
updated = new Subject<void>();
|
private _fallback: string;
|
||||||
|
private _viewBox: string;
|
||||||
private _initComplete = false;
|
|
||||||
|
updated = new Subject<void>();
|
||||||
constructor(private _iconLoader: IconLoader) {
|
|
||||||
this._loadIcons();
|
private _initComplete = false;
|
||||||
}
|
|
||||||
|
constructor(private _iconLoader: IconLoader) {
|
||||||
private async _loadIcons(): Promise<void> {
|
this._loadIcons();
|
||||||
const load = this._iconLoader.getIcons();
|
}
|
||||||
|
|
||||||
if (load instanceof Promise) {
|
private async _loadIcons(): Promise<void> {
|
||||||
const config = await load;
|
const load = this._iconLoader.getIcons();
|
||||||
this._init(config);
|
|
||||||
} else if (isObservable(load)) {
|
if (load instanceof Promise) {
|
||||||
load.subscribe((config) => {
|
const config = await load;
|
||||||
this._init(config);
|
this._init(config);
|
||||||
});
|
} else if (isObservable(load)) {
|
||||||
} else {
|
load.subscribe((config) => {
|
||||||
this._init(load);
|
this._init(config);
|
||||||
}
|
});
|
||||||
}
|
} else {
|
||||||
|
this._init(load);
|
||||||
private _init(config: IconConfig): void {
|
}
|
||||||
this.register(...config.icons);
|
}
|
||||||
this.alias(...config.aliases);
|
|
||||||
this.setViewBox(config.viewBox);
|
private _init(config: IconConfig): void {
|
||||||
this.setFallback(config.fallback);
|
this.register(...config.icons);
|
||||||
|
this.alias(...config.aliases);
|
||||||
this._initComplete = true;
|
this.setViewBox(config.viewBox);
|
||||||
|
this.setFallback(config.fallback);
|
||||||
this.updated.next();
|
|
||||||
}
|
this._initComplete = true;
|
||||||
|
|
||||||
register(...icons: Icon[]): IconRegistry {
|
this.updated.next();
|
||||||
icons?.forEach((icon) => {
|
}
|
||||||
this._icons.set(icon.name, icon);
|
|
||||||
});
|
register(...icons: Icon[]): IconRegistry {
|
||||||
|
icons?.forEach((icon) => {
|
||||||
return this;
|
this._icons.set(icon.name, icon);
|
||||||
}
|
});
|
||||||
|
|
||||||
setViewBox(viewBox: string): void {
|
return this;
|
||||||
this._viewBox = viewBox;
|
}
|
||||||
}
|
|
||||||
|
setViewBox(viewBox: string): void {
|
||||||
alias(...aliases: IconAlias[]): IconRegistry {
|
this._viewBox = viewBox;
|
||||||
aliases?.forEach((alias) => {
|
}
|
||||||
this._aliases.set(alias.alias, alias.name);
|
|
||||||
});
|
alias(...aliases: IconAlias[]): IconRegistry {
|
||||||
|
aliases?.forEach((alias) => {
|
||||||
return this;
|
this._aliases.set(alias.alias, alias.name);
|
||||||
}
|
});
|
||||||
|
|
||||||
setFallback(name: string): void {
|
return this;
|
||||||
this._fallback = name;
|
}
|
||||||
}
|
|
||||||
|
setFallback(name: string): void {
|
||||||
get(name: string): Icon | undefined {
|
this._fallback = name;
|
||||||
const alias = this._aliases.get(name);
|
}
|
||||||
let iconName = name;
|
|
||||||
if (alias) {
|
get(name: string): Icon | undefined {
|
||||||
iconName = alias;
|
const alias = this._aliases.get(name);
|
||||||
}
|
let iconName = name;
|
||||||
|
if (alias) {
|
||||||
let icon = this._icons.get(iconName);
|
iconName = alias;
|
||||||
|
}
|
||||||
if (!icon && this._initComplete) {
|
|
||||||
if (alias) {
|
let icon = this._icons.get(iconName);
|
||||||
console.warn(`Not found: Icon with name ${name} (${iconName})`);
|
|
||||||
} else {
|
if (!icon && this._initComplete) {
|
||||||
console.warn(`Unable to find icon: '${name}'`);
|
if (alias) {
|
||||||
}
|
console.warn(`Not found: Icon with name ${name} (${iconName})`);
|
||||||
}
|
} else {
|
||||||
|
console.warn(`Unable to find icon: '${name}'`);
|
||||||
if (!icon && this._fallback) {
|
}
|
||||||
icon = this._icons.get(this._fallback);
|
}
|
||||||
}
|
|
||||||
|
if (!icon && this._fallback) {
|
||||||
return { ...icon, viewBox: icon?.viewBox || this._viewBox };
|
icon = this._icons.get(this._fallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
get$(name: string): Observable<Icon | undefined> {
|
return { ...icon, viewBox: icon?.viewBox || this._viewBox };
|
||||||
return new Observable<Icon | undefined>((subscriber) => {
|
}
|
||||||
let icon = this.get(name);
|
|
||||||
subscriber.next(icon);
|
get$(name: string): Observable<Icon | undefined> {
|
||||||
subscriber.complete();
|
return new Observable<Icon | undefined>((subscriber) => {
|
||||||
|
let icon = this.get(name);
|
||||||
const sub = this.updated.subscribe(() => {
|
subscriber.next(icon);
|
||||||
icon = this.get(name);
|
subscriber.complete();
|
||||||
subscriber.next(icon);
|
|
||||||
subscriber.complete();
|
const sub = this.updated.subscribe(() => {
|
||||||
});
|
icon = this.get(name);
|
||||||
|
subscriber.next(icon);
|
||||||
return () => sub.unsubscribe();
|
subscriber.complete();
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
return () => sub.unsubscribe();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,66 +1,75 @@
|
|||||||
import {
|
import {
|
||||||
ChangeDetectionStrategy,
|
ChangeDetectionStrategy,
|
||||||
ChangeDetectorRef,
|
ChangeDetectorRef,
|
||||||
Component,
|
Component,
|
||||||
Input,
|
Input,
|
||||||
OnChanges,
|
OnChanges,
|
||||||
OnDestroy,
|
OnDestroy,
|
||||||
OnInit,
|
OnInit,
|
||||||
SimpleChanges,
|
SimpleChanges,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { IconRegistry } from './icon-registry';
|
import { IconRegistry } from './icon-registry';
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
import { takeUntil } from 'rxjs/operators';
|
import { takeUntil } from 'rxjs/operators';
|
||||||
|
|
||||||
@Component({
|
/**
|
||||||
selector: 'shared-icon',
|
* @deprecated Use UiIconModule from '@isa/ui/icon' instead.
|
||||||
template: `
|
*/
|
||||||
<svg [style.width.rem]="size / 16" [style.height.rem]="size / 16" [attr.viewBox]="viewBox">
|
@Component({
|
||||||
<path fill="currentColor" [attr.d]="data" />
|
selector: 'shared-icon',
|
||||||
</svg>
|
template: `
|
||||||
`,
|
<svg
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
[style.width.rem]="size / 16"
|
||||||
standalone: true,
|
[style.height.rem]="size / 16"
|
||||||
})
|
[attr.viewBox]="viewBox"
|
||||||
export class IconComponent implements OnInit, OnDestroy, OnChanges {
|
>
|
||||||
@Input()
|
<path fill="currentColor" [attr.d]="data" />
|
||||||
icon: string;
|
</svg>
|
||||||
|
`,
|
||||||
data: string;
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
standalone: true,
|
||||||
viewBox: string;
|
})
|
||||||
|
export class IconComponent implements OnInit, OnDestroy, OnChanges {
|
||||||
@Input()
|
@Input()
|
||||||
size: number = 24;
|
icon: string;
|
||||||
|
|
||||||
private _onDestroy$ = new Subject<void>();
|
data: string;
|
||||||
|
|
||||||
constructor(
|
viewBox: string;
|
||||||
private readonly _iconRegistry: IconRegistry,
|
|
||||||
private readonly _cdr: ChangeDetectorRef,
|
@Input()
|
||||||
) {}
|
size = 24;
|
||||||
|
|
||||||
ngOnInit(): void {
|
private _onDestroy$ = new Subject<void>();
|
||||||
this._iconRegistry.updated.pipe(takeUntil(this._onDestroy$)).subscribe(() => {
|
|
||||||
this.updateIcon();
|
constructor(
|
||||||
});
|
private readonly _iconRegistry: IconRegistry,
|
||||||
}
|
private readonly _cdr: ChangeDetectorRef,
|
||||||
|
) {}
|
||||||
ngOnDestroy(): void {
|
|
||||||
this._onDestroy$.next();
|
ngOnInit(): void {
|
||||||
this._onDestroy$.complete();
|
this._iconRegistry.updated
|
||||||
}
|
.pipe(takeUntil(this._onDestroy$))
|
||||||
|
.subscribe(() => {
|
||||||
ngOnChanges(changes: SimpleChanges): void {
|
this.updateIcon();
|
||||||
if (changes.icon) {
|
});
|
||||||
this.updateIcon();
|
}
|
||||||
}
|
|
||||||
}
|
ngOnDestroy(): void {
|
||||||
|
this._onDestroy$.next();
|
||||||
updateIcon(): void {
|
this._onDestroy$.complete();
|
||||||
const icon = this._iconRegistry.get(this.icon);
|
}
|
||||||
this.data = icon?.data;
|
|
||||||
this.viewBox = icon?.viewBox;
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
this._cdr.markForCheck();
|
if (changes.icon) {
|
||||||
}
|
this.updateIcon();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateIcon(): void {
|
||||||
|
const icon = this._iconRegistry.get(this.icon);
|
||||||
|
this.data = icon?.data;
|
||||||
|
this.viewBox = icon?.viewBox;
|
||||||
|
this._cdr.markForCheck();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,31 +1,37 @@
|
|||||||
import { NgModule, Provider } from '@angular/core';
|
import { NgModule, Provider } from '@angular/core';
|
||||||
import { IconComponent } from './icon.component';
|
import { IconComponent } from './icon.component';
|
||||||
import { IconLoader, JsonIconLoader } from './loader';
|
import { IconLoader, JsonIconLoader } from './loader';
|
||||||
import { IconRegistry } from './icon-registry';
|
import { IconRegistry } from './icon-registry';
|
||||||
|
|
||||||
export function provideIcon(loaderProvider?: Provider) {
|
/**
|
||||||
const providers: Provider[] = [IconRegistry];
|
* @deprecated Use UiIconModule from '@isa/ui/icon' instead.
|
||||||
if (!loaderProvider) {
|
*/
|
||||||
providers.push({
|
export function provideIcon(loaderProvider?: Provider) {
|
||||||
provide: IconLoader,
|
const providers: Provider[] = [IconRegistry];
|
||||||
useClass: JsonIconLoader,
|
if (!loaderProvider) {
|
||||||
});
|
providers.push({
|
||||||
} else {
|
provide: IconLoader,
|
||||||
providers.push(loaderProvider);
|
useClass: JsonIconLoader,
|
||||||
}
|
});
|
||||||
|
} else {
|
||||||
return providers;
|
providers.push(loaderProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NgModule({
|
return providers;
|
||||||
imports: [IconComponent],
|
}
|
||||||
exports: [IconComponent],
|
|
||||||
})
|
/**
|
||||||
export class IconModule {
|
* @deprecated Use UiIconModule from '@isa/ui/icon' instead.
|
||||||
static forRoot(loaderProvider?: Provider) {
|
*/
|
||||||
return {
|
@NgModule({
|
||||||
ngModule: IconModule,
|
imports: [IconComponent],
|
||||||
providers: provideIcon(loaderProvider),
|
exports: [IconComponent],
|
||||||
};
|
})
|
||||||
}
|
export class IconModule {
|
||||||
}
|
static forRoot(loaderProvider?: Provider) {
|
||||||
|
return {
|
||||||
|
ngModule: IconModule,
|
||||||
|
providers: provideIcon(loaderProvider),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,19 +1,22 @@
|
|||||||
import { Component, ChangeDetectionStrategy, Input } from '@angular/core';
|
import { Component, ChangeDetectionStrategy, Input } from '@angular/core';
|
||||||
|
|
||||||
@Component({
|
/**
|
||||||
selector: 'ui-icon-badge',
|
* @deprecated Use UiIconModule from '@isa/ui/icon' instead.
|
||||||
templateUrl: 'icon-badge.component.html',
|
*/
|
||||||
styleUrls: ['icon-badge.component.scss'],
|
@Component({
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
selector: 'ui-icon-badge',
|
||||||
standalone: false,
|
templateUrl: 'icon-badge.component.html',
|
||||||
})
|
styleUrls: ['icon-badge.component.scss'],
|
||||||
export class UiIconBadgeComponent {
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
@Input()
|
standalone: false,
|
||||||
icon: string;
|
})
|
||||||
|
export class UiIconBadgeComponent {
|
||||||
@Input()
|
@Input()
|
||||||
alt: string;
|
icon: string;
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
area: 'customer' | 'branch' = 'customer';
|
alt: string;
|
||||||
}
|
|
||||||
|
@Input()
|
||||||
|
area: 'customer' | 'branch' = 'customer';
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,59 +1,62 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { SvgIcon } from './defs';
|
import { SvgIcon } from './defs';
|
||||||
import { IconAlias } from './defs/icon-alias';
|
import { IconAlias } from './defs/icon-alias';
|
||||||
|
|
||||||
@Injectable()
|
/**
|
||||||
export class IconRegistry {
|
* @deprecated Use UiIconModule from '@isa/ui/icon' instead.
|
||||||
private _icons = new Map<string, SvgIcon>();
|
*/
|
||||||
private _aliases = new Map<string, string>();
|
@Injectable({ providedIn: 'root' })
|
||||||
private _fallback: string;
|
export class IconRegistry {
|
||||||
private _viewBox: string;
|
private _icons = new Map<string, SvgIcon>();
|
||||||
|
private _aliases = new Map<string, string>();
|
||||||
register(...icons: SvgIcon[]): IconRegistry {
|
private _fallback: string;
|
||||||
icons?.forEach((icon) => {
|
private _viewBox: string;
|
||||||
this._icons.set(icon.name, icon);
|
|
||||||
});
|
register(...icons: SvgIcon[]): IconRegistry {
|
||||||
|
icons?.forEach((icon) => {
|
||||||
return this;
|
this._icons.set(icon.name, icon);
|
||||||
}
|
});
|
||||||
|
|
||||||
setViewBox(viewBox: string): void {
|
return this;
|
||||||
this._viewBox = viewBox;
|
}
|
||||||
}
|
|
||||||
|
setViewBox(viewBox: string): void {
|
||||||
alias(...aliases: IconAlias[]): IconRegistry {
|
this._viewBox = viewBox;
|
||||||
aliases?.forEach((alias) => {
|
}
|
||||||
this._aliases.set(alias.alias, alias.name);
|
|
||||||
});
|
alias(...aliases: IconAlias[]): IconRegistry {
|
||||||
|
aliases?.forEach((alias) => {
|
||||||
return this;
|
this._aliases.set(alias.alias, alias.name);
|
||||||
}
|
});
|
||||||
|
|
||||||
setFallback(name: string): void {
|
return this;
|
||||||
this._fallback = name;
|
}
|
||||||
}
|
|
||||||
|
setFallback(name: string): void {
|
||||||
get(name: string): SvgIcon | undefined {
|
this._fallback = name;
|
||||||
const alias = this._aliases.get(name);
|
}
|
||||||
let iconName = name;
|
|
||||||
if (alias) {
|
get(name: string): SvgIcon | undefined {
|
||||||
iconName = alias;
|
const alias = this._aliases.get(name);
|
||||||
}
|
let iconName = name;
|
||||||
|
if (alias) {
|
||||||
let icon = this._icons.get(iconName);
|
iconName = alias;
|
||||||
|
}
|
||||||
if (!icon) {
|
|
||||||
if (alias) {
|
let icon = this._icons.get(iconName);
|
||||||
console.warn(`Not found: Icon with name ${name} (${iconName})`);
|
|
||||||
} else {
|
if (!icon) {
|
||||||
console.warn(`Unable to find icon: '${name}'`);
|
if (alias) {
|
||||||
}
|
console.warn(`Not found: Icon with name ${name} (${iconName})`);
|
||||||
}
|
} else {
|
||||||
|
console.warn(`Unable to find icon: '${name}'`);
|
||||||
if (!icon && this._fallback) {
|
}
|
||||||
icon = this._icons.get(this._fallback);
|
}
|
||||||
}
|
|
||||||
|
if (!icon && this._fallback) {
|
||||||
return { ...icon, viewBox: icon?.viewBox || this._viewBox };
|
icon = this._icons.get(this._fallback);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
return { ...icon, viewBox: icon?.viewBox || this._viewBox };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,29 +1,39 @@
|
|||||||
import { Component, ChangeDetectionStrategy, Input, Optional, Inject, HostBinding } from '@angular/core';
|
import {
|
||||||
import { UI_ICON_HREF, UI_ICON_VIEW_BOX } from './tokens';
|
Component,
|
||||||
|
ChangeDetectionStrategy,
|
||||||
@Component({
|
Input,
|
||||||
selector: 'ui-icon',
|
Optional,
|
||||||
templateUrl: 'icon.component.html',
|
Inject,
|
||||||
styleUrls: ['icon.component.scss'],
|
HostBinding,
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
} from '@angular/core';
|
||||||
standalone: false,
|
import { UI_ICON_HREF, UI_ICON_VIEW_BOX } from './tokens';
|
||||||
})
|
|
||||||
export class UiIconComponent {
|
/**
|
||||||
@Input()
|
* @deprecated Use UiIconModule from '@isa/ui/icon' instead.
|
||||||
@HostBinding('attr.icon')
|
*/
|
||||||
icon: string;
|
@Component({
|
||||||
|
selector: 'ui-icon',
|
||||||
@Input()
|
templateUrl: 'icon.component.html',
|
||||||
size = '1em';
|
styleUrls: ['icon.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
@Input()
|
standalone: false,
|
||||||
rotate = '0deg';
|
})
|
||||||
|
export class UiIconComponent {
|
||||||
constructor(
|
@Input()
|
||||||
@Optional() @Inject(UI_ICON_HREF) public iconHref: string,
|
@HostBinding('attr.icon')
|
||||||
@Optional() @Inject(UI_ICON_VIEW_BOX) public viewBox: string,
|
icon: string;
|
||||||
) {
|
|
||||||
this.iconHref = this.iconHref || '/assets/icons.svg';
|
@Input()
|
||||||
this.viewBox = this.viewBox || '0 0 32 32';
|
size = '1em';
|
||||||
}
|
|
||||||
}
|
@Input()
|
||||||
|
rotate = '0deg';
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
@Optional() @Inject(UI_ICON_HREF) public iconHref: string,
|
||||||
|
@Optional() @Inject(UI_ICON_VIEW_BOX) public viewBox: string,
|
||||||
|
) {
|
||||||
|
this.iconHref = this.iconHref || '/assets/icons.svg';
|
||||||
|
this.viewBox = this.viewBox || '0 0 32 32';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,38 +1,52 @@
|
|||||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, SimpleChanges } from '@angular/core';
|
import {
|
||||||
import { IconRegistry } from './icon-registry';
|
ChangeDetectionStrategy,
|
||||||
|
ChangeDetectorRef,
|
||||||
@Component({
|
Component,
|
||||||
selector: 'ui-svg-icon',
|
Input,
|
||||||
template: `
|
OnChanges,
|
||||||
<svg [style.width.rem]="size / 16" [style.height.rem]="size / 16" [attr.viewBox]="viewBox">
|
SimpleChanges,
|
||||||
<path fill="currentColor" [attr.d]="data" />
|
} from '@angular/core';
|
||||||
</svg>
|
import { IconRegistry } from './icon-registry';
|
||||||
`,
|
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
/**
|
||||||
standalone: false,
|
* @deprecated Use UiIconModule from '@isa/ui/icon' instead.
|
||||||
})
|
*/
|
||||||
export class UISvgIconComponent implements OnChanges {
|
@Component({
|
||||||
@Input()
|
selector: 'ui-svg-icon',
|
||||||
icon: string;
|
template: `
|
||||||
|
<svg
|
||||||
data: string;
|
[style.width.rem]="size / 16"
|
||||||
|
[style.height.rem]="size / 16"
|
||||||
viewBox: string;
|
[attr.viewBox]="viewBox"
|
||||||
|
>
|
||||||
@Input()
|
<path fill="currentColor" [attr.d]="data" />
|
||||||
size: number = 24;
|
</svg>
|
||||||
|
`,
|
||||||
constructor(
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
private readonly _iconRegistry: IconRegistry,
|
standalone: false,
|
||||||
private readonly _cdr: ChangeDetectorRef,
|
})
|
||||||
) {}
|
export class UISvgIconComponent implements OnChanges {
|
||||||
|
@Input()
|
||||||
ngOnChanges(changes: SimpleChanges): void {
|
icon: string;
|
||||||
if (changes.icon) {
|
|
||||||
const icon = this._iconRegistry.get(this.icon);
|
data: string;
|
||||||
this.data = icon?.data;
|
|
||||||
this.viewBox = icon?.viewBox;
|
viewBox: string;
|
||||||
this._cdr.markForCheck();
|
|
||||||
}
|
@Input()
|
||||||
}
|
size = 24;
|
||||||
}
|
|
||||||
|
constructor(
|
||||||
|
private readonly _iconRegistry: IconRegistry,
|
||||||
|
private readonly _cdr: ChangeDetectorRef,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
|
if (changes.icon) {
|
||||||
|
const icon = this._iconRegistry.get(this.icon);
|
||||||
|
this.data = icon?.data;
|
||||||
|
this.viewBox = icon?.viewBox;
|
||||||
|
this._cdr.markForCheck();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,57 +1,63 @@
|
|||||||
import { ModuleWithProviders, NgModule, Provider } from '@angular/core';
|
import { ModuleWithProviders, NgModule, Provider } from '@angular/core';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { UiIconComponent } from './icon.component';
|
import { UiIconComponent } from './icon.component';
|
||||||
import { UiIconBadgeComponent } from './icon-badge/icon-badge.component';
|
import { UiIconBadgeComponent } from './icon-badge/icon-badge.component';
|
||||||
import { UISvgIconComponent } from './svg-icon.component';
|
import { UISvgIconComponent } from './svg-icon.component';
|
||||||
import { IconRegistry } from './icon-registry';
|
import { IconRegistry } from './icon-registry';
|
||||||
import { UI_ICON_CFG } from './tokens';
|
import { UI_ICON_CFG } from './tokens';
|
||||||
import { UiIconConfig } from './icon-config';
|
import { UiIconConfig } from './icon-config';
|
||||||
|
|
||||||
export function _rootIconRegistryFactory(config: UiIconConfig): IconRegistry {
|
/**
|
||||||
const registry = new IconRegistry();
|
* @deprecated Use UiIconModule from '@isa/ui/icon' instead.
|
||||||
|
*/
|
||||||
if (config?.fallback) {
|
export function _rootIconRegistryFactory(config: UiIconConfig): IconRegistry {
|
||||||
registry.setFallback(config.fallback);
|
const registry = new IconRegistry();
|
||||||
}
|
|
||||||
if (config?.aliases) {
|
if (config?.fallback) {
|
||||||
registry.alias(...config.aliases);
|
registry.setFallback(config.fallback);
|
||||||
}
|
}
|
||||||
if (config?.icons) {
|
if (config?.aliases) {
|
||||||
registry.register(...config.icons);
|
registry.alias(...config.aliases);
|
||||||
}
|
}
|
||||||
|
if (config?.icons) {
|
||||||
if (config?.viewBox) {
|
registry.register(...config.icons);
|
||||||
registry.setViewBox(config.viewBox);
|
}
|
||||||
}
|
|
||||||
|
if (config?.viewBox) {
|
||||||
return registry;
|
registry.setViewBox(config.viewBox);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NgModule({
|
return registry;
|
||||||
imports: [CommonModule],
|
}
|
||||||
declarations: [UiIconComponent, UiIconBadgeComponent, UISvgIconComponent],
|
|
||||||
exports: [UiIconComponent, UiIconBadgeComponent, UISvgIconComponent],
|
/**
|
||||||
})
|
* @deprecated Use UiIconModule from '@isa/ui/icon' instead.
|
||||||
export class UiIconModule {
|
*/
|
||||||
static forRoot(config?: UiIconConfig): ModuleWithProviders<UiIconModule> {
|
@NgModule({
|
||||||
const providers: Provider[] = [
|
imports: [CommonModule],
|
||||||
{
|
declarations: [UiIconComponent, UiIconBadgeComponent, UISvgIconComponent],
|
||||||
provide: IconRegistry,
|
exports: [UiIconComponent, UiIconBadgeComponent, UISvgIconComponent],
|
||||||
useFactory: _rootIconRegistryFactory,
|
})
|
||||||
deps: [UI_ICON_CFG],
|
export class UiIconModule {
|
||||||
},
|
static forRoot(config?: UiIconConfig): ModuleWithProviders<UiIconModule> {
|
||||||
];
|
const providers: Provider[] = [
|
||||||
|
{
|
||||||
if (config) {
|
provide: IconRegistry,
|
||||||
providers.push({
|
useFactory: _rootIconRegistryFactory,
|
||||||
provide: UI_ICON_CFG,
|
deps: [UI_ICON_CFG],
|
||||||
useValue: config,
|
},
|
||||||
});
|
];
|
||||||
}
|
|
||||||
|
if (config) {
|
||||||
return {
|
providers.push({
|
||||||
ngModule: UiIconModule,
|
provide: UI_ICON_CFG,
|
||||||
providers,
|
useValue: config,
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
return {
|
||||||
|
ngModule: UiIconModule,
|
||||||
|
providers,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user