mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-31 09:37:15 +01:00
Merged PR 1887: feat(navigation): add collapsible submenu for remission navigation #5223
feat(navigation): add collapsible submenu for remission navigation #5223 - Convert remission navigation to expandable submenu with arrow toggle - Add 'list' route for remission with redirect from empty path - Separate navigation items for Remission and Warenbegleitscheine - Refactor side-menu component to use inject() pattern - Add remissionExpanded signal to track submenu state
This commit is contained in:
committed by
Nino Righi
parent
598df7d5ed
commit
76ff54dd3a
@@ -201,12 +201,17 @@ const routes: Routes = [
|
||||
).then((m) => m.routes),
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
path: 'list',
|
||||
loadChildren: () =>
|
||||
import('@isa/remission/feature/remission-list').then(
|
||||
(m) => m.routes,
|
||||
),
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
redirectTo: 'list',
|
||||
pathMatch: 'full',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
@@ -283,37 +283,69 @@
|
||||
<span class="side-menu-group-item-label">Wareneingang</span>
|
||||
</a>
|
||||
}
|
||||
|
||||
<a
|
||||
class="side-menu-group-item"
|
||||
(click)="closeSideMenu(); focusSearchBox()"
|
||||
[routerLink]="[
|
||||
'/',
|
||||
processService.activatedTab()?.id || processService.nextId(),
|
||||
'remission',
|
||||
]"
|
||||
(isActiveChange)="focusSearchBox()"
|
||||
>
|
||||
<span class="side-menu-group-item-icon w-[2.375rem] h-12">
|
||||
<ng-icon name="isaNavigationRemission2"></ng-icon>
|
||||
</span>
|
||||
<span class="side-menu-group-item-label">Remission</span>
|
||||
</a>
|
||||
<a
|
||||
class="side-menu-group-item"
|
||||
(click)="closeSideMenu(); focusSearchBox()"
|
||||
[routerLink]="[
|
||||
'/',
|
||||
processService.activatedTab()?.id || processService.nextId(),
|
||||
'remission',
|
||||
'return-receipt',
|
||||
]"
|
||||
(isActiveChange)="focusSearchBox()"
|
||||
>
|
||||
<span class="side-menu-group-item-icon w-[2.375rem] h-12">
|
||||
<ng-icon name="isaNavigationRemission2"></ng-icon>
|
||||
</span>
|
||||
<span class="side-menu-group-item-label">Warenbegleitscheine</span>
|
||||
</a>
|
||||
<div class="side-menu-group-sub-item-wrapper">
|
||||
<a
|
||||
class="side-menu-group-item"
|
||||
(click)="closeSideMenu(); focusSearchBox()"
|
||||
[routerLink]="[
|
||||
'/',
|
||||
processService.activatedTab()?.id || processService.nextId(),
|
||||
'remission',
|
||||
]"
|
||||
(isActiveChange)="focusSearchBox(); remissionExpanded.set($event)"
|
||||
routerLinkActive="active"
|
||||
#rlActive="routerLinkActive"
|
||||
>
|
||||
<span class="side-menu-group-item-icon w-[2.375rem] h-12">
|
||||
<ng-icon name="isaNavigationRemission2" size="1.5rem"></ng-icon>
|
||||
</span>
|
||||
<span class="side-menu-group-item-label">Remission</span>
|
||||
<button
|
||||
class="side-menu-group-arrow"
|
||||
[class.side-menu-item-rotate]="remissionExpanded()"
|
||||
(click)="
|
||||
$event.stopPropagation();
|
||||
$event.preventDefault();
|
||||
remissionExpanded.set(!remissionExpanded())
|
||||
"
|
||||
>
|
||||
<shared-icon icon="keyboard-arrow-down"></shared-icon>
|
||||
</button>
|
||||
</a>
|
||||
@if (remissionExpanded()) {
|
||||
<div class="side-menu-group-sub-items">
|
||||
<a
|
||||
class="side-menu-group-item"
|
||||
(click)="closeSideMenu(); focusSearchBox()"
|
||||
[routerLink]="[
|
||||
'/',
|
||||
processService.activatedTab()?.id || processService.nextId(),
|
||||
'remission',
|
||||
'list',
|
||||
]"
|
||||
(isActiveChange)="focusSearchBox()"
|
||||
routerLinkActive="active"
|
||||
>
|
||||
<span class="side-menu-group-item-icon"> </span>
|
||||
<span class="side-menu-group-item-label">Remission</span>
|
||||
</a>
|
||||
<a
|
||||
class="side-menu-group-item"
|
||||
(click)="closeSideMenu(); focusSearchBox()"
|
||||
[routerLink]="[
|
||||
'/',
|
||||
processService.activatedTab()?.id || processService.nextId(),
|
||||
'remission',
|
||||
'return-receipt',
|
||||
]"
|
||||
(isActiveChange)="focusSearchBox()"
|
||||
routerLinkActive="active"
|
||||
>
|
||||
<span class="side-menu-group-item-icon"> </span>
|
||||
<span class="side-menu-group-item-label">Warenbegleitscheine</span>
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
@@ -1,435 +1,434 @@
|
||||
import {
|
||||
Component,
|
||||
ChangeDetectionStrategy,
|
||||
Inject,
|
||||
ChangeDetectorRef,
|
||||
inject,
|
||||
DOCUMENT,
|
||||
} from '@angular/core';
|
||||
import { AuthModule, AuthService } from '@core/auth';
|
||||
import { StockService } from '@generated/swagger/wws-api';
|
||||
import { first, map, retry, switchMap, take } from 'rxjs/operators';
|
||||
import { ShellService } from '../shell.service';
|
||||
import { ApplicationProcess, ApplicationService } from '@core/application';
|
||||
import { ActivatedRoute, Router, RouterModule } from '@angular/router';
|
||||
import { EnvironmentService } from '@core/environment';
|
||||
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Config } from '@core/config';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { IconComponent } from '@shared/components/icon';
|
||||
import { RegexRouterLinkActiveDirective } from '@shared/directives/router-link-active';
|
||||
import { WrongDestinationModalService } from '@modal/wrong-destination';
|
||||
import {
|
||||
CustomerCreateNavigation,
|
||||
CustomerOrdersNavigationService,
|
||||
CustomerSearchNavigation,
|
||||
PickupShelfInNavigationService,
|
||||
PickUpShelfOutNavigationService,
|
||||
ProductCatalogNavigationService,
|
||||
} from '@shared/services/navigation';
|
||||
|
||||
import { TabService } from '@isa/core/tabs';
|
||||
import { NgIconComponent, provideIcons } from '@ng-icons/core';
|
||||
import { isaNavigationRemission2, isaNavigationReturn } from '@isa/icons';
|
||||
|
||||
@Component({
|
||||
selector: 'shell-side-menu',
|
||||
templateUrl: 'side-menu.component.html',
|
||||
styleUrls: ['side-menu.component.css'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [
|
||||
CommonModule,
|
||||
NgIconComponent,
|
||||
IconComponent,
|
||||
RouterModule,
|
||||
AuthModule,
|
||||
RegexRouterLinkActiveDirective,
|
||||
],
|
||||
providers: [provideIcons({ isaNavigationReturn, isaNavigationRemission2 })],
|
||||
})
|
||||
export class ShellSideMenuComponent {
|
||||
processService = inject(TabService);
|
||||
|
||||
branchKey$ = this._stockService.StockCurrentBranch().pipe(
|
||||
retry(3),
|
||||
map((x) => x.result.key),
|
||||
);
|
||||
|
||||
section$ = this._app.getSection$();
|
||||
|
||||
processes$ = this.section$.pipe(
|
||||
switchMap((section) => this._app.getProcesses$(section)),
|
||||
);
|
||||
|
||||
processesCount$ = this.processes$.pipe(
|
||||
map((processes) => processes?.length ?? 0),
|
||||
);
|
||||
|
||||
activeProcess$ = this._app.activatedProcessId$.pipe(
|
||||
switchMap((processId) => this._app.getProcessById$(processId)),
|
||||
);
|
||||
|
||||
get isTablet() {
|
||||
return this._environment.matchTablet();
|
||||
}
|
||||
|
||||
customerBasePath$ = this.activeProcess$.pipe(
|
||||
map((process) => {
|
||||
if (
|
||||
!!process &&
|
||||
process.section === 'customer' &&
|
||||
process.type !== 'cart-checkout'
|
||||
) {
|
||||
// Übernehme aktiven Prozess
|
||||
return `/kunde/${process.id}`;
|
||||
} else {
|
||||
// Über Guards wird ein neuer Prozess erstellt
|
||||
return '/kunde';
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
customerSearchRoute$ = this.getLastActivatedCustomerProcessId$().pipe(
|
||||
map((processId) => {
|
||||
return this._customerSearchNavigation.defaultRoute({ processId });
|
||||
}),
|
||||
);
|
||||
|
||||
customerCreateRoute$ = this.getLastActivatedCustomerProcessId$().pipe(
|
||||
map((processId) => {
|
||||
return this._customerCreateNavigation.defaultRoute({ processId });
|
||||
}),
|
||||
);
|
||||
|
||||
pickUpShelfOutRoutePath$ = this.getLastActivatedCustomerProcessId$().pipe(
|
||||
map((processId) => {
|
||||
if (processId) {
|
||||
// Übernehme aktiven Prozess
|
||||
return this._pickUpShelfOutNavigation.defaultRoute({ processId }).path;
|
||||
} else {
|
||||
// Über Guards wird ein neuer Prozess erstellt
|
||||
return this._pickUpShelfOutNavigation.defaultRoute({}).path;
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
productRoutePath$ = this.getLastActivatedCustomerProcessId$().pipe(
|
||||
map((processId) => {
|
||||
if (processId) {
|
||||
// Übernehme aktiven Prozess
|
||||
return this._catalogNavigationService.getArticleSearchBasePath(
|
||||
processId,
|
||||
).path;
|
||||
} else {
|
||||
// Über Guards wird ein neuer Prozess erstellt
|
||||
return this._catalogNavigationService.getArticleSearchBasePath().path;
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
customerOrdersRoutePath$ = this.getLastActivatedCustomerProcessId$().pipe(
|
||||
map((processId) => {
|
||||
if (processId) {
|
||||
// Übernehme aktiven Prozess
|
||||
return this._customerOrdersNavigationService.getCustomerOrdersBasePath(
|
||||
processId,
|
||||
).path;
|
||||
} else {
|
||||
// Über Guards wird ein neuer Prozess erstellt
|
||||
return this._customerOrdersNavigationService.getCustomerOrdersBasePath()
|
||||
.path;
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
taskCalenderNavigation$ = this.getLastNavigationByProcessId(
|
||||
this._config.get('process.ids.taskCalendar'),
|
||||
{
|
||||
path: ['/filiale', 'task-calendar'],
|
||||
queryParams: {},
|
||||
},
|
||||
'/filiale/task-calendar',
|
||||
);
|
||||
|
||||
assortmentNavigation$ = this.getLastNavigationByProcessId(
|
||||
this._config.get('process.ids.assortment'),
|
||||
{
|
||||
path: ['/filiale', 'assortment'],
|
||||
queryParams: {},
|
||||
},
|
||||
);
|
||||
|
||||
pickUpShelfInRoutePath$ = this.getLastNavigationByProcessId(
|
||||
this._config.get('process.ids.pickupShelf'),
|
||||
this._pickUpShelfInNavigation.defaultRoute(),
|
||||
'/filiale/pickup-shelf',
|
||||
);
|
||||
|
||||
// #4478 - RD // Abholfach - Routing löst Suche aus
|
||||
// pickUpShelfInListRoutePath$ = this.getLastNavigationByProcessId(
|
||||
// this._config.get('process.ids.pickupShelf'),
|
||||
// this._pickUpShelfInNavigation.listRoute()
|
||||
// );
|
||||
|
||||
remissionNavigation$ = this.getLastNavigationByProcessId(
|
||||
this._config.get('process.ids.remission'),
|
||||
{
|
||||
path: ['/filiale', 'remission'],
|
||||
queryParams: {},
|
||||
},
|
||||
);
|
||||
|
||||
packageInspectionNavigation$ = this.getLastNavigationByProcessId(
|
||||
this._config.get('process.ids.packageInspection'),
|
||||
{
|
||||
path: ['/filiale', 'package-inspection'],
|
||||
queryParams: {},
|
||||
},
|
||||
);
|
||||
|
||||
get currentShelfView$() {
|
||||
return this._route.queryParams.pipe(map((params) => params.view));
|
||||
}
|
||||
|
||||
shelfExpanded = false;
|
||||
customerExpanded = false;
|
||||
|
||||
constructor(
|
||||
private _shellService: ShellService,
|
||||
private _authService: AuthService,
|
||||
private _stockService: StockService,
|
||||
private _app: ApplicationService,
|
||||
private _router: Router,
|
||||
private _route: ActivatedRoute,
|
||||
private readonly _wrongDestinationModalService: WrongDestinationModalService,
|
||||
private _environment: EnvironmentService,
|
||||
private _catalogNavigationService: ProductCatalogNavigationService,
|
||||
private _customerOrdersNavigationService: CustomerOrdersNavigationService,
|
||||
private _config: Config,
|
||||
private _breadcrumbService: BreadcrumbService,
|
||||
private _customerSearchNavigation: CustomerSearchNavigation,
|
||||
private _customerCreateNavigation: CustomerCreateNavigation,
|
||||
private _pickUpShelfOutNavigation: PickUpShelfOutNavigationService,
|
||||
private _pickUpShelfInNavigation: PickupShelfInNavigationService,
|
||||
private _cdr: ChangeDetectorRef,
|
||||
@Inject(DOCUMENT) private readonly _document: Document,
|
||||
) {}
|
||||
|
||||
customerActive(isActive: boolean) {
|
||||
if (isActive) {
|
||||
this.expandCustomer();
|
||||
}
|
||||
}
|
||||
|
||||
shelfActive(isActive: boolean) {
|
||||
if (isActive) {
|
||||
this.expandShelf();
|
||||
}
|
||||
}
|
||||
|
||||
expandCustomer() {
|
||||
this.customerExpanded = true;
|
||||
this._cdr.markForCheck();
|
||||
}
|
||||
|
||||
expandShelf() {
|
||||
this.shelfExpanded = true;
|
||||
this._cdr.markForCheck();
|
||||
}
|
||||
|
||||
getLastNavigationByProcessId(
|
||||
id: number,
|
||||
fallback?: { path: string[]; queryParams: unknown },
|
||||
pathContainsString?: string,
|
||||
) {
|
||||
return this._breadcrumbService.getBreadcrumbByKey$(id)?.pipe(
|
||||
map((breadcrumbs) => {
|
||||
const lastCrumb = breadcrumbs
|
||||
.filter((breadcrumb) => {
|
||||
/**
|
||||
* #4532 - Der optionale Filter wurde hinzugefügt Breadcrumbs mit fehlerhaften Pfad auszuschließen.
|
||||
* Dieser Filter kann entfernt werden, sobald die Breadcrumbs korrekt gesetzt werden. Jedoch konnte man bisher nicht feststellen,
|
||||
* woher die fehlerhaften Breadcrumbs kommen.
|
||||
*/
|
||||
if (!pathContainsString) {
|
||||
// Wenn kein Filter gesetzt ist, dann wird der letzte Breadcrumb zurückgegeben
|
||||
return true;
|
||||
}
|
||||
|
||||
const pathStr = Array.isArray(breadcrumb.path)
|
||||
? breadcrumb.path.join('/')
|
||||
: breadcrumb.path;
|
||||
return pathStr.includes(pathContainsString);
|
||||
})
|
||||
// eslint-disable-next-line no-prototype-builtins
|
||||
.filter((breadcrumb) => !breadcrumb?.params?.hasOwnProperty('view'))
|
||||
.filter((breadcrumb) => !breadcrumb?.tags?.includes('reservation'))
|
||||
.filter((breadcrumb) => !breadcrumb?.tags?.includes('cleanup'))
|
||||
.filter(
|
||||
(breadcrumb) => !breadcrumb?.tags?.includes('wareneingangsliste'),
|
||||
)
|
||||
.filter((breadcrumb) => !breadcrumb?.tags?.includes('preview'))
|
||||
.reduce((last, current) => {
|
||||
if (!last) return current;
|
||||
|
||||
if (last.changed > current.changed) {
|
||||
return last;
|
||||
} else {
|
||||
return current;
|
||||
}
|
||||
}, undefined);
|
||||
|
||||
if (!lastCrumb) {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
// #4692 Return Fallback if Values contain undefined or null values, regardless if path is from type string or array
|
||||
if (typeof lastCrumb?.path === 'string') {
|
||||
if (
|
||||
lastCrumb?.path?.includes('undefined') ||
|
||||
lastCrumb?.path?.includes('null')
|
||||
) {
|
||||
return fallback;
|
||||
}
|
||||
} else {
|
||||
const valuesToCheck = [];
|
||||
|
||||
// eslint-disable-next-line no-unsafe-optional-chaining
|
||||
for (const value of lastCrumb?.path) {
|
||||
if (
|
||||
value?.outlets &&
|
||||
value?.outlets?.primary &&
|
||||
value?.outlets?.side
|
||||
) {
|
||||
valuesToCheck.push(
|
||||
...Object.values(value?.outlets?.primary),
|
||||
...Object.values(value?.outlets?.side),
|
||||
);
|
||||
} else {
|
||||
valuesToCheck.push(value);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.checkIfArrayContainsUndefinedOrNull(valuesToCheck)) {
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
|
||||
return { path: lastCrumb.path, queryParams: lastCrumb.params };
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
checkIfArrayContainsUndefinedOrNull(array: unknown[]) {
|
||||
return (
|
||||
array?.includes(undefined) ||
|
||||
array?.includes('undefined') ||
|
||||
array?.includes(null) ||
|
||||
array?.includes('null')
|
||||
);
|
||||
}
|
||||
|
||||
getLastActivatedCustomerProcessId$() {
|
||||
return this._app.getProcesses$('customer').pipe(
|
||||
map((processes) => {
|
||||
const lastCustomerProcess = processes
|
||||
.filter((process) => process.type === 'cart')
|
||||
.reduce((last, current) => {
|
||||
if (!last) return current;
|
||||
|
||||
if (last.activated > current.activated) {
|
||||
return last;
|
||||
} else {
|
||||
return current;
|
||||
}
|
||||
}, undefined);
|
||||
|
||||
return lastCustomerProcess?.id ?? Date.now();
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
closeSideMenu() {
|
||||
this._shellService.closeSideMenu();
|
||||
}
|
||||
|
||||
logout() {
|
||||
this._authService.logout();
|
||||
}
|
||||
|
||||
async resetBranch() {
|
||||
const process = await this.activeProcess$.pipe(first()).toPromise();
|
||||
if (process?.id) {
|
||||
this._app.patchProcessData(process.id, { selectedBranch: undefined });
|
||||
}
|
||||
}
|
||||
|
||||
focusSearchBox() {
|
||||
setTimeout(() => this._document.getElementById('searchbox')?.focus(), 0);
|
||||
}
|
||||
|
||||
async createProcess() {
|
||||
const process = await this.createCartProcess();
|
||||
this.navigateToCatalog(process);
|
||||
}
|
||||
|
||||
async createCartProcess() {
|
||||
const nextProcessName = await this.getNextProcessName();
|
||||
|
||||
const process: ApplicationProcess = {
|
||||
id: this.getNextProcessId(),
|
||||
type: 'cart',
|
||||
name: nextProcessName,
|
||||
section: 'customer',
|
||||
closeable: true,
|
||||
};
|
||||
|
||||
this._app.createProcess(process);
|
||||
|
||||
return process;
|
||||
}
|
||||
|
||||
async getNextProcessName() {
|
||||
let processes = await this._app
|
||||
.getProcesses$('customer')
|
||||
.pipe(first())
|
||||
.toPromise();
|
||||
|
||||
processes = processes.filter(
|
||||
(x) => x.type === 'cart' && x.name.startsWith('Vorgang '),
|
||||
);
|
||||
|
||||
const maxProcessNumber = processes.reduce((max, process) => {
|
||||
const number = parseInt(process.name.replace('Vorgang ', ''), 10);
|
||||
return number > max ? number : max;
|
||||
}, 0);
|
||||
|
||||
return `Vorgang ${maxProcessNumber + 1}`;
|
||||
}
|
||||
|
||||
getNextProcessId() {
|
||||
return Date.now();
|
||||
}
|
||||
|
||||
async navigateToCatalog(process: ApplicationProcess) {
|
||||
await this._catalogNavigationService
|
||||
.getArticleSearchBasePath(process.id)
|
||||
.navigate();
|
||||
}
|
||||
|
||||
navigateToDashboard() {
|
||||
this._router.navigate(['/kunde', 'dashboard']);
|
||||
}
|
||||
|
||||
async closeAllProcesses() {
|
||||
const processes = await this.processes$.pipe(take(1)).toPromise();
|
||||
|
||||
processes.forEach((process) => this._app.removeProcess(process.id));
|
||||
|
||||
this.navigateToDashboard();
|
||||
}
|
||||
|
||||
fetchAndOpenPackages = () =>
|
||||
this._wrongDestinationModalService.fetchAndOpen();
|
||||
}
|
||||
import {
|
||||
Component,
|
||||
ChangeDetectionStrategy,
|
||||
Inject,
|
||||
ChangeDetectorRef,
|
||||
inject,
|
||||
DOCUMENT,
|
||||
signal,
|
||||
} from '@angular/core';
|
||||
import { AuthModule, AuthService } from '@core/auth';
|
||||
import { StockService } from '@generated/swagger/wws-api';
|
||||
import { first, map, retry, switchMap, take } from 'rxjs/operators';
|
||||
import { ShellService } from '../shell.service';
|
||||
import { ApplicationProcess, ApplicationService } from '@core/application';
|
||||
import { ActivatedRoute, Router, RouterModule } from '@angular/router';
|
||||
import { EnvironmentService } from '@core/environment';
|
||||
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Config } from '@core/config';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { IconComponent } from '@shared/components/icon';
|
||||
import { RegexRouterLinkActiveDirective } from '@shared/directives/router-link-active';
|
||||
import { WrongDestinationModalService } from '@modal/wrong-destination';
|
||||
import {
|
||||
CustomerCreateNavigation,
|
||||
CustomerOrdersNavigationService,
|
||||
CustomerSearchNavigation,
|
||||
PickupShelfInNavigationService,
|
||||
PickUpShelfOutNavigationService,
|
||||
ProductCatalogNavigationService,
|
||||
} from '@shared/services/navigation';
|
||||
|
||||
import { TabService } from '@isa/core/tabs';
|
||||
import { NgIconComponent, provideIcons } from '@ng-icons/core';
|
||||
import { isaNavigationRemission2, isaNavigationReturn } from '@isa/icons';
|
||||
|
||||
@Component({
|
||||
selector: 'shell-side-menu',
|
||||
templateUrl: 'side-menu.component.html',
|
||||
styleUrls: ['side-menu.component.css'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [
|
||||
CommonModule,
|
||||
NgIconComponent,
|
||||
IconComponent,
|
||||
RouterModule,
|
||||
AuthModule,
|
||||
RegexRouterLinkActiveDirective,
|
||||
],
|
||||
providers: [provideIcons({ isaNavigationReturn, isaNavigationRemission2 })],
|
||||
})
|
||||
export class ShellSideMenuComponent {
|
||||
#shellService = inject(ShellService);
|
||||
#authService = inject(AuthService);
|
||||
#stockService = inject(StockService);
|
||||
#app = inject(ApplicationService);
|
||||
#router = inject(Router);
|
||||
#route = inject(ActivatedRoute);
|
||||
#wrongDestinationModalService = inject(WrongDestinationModalService);
|
||||
#environment = inject(EnvironmentService);
|
||||
#catalogNavigationService = inject(ProductCatalogNavigationService);
|
||||
#customerOrdersNavigationService = inject(CustomerOrdersNavigationService);
|
||||
#config = inject(Config);
|
||||
#breadcrumbService = inject(BreadcrumbService);
|
||||
#customerSearchNavigation = inject(CustomerSearchNavigation);
|
||||
#customerCreateNavigation = inject(CustomerCreateNavigation);
|
||||
#pickUpShelfOutNavigation = inject(PickUpShelfOutNavigationService);
|
||||
#pickUpShelfInNavigation = inject(PickupShelfInNavigationService);
|
||||
#cdr = inject(ChangeDetectorRef);
|
||||
#document = inject(DOCUMENT);
|
||||
processService = inject(TabService);
|
||||
|
||||
branchKey$ = this.#stockService.StockCurrentBranch().pipe(
|
||||
retry(3),
|
||||
map((x) => x.result.key),
|
||||
);
|
||||
|
||||
section$ = this.#app.getSection$();
|
||||
|
||||
processes$ = this.section$.pipe(
|
||||
switchMap((section) => this.#app.getProcesses$(section)),
|
||||
);
|
||||
|
||||
processesCount$ = this.processes$.pipe(
|
||||
map((processes) => processes?.length ?? 0),
|
||||
);
|
||||
|
||||
activeProcess$ = this.#app.activatedProcessId$.pipe(
|
||||
switchMap((processId) => this.#app.getProcessById$(processId)),
|
||||
);
|
||||
|
||||
get isTablet() {
|
||||
return this.#environment.matchTablet();
|
||||
}
|
||||
|
||||
customerBasePath$ = this.activeProcess$.pipe(
|
||||
map((process) => {
|
||||
if (
|
||||
!!process &&
|
||||
process.section === 'customer' &&
|
||||
process.type !== 'cart-checkout'
|
||||
) {
|
||||
// Übernehme aktiven Prozess
|
||||
return `/kunde/${process.id}`;
|
||||
} else {
|
||||
// Über Guards wird ein neuer Prozess erstellt
|
||||
return '/kunde';
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
customerSearchRoute$ = this.getLastActivatedCustomerProcessId$().pipe(
|
||||
map((processId) => {
|
||||
return this.#customerSearchNavigation.defaultRoute({ processId });
|
||||
}),
|
||||
);
|
||||
|
||||
customerCreateRoute$ = this.getLastActivatedCustomerProcessId$().pipe(
|
||||
map((processId) => {
|
||||
return this.#customerCreateNavigation.defaultRoute({ processId });
|
||||
}),
|
||||
);
|
||||
|
||||
pickUpShelfOutRoutePath$ = this.getLastActivatedCustomerProcessId$().pipe(
|
||||
map((processId) => {
|
||||
if (processId) {
|
||||
// Übernehme aktiven Prozess
|
||||
return this.#pickUpShelfOutNavigation.defaultRoute({ processId }).path;
|
||||
} else {
|
||||
// Über Guards wird ein neuer Prozess erstellt
|
||||
return this.#pickUpShelfOutNavigation.defaultRoute({}).path;
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
productRoutePath$ = this.getLastActivatedCustomerProcessId$().pipe(
|
||||
map((processId) => {
|
||||
if (processId) {
|
||||
// Übernehme aktiven Prozess
|
||||
return this.#catalogNavigationService.getArticleSearchBasePath(
|
||||
processId,
|
||||
).path;
|
||||
} else {
|
||||
// Über Guards wird ein neuer Prozess erstellt
|
||||
return this.#catalogNavigationService.getArticleSearchBasePath().path;
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
customerOrdersRoutePath$ = this.getLastActivatedCustomerProcessId$().pipe(
|
||||
map((processId) => {
|
||||
if (processId) {
|
||||
// Übernehme aktiven Prozess
|
||||
return this.#customerOrdersNavigationService.getCustomerOrdersBasePath(
|
||||
processId,
|
||||
).path;
|
||||
} else {
|
||||
// Über Guards wird ein neuer Prozess erstellt
|
||||
return this.#customerOrdersNavigationService.getCustomerOrdersBasePath()
|
||||
.path;
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
taskCalenderNavigation$ = this.getLastNavigationByProcessId(
|
||||
this.#config.get('process.ids.taskCalendar'),
|
||||
{
|
||||
path: ['/filiale', 'task-calendar'],
|
||||
queryParams: {},
|
||||
},
|
||||
'/filiale/task-calendar',
|
||||
);
|
||||
|
||||
assortmentNavigation$ = this.getLastNavigationByProcessId(
|
||||
this.#config.get('process.ids.assortment'),
|
||||
{
|
||||
path: ['/filiale', 'assortment'],
|
||||
queryParams: {},
|
||||
},
|
||||
);
|
||||
|
||||
pickUpShelfInRoutePath$ = this.getLastNavigationByProcessId(
|
||||
this.#config.get('process.ids.pickupShelf'),
|
||||
this.#pickUpShelfInNavigation.defaultRoute(),
|
||||
'/filiale/pickup-shelf',
|
||||
);
|
||||
|
||||
// #4478 - RD // Abholfach - Routing löst Suche aus
|
||||
// pickUpShelfInListRoutePath$ = this.getLastNavigationByProcessId(
|
||||
// this._config.get('process.ids.pickupShelf'),
|
||||
// this._pickUpShelfInNavigation.listRoute()
|
||||
// );
|
||||
|
||||
remissionNavigation$ = this.getLastNavigationByProcessId(
|
||||
this.#config.get('process.ids.remission'),
|
||||
{
|
||||
path: ['/filiale', 'remission'],
|
||||
queryParams: {},
|
||||
},
|
||||
);
|
||||
|
||||
packageInspectionNavigation$ = this.getLastNavigationByProcessId(
|
||||
this.#config.get('process.ids.packageInspection'),
|
||||
{
|
||||
path: ['/filiale', 'package-inspection'],
|
||||
queryParams: {},
|
||||
},
|
||||
);
|
||||
|
||||
get currentShelfView$() {
|
||||
return this.#route.queryParams.pipe(map((params) => params.view));
|
||||
}
|
||||
|
||||
shelfExpanded = false;
|
||||
customerExpanded = false;
|
||||
remissionExpanded = signal(false);
|
||||
|
||||
customerActive(isActive: boolean) {
|
||||
if (isActive) {
|
||||
this.expandCustomer();
|
||||
}
|
||||
}
|
||||
|
||||
shelfActive(isActive: boolean) {
|
||||
if (isActive) {
|
||||
this.expandShelf();
|
||||
}
|
||||
}
|
||||
|
||||
expandCustomer() {
|
||||
this.customerExpanded = true;
|
||||
this.#cdr.markForCheck();
|
||||
}
|
||||
|
||||
expandShelf() {
|
||||
this.shelfExpanded = true;
|
||||
this.#cdr.markForCheck();
|
||||
}
|
||||
|
||||
getLastNavigationByProcessId(
|
||||
id: number,
|
||||
fallback?: { path: string[]; queryParams: unknown },
|
||||
pathContainsString?: string,
|
||||
) {
|
||||
return this.#breadcrumbService.getBreadcrumbByKey$(id)?.pipe(
|
||||
map((breadcrumbs) => {
|
||||
const lastCrumb = breadcrumbs
|
||||
.filter((breadcrumb) => {
|
||||
/**
|
||||
* #4532 - Der optionale Filter wurde hinzugefügt Breadcrumbs mit fehlerhaften Pfad auszuschließen.
|
||||
* Dieser Filter kann entfernt werden, sobald die Breadcrumbs korrekt gesetzt werden. Jedoch konnte man bisher nicht feststellen,
|
||||
* woher die fehlerhaften Breadcrumbs kommen.
|
||||
*/
|
||||
if (!pathContainsString) {
|
||||
// Wenn kein Filter gesetzt ist, dann wird der letzte Breadcrumb zurückgegeben
|
||||
return true;
|
||||
}
|
||||
|
||||
const pathStr = Array.isArray(breadcrumb.path)
|
||||
? breadcrumb.path.join('/')
|
||||
: breadcrumb.path;
|
||||
return pathStr.includes(pathContainsString);
|
||||
})
|
||||
// eslint-disable-next-line no-prototype-builtins
|
||||
.filter((breadcrumb) => !breadcrumb?.params?.hasOwnProperty('view'))
|
||||
.filter((breadcrumb) => !breadcrumb?.tags?.includes('reservation'))
|
||||
.filter((breadcrumb) => !breadcrumb?.tags?.includes('cleanup'))
|
||||
.filter(
|
||||
(breadcrumb) => !breadcrumb?.tags?.includes('wareneingangsliste'),
|
||||
)
|
||||
.filter((breadcrumb) => !breadcrumb?.tags?.includes('preview'))
|
||||
.reduce((last, current) => {
|
||||
if (!last) return current;
|
||||
|
||||
if (last.changed > current.changed) {
|
||||
return last;
|
||||
} else {
|
||||
return current;
|
||||
}
|
||||
}, undefined);
|
||||
|
||||
if (!lastCrumb) {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
// #4692 Return Fallback if Values contain undefined or null values, regardless if path is from type string or array
|
||||
if (typeof lastCrumb?.path === 'string') {
|
||||
if (
|
||||
lastCrumb?.path?.includes('undefined') ||
|
||||
lastCrumb?.path?.includes('null')
|
||||
) {
|
||||
return fallback;
|
||||
}
|
||||
} else {
|
||||
const valuesToCheck = [];
|
||||
|
||||
// eslint-disable-next-line no-unsafe-optional-chaining
|
||||
for (const value of lastCrumb?.path) {
|
||||
if (
|
||||
value?.outlets &&
|
||||
value?.outlets?.primary &&
|
||||
value?.outlets?.side
|
||||
) {
|
||||
valuesToCheck.push(
|
||||
...Object.values(value?.outlets?.primary),
|
||||
...Object.values(value?.outlets?.side),
|
||||
);
|
||||
} else {
|
||||
valuesToCheck.push(value);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.checkIfArrayContainsUndefinedOrNull(valuesToCheck)) {
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
|
||||
return { path: lastCrumb.path, queryParams: lastCrumb.params };
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
checkIfArrayContainsUndefinedOrNull(array: unknown[]) {
|
||||
return (
|
||||
array?.includes(undefined) ||
|
||||
array?.includes('undefined') ||
|
||||
array?.includes(null) ||
|
||||
array?.includes('null')
|
||||
);
|
||||
}
|
||||
|
||||
getLastActivatedCustomerProcessId$() {
|
||||
return this.#app.getProcesses$('customer').pipe(
|
||||
map((processes) => {
|
||||
const lastCustomerProcess = processes
|
||||
.filter((process) => process.type === 'cart')
|
||||
.reduce((last, current) => {
|
||||
if (!last) return current;
|
||||
|
||||
if (last.activated > current.activated) {
|
||||
return last;
|
||||
} else {
|
||||
return current;
|
||||
}
|
||||
}, undefined);
|
||||
|
||||
return lastCustomerProcess?.id ?? Date.now();
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
closeSideMenu() {
|
||||
this.#shellService.closeSideMenu();
|
||||
}
|
||||
|
||||
logout() {
|
||||
this.#authService.logout();
|
||||
}
|
||||
|
||||
async resetBranch() {
|
||||
const process = await this.activeProcess$.pipe(first()).toPromise();
|
||||
if (process?.id) {
|
||||
this.#app.patchProcessData(process.id, { selectedBranch: undefined });
|
||||
}
|
||||
}
|
||||
|
||||
focusSearchBox() {
|
||||
setTimeout(() => this.#document.getElementById('searchbox')?.focus(), 0);
|
||||
}
|
||||
|
||||
async createProcess() {
|
||||
const process = await this.createCartProcess();
|
||||
this.navigateToCatalog(process);
|
||||
}
|
||||
|
||||
async createCartProcess() {
|
||||
const nextProcessName = await this.getNextProcessName();
|
||||
|
||||
const process: ApplicationProcess = {
|
||||
id: this.getNextProcessId(),
|
||||
type: 'cart',
|
||||
name: nextProcessName,
|
||||
section: 'customer',
|
||||
closeable: true,
|
||||
};
|
||||
|
||||
this.#app.createProcess(process);
|
||||
|
||||
return process;
|
||||
}
|
||||
|
||||
async getNextProcessName() {
|
||||
let processes = await this.#app
|
||||
.getProcesses$('customer')
|
||||
.pipe(first())
|
||||
.toPromise();
|
||||
|
||||
processes = processes.filter(
|
||||
(x) => x.type === 'cart' && x.name.startsWith('Vorgang '),
|
||||
);
|
||||
|
||||
const maxProcessNumber = processes.reduce((max, process) => {
|
||||
const number = parseInt(process.name.replace('Vorgang ', ''), 10);
|
||||
return number > max ? number : max;
|
||||
}, 0);
|
||||
|
||||
return `Vorgang ${maxProcessNumber + 1}`;
|
||||
}
|
||||
|
||||
getNextProcessId() {
|
||||
return Date.now();
|
||||
}
|
||||
|
||||
async navigateToCatalog(process: ApplicationProcess) {
|
||||
await this.#catalogNavigationService
|
||||
.getArticleSearchBasePath(process.id)
|
||||
.navigate();
|
||||
}
|
||||
|
||||
navigateToDashboard() {
|
||||
this.#router.navigate(['/kunde', 'dashboard']);
|
||||
}
|
||||
|
||||
async closeAllProcesses() {
|
||||
const processes = await this.processes$.pipe(take(1)).toPromise();
|
||||
|
||||
processes.forEach((process) => this.#app.removeProcess(process.id));
|
||||
|
||||
this.navigateToDashboard();
|
||||
}
|
||||
|
||||
fetchAndOpenPackages = () =>
|
||||
this.#wrongDestinationModalService.fetchAndOpen();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user