mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
Compare commits
120 Commits
feature/43
...
bugfix/453
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c43ddef51c | ||
|
|
9dbb251254 | ||
|
|
1e776f8d14 | ||
|
|
d5254cc150 | ||
|
|
adc5a5a280 | ||
|
|
f0b653fd0f | ||
|
|
14eba6e5ea | ||
|
|
0777f2c910 | ||
|
|
5073693fc2 | ||
|
|
f3cb6236a5 | ||
|
|
4ab9890313 | ||
|
|
a8cd6ce844 | ||
|
|
3bba23cc76 | ||
|
|
e25f176a7b | ||
|
|
a169d2a4e9 | ||
|
|
6a9caa432e | ||
|
|
389948c077 | ||
|
|
e7724ed8b9 | ||
|
|
c7f1b27fdf | ||
|
|
135f0255b8 | ||
|
|
65a7aa569d | ||
|
|
211eaa6175 | ||
|
|
8097c6ad9e | ||
|
|
b0d76b01d7 | ||
|
|
626fd0081f | ||
|
|
362fca74bc | ||
|
|
b8f0a29f79 | ||
|
|
f54400f00d | ||
|
|
54094695b1 | ||
|
|
1e3e9588da | ||
|
|
f04705b659 | ||
|
|
c22672fad0 | ||
|
|
59673a47db | ||
|
|
e56ea0bd4e | ||
|
|
f8c4d4a842 | ||
|
|
c4dd9214a3 | ||
|
|
4d74b3a89e | ||
|
|
b0b3fd40ce | ||
|
|
3404c930c5 | ||
|
|
abcd940ed3 | ||
|
|
c1756942b2 | ||
|
|
ea4d036066 | ||
|
|
101a34bd3f | ||
|
|
99bad149cb | ||
|
|
a0bff7164c | ||
|
|
0c4a4130b9 | ||
|
|
8dd1211729 | ||
|
|
a2f1b8b624 | ||
|
|
98a331ffe5 | ||
|
|
c0f97c9bae | ||
|
|
960ffa165f | ||
|
|
6bdfbe2eff | ||
|
|
80342e61ac | ||
|
|
6bf3894e4d | ||
|
|
aab29838bf | ||
|
|
856ca5651e | ||
|
|
a7d4b8d7fb | ||
|
|
62d260473c | ||
|
|
bde52a2526 | ||
|
|
6243b03cfc | ||
|
|
d24841800e | ||
|
|
f60628c769 | ||
|
|
034f697da5 | ||
|
|
ec9f80767b | ||
|
|
a5e569cf05 | ||
|
|
b62259f9b4 | ||
|
|
95baeaa8a8 | ||
|
|
a5b9115a91 | ||
|
|
1885c58d86 | ||
|
|
add55a47d6 | ||
|
|
129f49a9ee | ||
|
|
9560eb7ad6 | ||
|
|
772ba29a8e | ||
|
|
8ac8f6cc1f | ||
|
|
a0f496475c | ||
|
|
8979a388ee | ||
|
|
8b9a209c49 | ||
|
|
f4c3e3ceee | ||
|
|
5ca8a83f25 | ||
|
|
006011885f | ||
|
|
9c9e061f6d | ||
|
|
c9782a7d29 | ||
|
|
59de82def8 | ||
|
|
dc2617bb5d | ||
|
|
d80e621563 | ||
|
|
63c02e4605 | ||
|
|
3f93fe0869 | ||
|
|
9011f76e95 | ||
|
|
dd88e4ad3e | ||
|
|
a0869aa4a5 | ||
|
|
1107264d7c | ||
|
|
31512546d3 | ||
|
|
183e7b6945 | ||
|
|
fba465d573 | ||
|
|
dd6784e3b3 | ||
|
|
9caa0fc0fa | ||
|
|
1102fb4608 | ||
|
|
012cc6ac67 | ||
|
|
72de7efc1d | ||
|
|
b8c7bbec88 | ||
|
|
608513b6dc | ||
|
|
fa78eca087 | ||
|
|
77fda0f939 | ||
|
|
81d210a77b | ||
|
|
5ab4456040 | ||
|
|
2db45c900a | ||
|
|
705dc23908 | ||
|
|
9def487ab8 | ||
|
|
50e08f115a | ||
|
|
b15693a914 | ||
|
|
cbf23b6f30 | ||
|
|
fc45efb4af | ||
|
|
b4fbcd6d16 | ||
|
|
c54e5c27ae | ||
|
|
bb81b8f826 | ||
|
|
486e2e5a28 | ||
|
|
86d3b4e3f5 | ||
|
|
b440ddbe82 | ||
|
|
d6e0d92132 | ||
|
|
b16ffa4352 |
@@ -2,11 +2,16 @@ import { Injectable, inject } from '@angular/core';
|
||||
import { AbholfachService, AutocompleteTokenDTO, ListResponseArgsOfDBHOrderItemListItemDTO, QueryTokenDTO } from '@swagger/oms';
|
||||
import { PickupShelfIOService } from './pickup-shelf-io.service';
|
||||
import { Observable, throwError } from 'rxjs';
|
||||
import { Filter } from '@shared/components/filter';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class PickupShelfInService extends PickupShelfIOService {
|
||||
private _abholfachService = inject(AbholfachService);
|
||||
|
||||
name() {
|
||||
return 'PickupShelfInService';
|
||||
}
|
||||
|
||||
getQuerySettings() {
|
||||
return this._abholfachService.AbholfachWareneingangQuerySettings();
|
||||
}
|
||||
@@ -22,6 +27,7 @@ export class PickupShelfInService extends PickupShelfIOService {
|
||||
getOrderItemsByOrderNumberOrCompartmentCode(args: {
|
||||
orderNumber?: string;
|
||||
compartmentCode?: string;
|
||||
filter?: Filter;
|
||||
}): Observable<ListResponseArgsOfDBHOrderItemListItemDTO> {
|
||||
if (!args.orderNumber && !args.compartmentCode) {
|
||||
return throwError(
|
||||
@@ -29,13 +35,16 @@ export class PickupShelfInService extends PickupShelfIOService {
|
||||
);
|
||||
}
|
||||
|
||||
const { orderdate } = args.filter?.getQueryToken()?.filter ?? {};
|
||||
|
||||
return this._abholfachService.AbholfachWareneingang({
|
||||
input: {
|
||||
qs: args.compartmentCode ?? args.orderNumber,
|
||||
},
|
||||
filter: {
|
||||
archive: String(true),
|
||||
all_branches: String(true),
|
||||
archive: String(false),
|
||||
orderdate,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Filter } from '@shared/components/filter';
|
||||
import {
|
||||
AutocompleteTokenDTO,
|
||||
ListResponseArgsOfDBHOrderItemListItemDTO,
|
||||
@@ -10,6 +11,8 @@ import { Observable } from 'rxjs';
|
||||
|
||||
@Injectable()
|
||||
export abstract class PickupShelfIOService {
|
||||
abstract name(): string;
|
||||
|
||||
abstract getQuerySettings(): Observable<ResponseArgsOfQuerySettingsDTO>;
|
||||
|
||||
abstract search(queryToken: QueryTokenDTO): Observable<ListResponseArgsOfDBHOrderItemListItemDTO>;
|
||||
@@ -19,6 +22,7 @@ export abstract class PickupShelfIOService {
|
||||
abstract getOrderItemsByOrderNumberOrCompartmentCode(args: {
|
||||
orderNumber?: string;
|
||||
compartmentCode?: string;
|
||||
filter?: Filter;
|
||||
}): Observable<ListResponseArgsOfDBHOrderItemListItemDTO>;
|
||||
|
||||
abstract getOrderItemsByCustomerNumber(args: { customerNumber: string }): Observable<ListResponseArgsOfDBHOrderItemListItemDTO>;
|
||||
|
||||
@@ -2,11 +2,16 @@ import { Injectable, inject } from '@angular/core';
|
||||
import { AbholfachService, AutocompleteTokenDTO, ListResponseArgsOfDBHOrderItemListItemDTO, QueryTokenDTO } from '@swagger/oms';
|
||||
import { PickupShelfIOService } from './pickup-shelf-io.service';
|
||||
import { Observable, throwError } from 'rxjs';
|
||||
import { Filter } from '@shared/components/filter';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class PickupShelfOutService extends PickupShelfIOService {
|
||||
private _abholfachService = inject(AbholfachService);
|
||||
|
||||
name() {
|
||||
return 'PickupShelfOutService';
|
||||
}
|
||||
|
||||
getQuerySettings() {
|
||||
return this._abholfachService.AbholfachWarenausgabeQuerySettings();
|
||||
}
|
||||
@@ -22,6 +27,7 @@ export class PickupShelfOutService extends PickupShelfIOService {
|
||||
getOrderItemsByOrderNumberOrCompartmentCode(args: {
|
||||
orderNumber?: string;
|
||||
compartmentCode?: string;
|
||||
filter?: Filter;
|
||||
}): Observable<ListResponseArgsOfDBHOrderItemListItemDTO> {
|
||||
if (!args.orderNumber && !args.compartmentCode) {
|
||||
return throwError(
|
||||
@@ -29,13 +35,16 @@ export class PickupShelfOutService extends PickupShelfIOService {
|
||||
);
|
||||
}
|
||||
|
||||
const { orderdate } = args.filter?.getQueryToken()?.filter ?? {};
|
||||
|
||||
return this._abholfachService.AbholfachWarenausgabe({
|
||||
input: {
|
||||
qs: args.compartmentCode ?? args.orderNumber,
|
||||
},
|
||||
filter: {
|
||||
archive: String(true),
|
||||
all_branches: String(true),
|
||||
archive: String(false),
|
||||
orderdate,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -15,11 +15,12 @@ export class CanActivateCustomerOrdersWithProcessIdGuard {
|
||||
.toPromise();
|
||||
|
||||
if (!process) {
|
||||
const processes = await this._applicationService.getProcesses$('customer').pipe(first()).toPromise();
|
||||
await this._applicationService.createProcess({
|
||||
id: +route.params.processId,
|
||||
type: 'customer-order',
|
||||
type: 'cart',
|
||||
section: 'customer',
|
||||
name: `Kundenbestellungen`,
|
||||
name: `Vorgang ${this.processNumber(processes.filter((process) => process.type === 'cart'))}`,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -46,6 +47,18 @@ export class CanActivateCustomerOrdersWithProcessIdGuard {
|
||||
|
||||
processNumber(processes: ApplicationProcess[]) {
|
||||
const processNumbers = processes?.map((process) => Number(process?.name?.replace(/\D/g, '')));
|
||||
return !!processNumbers && processNumbers?.length > 0 ? Math.max(...processNumbers) + 1 : 1;
|
||||
return !!processNumbers && processNumbers.length > 0 ? this.findMissingNumber(processNumbers) : 1;
|
||||
}
|
||||
|
||||
findMissingNumber(processNumbers: number[]) {
|
||||
// Ticket #3272 Bei Klick auf "+" bzw. neuen Prozess hinzufügen soll der neue Tab immer die höchste Nummer haben (wie aktuell im Produktiv)
|
||||
// ----------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
// for (let missingNumber = 1; missingNumber < Math.max(...processNumbers); missingNumber++) {
|
||||
// if (!processNumbers.find((number) => number === missingNumber)) {
|
||||
// return missingNumber;
|
||||
// }
|
||||
// }
|
||||
return Math.max(...processNumbers) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ export class CanActivateGoodsInGuard {
|
||||
id: this._config.get('process.ids.goodsIn'),
|
||||
type: 'goods-in',
|
||||
section: 'branch',
|
||||
name: 'Abholfach',
|
||||
name: '',
|
||||
});
|
||||
}
|
||||
this._applicationService.activateProcess(this._config.get('process.ids.goodsIn'));
|
||||
|
||||
@@ -415,6 +415,10 @@
|
||||
{
|
||||
"name": "isa-box-out",
|
||||
"alias": "Versandbestellung (oder gemischt)"
|
||||
},
|
||||
{
|
||||
"name": "package-variant-closed",
|
||||
"alias": "Bestellung ohne Konto"
|
||||
},{
|
||||
"name": "person",
|
||||
"alias": "Onlinekonto"
|
||||
|
||||
@@ -1,3 +1,386 @@
|
||||
<div class="page-article-details__wrapper">
|
||||
<div #detailsContainer class="page-article-details__container px-5" *ngIf="store.item$ | async; let item">
|
||||
<div class="page-article-details__product-details mb-3">
|
||||
<div class="page-article-details__product-bookmark justify-self-end">
|
||||
<div *ngIf="showArchivBadge$ | async" class="archiv-badge">
|
||||
<button [uiOverlayTrigger]="archivTooltip" class="p-0 m-0 outline-none border-none bg-transparent relative -top-[0.3125rem]">
|
||||
<img src="/assets/images/bookmark_benachrichtigung_archiv.svg" alt="Archiv Badge" />
|
||||
<ui-tooltip yPosition="above" xPosition="after" [yOffset]="-11" [xOffset]="-8" #archivTooltip [closeable]="true">
|
||||
<ng-container *ngIf="isAvailable$ | async; else notAvailable">
|
||||
Archivtitel. Wird nicht mehr gedruckt. Artikel ist bestellbar, weil lieferbar.
|
||||
</ng-container>
|
||||
<ng-template #notAvailable>
|
||||
Archivtitel. Wird nicht mehr gedruckt. Nicht bestellbar.
|
||||
</ng-template>
|
||||
</ui-tooltip>
|
||||
</button>
|
||||
</div>
|
||||
<div *ngIf="showSubscriptionBadge$ | async">
|
||||
<button
|
||||
[uiOverlayTrigger]="subscribtionTooltip"
|
||||
class="p-0 m-0 outline-none border-none bg-transparent relative -top-[0.3125rem]"
|
||||
>
|
||||
<img src="/assets/images/bookmark_subscription.svg" alt="Fortsetzungsartikel Badge" />
|
||||
</button>
|
||||
<ui-tooltip yPosition="above" xPosition="after" [yOffset]="-11" [xOffset]="-8" #subscribtionTooltip [closeable]="true"
|
||||
>Artikel ist ein Fortsetzungsartikel,<br />
|
||||
Artikel muss über eine Aboabteilung<br />
|
||||
bestellt werden.
|
||||
</ui-tooltip>
|
||||
</div>
|
||||
<div *ngIf="showPromotionBadge$ | async" class="promotion-badge">
|
||||
<button [uiOverlayTrigger]="promotionTooltip" class="p-0 m-0 outline-none border-none bg-transparent relative -top-[0.3125rem]">
|
||||
<ui-icon-badge icon="gift" alt="Prämienkatalog Badge"></ui-icon-badge>
|
||||
<ui-tooltip yPosition="above" xPosition="after" [yOffset]="-11" [xOffset]="-8" #promotionTooltip [closeable]="true">
|
||||
Dieser Artikel befindet sich im Prämienkatalog.
|
||||
</ui-tooltip>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="page-article-details__product-image-recessions flex flex-col items-center">
|
||||
<div class="page-article-details__product-image">
|
||||
<button class="border-none outline-none bg-transparent relative" (click)="showImages()">
|
||||
<img
|
||||
class="max-h-[19.6875rem] max-w-[12.1875rem] rounded"
|
||||
(load)="loadImage()"
|
||||
[src]="item.imageId | productImage: 195:315:true"
|
||||
alt="product image"
|
||||
/>
|
||||
<ui-icon
|
||||
class="absolute text-[#A7B9CB] inline-block bottom-[0.875rem] right-[1.125rem]"
|
||||
*ngIf="imageLoaded$ | async"
|
||||
icon="search_add"
|
||||
size="25px"
|
||||
></ui-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<button
|
||||
(click)="showReviews()"
|
||||
class="page-article-details__product-recessions flex flex-col mt-2 items-center bg-transparent border-none outline-none"
|
||||
*ngIf="item.reviews?.length > 0"
|
||||
>
|
||||
<ui-stars [rating]="store.reviewRating$ | async"></ui-stars>
|
||||
|
||||
<div class="text-p2 text-[#0556B4] font-bold">{{ item.reviews.length }} Rezensionen</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="page-article-details__product-contributors">
|
||||
<a
|
||||
*ngFor="let contributor of contributors$ | async; let last = last"
|
||||
class="text-[#0556B4] font-semibold no-underline text-p2"
|
||||
[routerLink]="resultsPath"
|
||||
[queryParams]="{ main_qs: contributor, main_author: 'author' }"
|
||||
>
|
||||
{{ contributor }}{{ last ? '' : ';' }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="page-article-details__product-print justify-self-end" [class.mt-4]="isBadgeVisible$ | async">
|
||||
<button class="bg-transparent text-brand font-bold text-lg outline-none border-none p-0" (click)="print()">Drucken</button>
|
||||
</div>
|
||||
|
||||
<div class="page-article-details__product-title text-h3 font-bold mb-6">
|
||||
{{ item.product?.name }}
|
||||
</div>
|
||||
|
||||
<div class="page-article-details__product-misc flex flex-col mb-4">
|
||||
<div
|
||||
class="page-article-details__product-format flex items-center font-bold text-p3"
|
||||
*ngIf="item?.product?.format && item?.product?.formatDetail"
|
||||
>
|
||||
<img
|
||||
*ngIf="item?.product?.format !== '--'"
|
||||
class="flex mr-2 h-[1.125rem]"
|
||||
[src]="'/assets/images/Icon_' + item.product?.format + '.svg'"
|
||||
[alt]="item.product?.formatDetail"
|
||||
/>
|
||||
{{ item.product?.formatDetail }}
|
||||
</div>
|
||||
|
||||
<div class="page-article-details__product-volume" *ngIf="item?.product?.volume">Band/Reihe {{ item?.product?.volume }}</div>
|
||||
|
||||
<div class="page-article-details__product-publication">{{ publicationDate$ | async }}</div>
|
||||
</div>
|
||||
|
||||
<div class="page-article-details__product-price-info flex flex-col mb-4 flex-nowrap self-end">
|
||||
<div class="page-article-details__product-price font-bold text-xl self-end" *ngIf="price$ | async; let price">
|
||||
{{ price?.value?.value | currency: price?.value?.currency:'code' }}
|
||||
</div>
|
||||
<div *ngIf="price$ | async; let price" class="page-article-details__product-price-bound self-end">
|
||||
{{ price?.vat?.vatType | vat: (priceMaintained$ | async) }}
|
||||
</div>
|
||||
<div class="page-article-details__product-points self-end" *ngIf="store.promotionPoints$ | async; let promotionPoints">
|
||||
{{ promotionPoints }} Lesepunkte
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="page-article-details__product-origin-infos flex flex-col mb-4">
|
||||
<div class="page-article-details__product-manufacturer" data-name="product-manufacturer">{{ item.product?.manufacturer }}</div>
|
||||
|
||||
<div class="page-article-details__product-language" *ngIf="item?.product?.locale" data-name="product-language">
|
||||
{{ item?.product?.locale }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="page-article-details__product-stock flex justify-end items-center">
|
||||
<div class="h-5 w-16 bg-[#e6eff9] animate-[load_0.75s_linear_infinite]" *ngIf="store.fetchingTakeAwayAvailability$ | async"></div>
|
||||
<button
|
||||
class="flex flex-row py-4 pl-4"
|
||||
type="button"
|
||||
[uiOverlayTrigger]="tooltip"
|
||||
[overlayTriggerDisabled]="!(stockTooltipText$ | async)"
|
||||
(click)="showTooltip()"
|
||||
*ngIf="!(store.fetchingTakeAwayAvailability$ | async)"
|
||||
>
|
||||
<ng-container *ngIf="store.takeAwayAvailability$ | async; let takeAwayAvailability">
|
||||
<ui-icon class="mr-2 mb-1" icon="home" size="15px"></ui-icon>
|
||||
<span class="font-bold text-p3">{{ takeAwayAvailability.inStock || 0 }}x</span>
|
||||
</ng-container>
|
||||
</button>
|
||||
<ui-tooltip #tooltip yPosition="above" xPosition="after" [yOffset]="-12" [closeable]="true">
|
||||
{{ stockTooltipText$ | async }}
|
||||
</ui-tooltip>
|
||||
</div>
|
||||
|
||||
<div class="page-article-details__product-ean-specs flex flex-col">
|
||||
<div class="page-article-details__product-ean" data-name="product-ean">{{ item.product?.ean }}</div>
|
||||
|
||||
<div class="page-article-details__product-specs">
|
||||
<ng-container *ngIf="item?.specs?.length > 0">
|
||||
{{ (item?.specs)[0]?.value }}
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="page-article-details__product-availabilities flex flex-row items-center justify-end mt-4">
|
||||
<div
|
||||
class="h-5 w-6 bg-[#e6eff9] animate-[load_0.75s_linear_infinite]"
|
||||
*ngIf="store.fetchingTakeAwayAvailability$ | async; else showAvailabilityTakeAwayIcon"
|
||||
></div>
|
||||
<ng-template #showAvailabilityTakeAwayIcon>
|
||||
<div
|
||||
*ngIf="store.isTakeAwayAvailabilityAvailable$ | async"
|
||||
class="w-[2.25rem] h-[2.25rem] bg-[#D8DFE5] rounded-[5px_5px_0px_5px] flex items-center justify-center"
|
||||
>
|
||||
<ui-icon class="mx-1" icon="shopping_bag" size="18px"> </ui-icon>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<div
|
||||
class="h-5 w-6 bg-[#e6eff9] animate-[load_0.75s_linear_infinite]"
|
||||
*ngIf="store.fetchingPickUpAvailability$ | async; else showAvailabilityPickUpIcon"
|
||||
></div>
|
||||
<ng-template #showAvailabilityPickUpIcon>
|
||||
<div
|
||||
#uiOverlayTrigger="uiOverlayTrigger"
|
||||
[uiOverlayTrigger]="orderDeadlineTooltip"
|
||||
*ngIf="store.isPickUpAvailabilityAvailable$ | async"
|
||||
class="page-article-details__product-pick-up-availability w-[2.25rem] h-[2.25rem] cursor-pointer bg-[#D8DFE5] rounded-[5px_5px_0px_5px] flex items-center justify-center ml-3"
|
||||
[class.tooltip-active]="uiOverlayTrigger.opened"
|
||||
>
|
||||
<shared-icon icon="isa-box-out" [size]="24"></shared-icon>
|
||||
</div>
|
||||
|
||||
<ui-tooltip [warning]="true" yPosition="above" xPosition="after" [yOffset]="-12" #orderDeadlineTooltip [closeable]="true">
|
||||
<b>{{ (store.pickUpAvailability$ | async)?.orderDeadline | orderDeadline }}</b>
|
||||
</ui-tooltip>
|
||||
</ng-template>
|
||||
|
||||
<div
|
||||
class="h-5 w-6 bg-[#e6eff9] animate-[load_0.75s_linear_infinite]"
|
||||
*ngIf="store.fetchingDeliveryAvailability$ | async; else showAvailabilityDeliveryIcon"
|
||||
></div>
|
||||
<ng-template #showAvailabilityDeliveryIcon>
|
||||
<div
|
||||
*ngIf="showDeliveryTruck$ | async"
|
||||
class="w-[2.25rem] h-[2.25rem] bg-[#D8DFE5] rounded-[5px_5px_0px_5px] flex items-center justify-center ml-3"
|
||||
>
|
||||
<ui-icon class="-mb-[0.3125rem] -mt-[0.3125rem] mx-1" icon="truck" size="30px"></ui-icon>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<div
|
||||
class="h-5 w-6 bg-[#e6eff9] animate-[load_0.75s_linear_infinite]"
|
||||
*ngIf="store.fetchingDeliveryB2BAvailability$ | async; else showAvailabilityDeliveryB2BIcon"
|
||||
></div>
|
||||
<ng-template #showAvailabilityDeliveryB2BIcon>
|
||||
<div
|
||||
*ngIf="showDeliveryB2BTruck$ | async"
|
||||
class="w-[2.25rem] h-[2.25rem] bg-[#D8DFE5] rounded-[5px_5px_0px_5px] flex items-center justify-center ml-3"
|
||||
>
|
||||
<ui-icon class="-mb-[0.625rem] -mt-[0.625rem] mx-1" icon="truck_b2b" size="30px"> </ui-icon>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<span *ngIf="store.isDownload$ | async" class="flex flex-row items-center">
|
||||
<div class="w-[2.25rem] h-[2.25rem] bg-[#D8DFE5] rounded-[5px_5px_0px_5px] flex items-center justify-center ml-3">
|
||||
<ui-icon class="mx-1" icon="download" size="18px"></ui-icon>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="page-article-details__shelf-ssc">
|
||||
<div class="page-article-details__ssc flex justify-end my-2 font-bold text-lg">
|
||||
<div class="w-52 h-5 bg-[#e6eff9] animate-[load_0.75s_linear_infinite]" *ngIf="fetchingAvailabilities$ | async"></div>
|
||||
<ng-container *ngIf="!(fetchingAvailabilities$ | async)">
|
||||
<div class="text-right" *ngIf="store.sscText$ | async; let sscText">
|
||||
{{ sscText }}
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<div class="page-article-details__shelfinfo text-right" *ngIf="store.isDownload$ | async">
|
||||
<ng-container
|
||||
*ngIf="
|
||||
item?.stockInfos && item?.shelfInfos && (item?.stockInfos)[0]?.compartment && (item?.shelfInfos)[0]?.label;
|
||||
else stockInfos
|
||||
"
|
||||
>
|
||||
<span data-name="compartment">
|
||||
{{ (item?.stockInfos)[0]?.compartment }}
|
||||
</span>
|
||||
/
|
||||
<br />
|
||||
<span data-name="shelf-info-label">
|
||||
{{ (item?.shelfInfos)[0]?.label }}
|
||||
</span>
|
||||
</ng-container>
|
||||
<ng-template #stockInfos>
|
||||
<ng-container *ngIf="item?.stockInfos && (item?.stockInfos)[0]?.compartment; else shelfInfos">
|
||||
{{ (item?.stockInfos)[0]?.compartment }}
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
<ng-template #shelfInfos>
|
||||
<ng-container *ngIf="item?.shelfInfos && (item?.shelfInfos)[0]?.label">
|
||||
<span data-name="shelf-info-label">{{ (item?.shelfInfos)[0]?.label }}</span>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
</div>
|
||||
<div class="page-article-details__shelfinfo text-right" *ngIf="!(store.isDownload$ | async)">
|
||||
<ng-container
|
||||
*ngIf="
|
||||
item?.stockInfos && item?.shelfInfos && (item?.stockInfos)[0]?.compartment && (item?.shelfInfos)[0]?.label;
|
||||
else stockInfos2
|
||||
"
|
||||
>
|
||||
<span data-name="compartment">{{ (item?.stockInfos)[0]?.compartment }}</span>
|
||||
/
|
||||
<br />
|
||||
<span data-name="shelf-info-label">{{ (item?.shelfInfos)[0]?.label }}</span>
|
||||
</ng-container>
|
||||
<ng-template #stockInfos2>
|
||||
<ng-container *ngIf="item?.stockInfos && (item?.stockInfos)[0]?.compartment; else shelfInfos2">
|
||||
{{ (item?.stockInfos)[0]?.compartment }}
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
<ng-template #shelfInfos2>
|
||||
<ng-container *ngIf="item?.shelfInfos && (item?.shelfInfos)[0]?.label">
|
||||
{{ (item?.shelfInfos)[0]?.label }}
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="page-article-details__product-formats-container mt-3" *ngIf="item.family?.length > 0">
|
||||
<hr class="bg-[#E6EFF9] border-t-2" />
|
||||
<div class="pt-3">
|
||||
<div class="page-article-details__product-formats">
|
||||
<span class="mr-2">Auch verfügbar als</span>
|
||||
|
||||
<ui-slider [scrollDistance]="250">
|
||||
<a
|
||||
class="mr-4 text-[#0556B4] font-bold no-underline px-2"
|
||||
*ngFor="let format of item.family"
|
||||
[routerLink]="getDetailsPath(format.product.ean)"
|
||||
queryParamsHandling="preserve"
|
||||
>
|
||||
<span class="flex items-center">
|
||||
<img
|
||||
class="mr-2"
|
||||
*ngIf="!!format.product?.format"
|
||||
[src]="'/assets/images/OF_Icon_' + format.product?.format + '.svg'"
|
||||
alt="format icon"
|
||||
/>
|
||||
{{ format.product?.formatDetail }}
|
||||
<span class="ml-1">{{ format.catalogAvailability?.price?.value?.value | currency: '€' }}</span>
|
||||
</span>
|
||||
</a>
|
||||
</ui-slider>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="bg-[#E6EFF9] border-t-2 my-3" />
|
||||
<div #description class="page-article-details__product-description flex flex-col flex-grow mb-6" *ngIf="item.texts?.length > 0">
|
||||
<page-article-details-text class="block box-border" [text]="item.texts[0]"> </page-article-details-text>
|
||||
<div class="box-border">
|
||||
<button
|
||||
class="font-bold flex flex-row text-[#0556B4] items-center mt-2"
|
||||
*ngIf="!showMore && item?.texts?.length > 1"
|
||||
(click)="showMore = !showMore"
|
||||
>
|
||||
Mehr <ui-icon class="ml-2" size="15px" icon="arrow"></ui-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div *ngIf="showMore" class="page-article-details__product-description-text flex flex-col whitespace-pre-line break-words box-border">
|
||||
<span *ngFor="let text of item.texts | slice: 1">
|
||||
<h3 class="my-4 text-p2 font-bold">{{ text.label }}</h3>
|
||||
{{ text.value }}
|
||||
</span>
|
||||
|
||||
<button class="font-bold flex flex-row text-[#0556B4] items-center mt-2" (click)="showMore = !showMore">
|
||||
<ui-icon class="transform ml-0 mr-2 rotate-180" size="15px" icon="arrow"></ui-icon> Weniger
|
||||
</button>
|
||||
|
||||
<button class="page-article-details__scroll-top-cta" (click)="scrollTop(description)">
|
||||
<ui-icon class="text-[#0556B4]" icon="arrow" size="20px"></ui-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="h-28 box-border"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="page-article-details__product-recommendations relative">
|
||||
<button
|
||||
*ngIf="store.item$ | async; let item"
|
||||
class="shadow-[#dce2e9_0px_-2px_18px_0px] mb-5 border-none outline-none flex items-center px-5 h-14 min-h-[3.5rem] bg-white w-full"
|
||||
(click)="showRecommendations = true"
|
||||
>
|
||||
<span class="uppercase text-[#0556B4] font-bold text-p3">Empfehlungen</span>
|
||||
<img class="absolute right-5 -top-[0.125rem] h-12" src="assets/images/recommendation_tag.png" alt="recommendation icon" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="page-article-details__actions absolute bottom-32 left-1/2 -translate-x-1/2 whitespace-nowrap"
|
||||
*ngIf="store.item$ | async; let item"
|
||||
>
|
||||
<button
|
||||
*ngIf="!(store.isDownload$ | async)"
|
||||
class="text-brand border-2 border-brand bg-white font-bold text-lg px-[1.375rem] py-4 rounded-full mr-[1.875rem]"
|
||||
(click)="showAvailabilities()"
|
||||
>
|
||||
Bestände in anderen Filialen
|
||||
</button>
|
||||
<button
|
||||
class="text-white bg-brand border-brand font-bold text-lg px-[1.375rem] py-4 rounded-full border-none no-underline"
|
||||
(click)="showPurchasingModal()"
|
||||
[disabled]="!(isAvailable$ | async) || (fetchingAvailabilities$ | async) || (item?.features && (item?.features)[0]?.key === 'PFO')"
|
||||
>
|
||||
In den Warenkorb
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="page-article-details__recommendations-overlay absolute top-0 inset-x-0" @slideYAnimation *ngIf="showRecommendations">
|
||||
<page-article-recommendations (close)="showRecommendations = false"></page-article-recommendations>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
<ng-container *ngIf="!showRecommendations">
|
||||
<div #detailsContainer class="page-article-details__container px-5 relative">
|
||||
<ng-container *ngIf="store.item$ | async; let item">
|
||||
@@ -385,4 +768,4 @@
|
||||
|
||||
<div class="page-article-details__recommendations-overlay absolute rounded-t" @slideYAnimation *ngIf="showRecommendations">
|
||||
<page-article-recommendations (close)="showRecommendations = false"></page-article-recommendations>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
:host {
|
||||
@apply box-border block h-split-screen-tablet desktop-small:h-split-screen-desktop;
|
||||
@apply box-border block relative;
|
||||
}
|
||||
|
||||
.page-article-details__wrapper {
|
||||
@apply grid grid-rows-[1fr_auto] h-split-screen-tablet max-h-split-screen-tablet desktop-small:h-split-screen-desktop desktop-small:max-h-split-screen-desktop;
|
||||
}
|
||||
|
||||
.page-article-details__container {
|
||||
@apply h-full w-full overflow-y-scroll overflow-hidden bg-white rounded shadow-card flex flex-col;
|
||||
@apply overflow-scroll bg-white rounded shadow-card flex flex-col;
|
||||
}
|
||||
|
||||
.page-article-details__product-details {
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
class="article"
|
||||
*ngFor="let recommendation of store.recommendations$ | async"
|
||||
[routerLink]="getDetailsPath(recommendation.product.ean)"
|
||||
[queryParams]="{ main_qs: recommendation.product.ean }"
|
||||
[queryParams]="{ main_qs: recommendation.product.ean, filter_format: '' }"
|
||||
(click)="close.emit()"
|
||||
>
|
||||
<img [src]="recommendation.product?.ean | productImage: 195:315:true" alt="product-image" />
|
||||
|
||||
@@ -43,26 +43,6 @@ export class ArticleSearchComponent implements OnInit, OnDestroy {
|
||||
this.removeBreadcrumbs(processId);
|
||||
this.addOrUpdateBreadcrumbs(processId, queryParams);
|
||||
});
|
||||
|
||||
this._articleSearch.searchCompleted
|
||||
.pipe(takeUntil(this._onDestroy$), withLatestFrom(this._processId$))
|
||||
.subscribe(async ([searchCompleted, processId]) => {
|
||||
if (searchCompleted.state.searchState === '') {
|
||||
const params = searchCompleted.state.filter.getQueryParams();
|
||||
if (searchCompleted.state.hits === 1) {
|
||||
const item = searchCompleted.state.items.find((f) => f);
|
||||
await this._navigationService
|
||||
.getArticleDetailsPath({
|
||||
processId,
|
||||
itemId: item.id,
|
||||
extras: { queryParams: params },
|
||||
})
|
||||
.navigate();
|
||||
} else {
|
||||
await this._navigationService.getArticleSearchResultsPath(processId, { queryParams: params }).navigate();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
cleanupQueryParams(params: Record<string, string> = {}) {
|
||||
|
||||
@@ -16,7 +16,6 @@ export interface ArticleSearchState {
|
||||
hits: number;
|
||||
selectedBranch: BranchDTO;
|
||||
selectedItemIds: number[];
|
||||
scrollPosition: number;
|
||||
defaultSettings?: UISettingsDTO;
|
||||
}
|
||||
|
||||
@@ -44,12 +43,6 @@ export class ArticleSearchService extends ComponentStore<ArticleSearchState> {
|
||||
return this.get((s) => s.items);
|
||||
}
|
||||
|
||||
scrollPosition$ = this.select((s) => s.scrollPosition);
|
||||
|
||||
get scrollPosition() {
|
||||
return this.get((s) => s.scrollPosition);
|
||||
}
|
||||
|
||||
selectedBranch$ = this.select((s) => s.selectedBranch);
|
||||
|
||||
get selectedBranch() {
|
||||
@@ -92,7 +85,6 @@ export class ArticleSearchService extends ComponentStore<ArticleSearchState> {
|
||||
searchState: '',
|
||||
selectedItemIds: [],
|
||||
selectedBranch: undefined,
|
||||
scrollPosition: 0,
|
||||
});
|
||||
this.setDefaultFilter();
|
||||
}
|
||||
@@ -113,10 +105,6 @@ export class ArticleSearchService extends ComponentStore<ArticleSearchState> {
|
||||
this.patchState({ selectedBranch });
|
||||
}
|
||||
|
||||
setScrollPosition(scrollPosition: number) {
|
||||
this.patchState({ scrollPosition });
|
||||
}
|
||||
|
||||
async setDefaultFilter(defaultQueryParams?: Record<string, string>) {
|
||||
const defaultSettings = await this.catalog.getSettings().toPromise();
|
||||
|
||||
@@ -160,7 +148,7 @@ export class ArticleSearchService extends ComponentStore<ArticleSearchState> {
|
||||
}
|
||||
}
|
||||
|
||||
search = this.effect((options$: Observable<{ clear?: boolean; orderBy?: boolean }>) =>
|
||||
search = this.effect((options$: Observable<{ clear?: boolean; orderBy?: boolean; doNotTrack?: boolean }>) =>
|
||||
options$.pipe(
|
||||
tap((options) => {
|
||||
this.searchStarted.next({ clear: options?.clear });
|
||||
@@ -178,6 +166,7 @@ export class ArticleSearchService extends ComponentStore<ArticleSearchState> {
|
||||
take: 25,
|
||||
friendlyName: this.friendlyName,
|
||||
stockId: selectedBranch?.id,
|
||||
doNotTrack: options?.doNotTrack,
|
||||
}).pipe(
|
||||
tapResponse(
|
||||
(res) => {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { map, takeUntil, withLatestFrom } from 'rxjs/operators';
|
||||
import { ArticleSearchService } from '../article-search.store';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { ProductCatalogNavigationService } from '@shared/services';
|
||||
import { Filter, FilterComponent, FilterInput } from 'apps/shared/components/filter/src/lib';
|
||||
import { Filter, FilterComponent } from 'apps/shared/components/filter/src/lib';
|
||||
|
||||
@Component({
|
||||
selector: 'page-article-search-filter',
|
||||
@@ -28,10 +28,6 @@ export class ArticleSearchFilterComponent implements OnInit, OnDestroy {
|
||||
|
||||
showFilter: boolean = false;
|
||||
|
||||
get isDesktop() {
|
||||
return this._environment.matchDesktop();
|
||||
}
|
||||
|
||||
get isTablet() {
|
||||
return this._environment.matchTablet();
|
||||
}
|
||||
@@ -92,6 +88,26 @@ export class ArticleSearchFilterComponent implements OnInit, OnDestroy {
|
||||
|
||||
await this.articleSearch.setDefaultFilter(queryParams);
|
||||
});
|
||||
|
||||
this.articleSearch.searchCompleted
|
||||
.pipe(takeUntil(this._onDestroy$), withLatestFrom(this._processId$))
|
||||
.subscribe(async ([searchCompleted, processId]) => {
|
||||
if (searchCompleted.state.searchState === '') {
|
||||
const params = searchCompleted.state.filter.getQueryParams();
|
||||
if (searchCompleted.state.hits === 1) {
|
||||
const item = searchCompleted.state.items.find((f) => f);
|
||||
await this._navigationService
|
||||
.getArticleDetailsPath({
|
||||
processId,
|
||||
itemId: item.id,
|
||||
extras: { queryParams: params },
|
||||
})
|
||||
.navigate();
|
||||
} else {
|
||||
await this._navigationService.getArticleSearchResultsPath(processId, { queryParams: params }).navigate();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
@@ -106,17 +122,17 @@ export class ArticleSearchFilterComponent implements OnInit, OnDestroy {
|
||||
.pipe(takeUntil(this._onDestroy$), withLatestFrom(this._processId$))
|
||||
.subscribe(async ([searchCompleted, processId]) => {
|
||||
if (searchCompleted.state.searchState === '') {
|
||||
// Check if desktop is necessary, otherwise it would trigger navigation twice (Inside Article-Search.component and here)
|
||||
if (searchCompleted.state.hits === 1 && !this.isDesktop) {
|
||||
const params = searchCompleted.state.filter.getQueryParams();
|
||||
if (searchCompleted.state.hits === 1) {
|
||||
const item = searchCompleted.state.items.find((f) => f);
|
||||
await this._navigationService
|
||||
.getArticleDetailsPath({
|
||||
processId,
|
||||
itemId: item.id,
|
||||
extras: { queryParams: params },
|
||||
})
|
||||
.navigate();
|
||||
} else if (!this.isDesktop) {
|
||||
const params = searchCompleted.state.filter.getQueryParams();
|
||||
} else {
|
||||
await this._navigationService.getArticleSearchResultsPath(processId, { queryParams: params }).navigate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,12 @@ import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { DomainCatalogService } from '@domain/catalog';
|
||||
import { combineLatest, NEVER, Subscription } from 'rxjs';
|
||||
import { catchError, debounceTime, first, switchMap, map } from 'rxjs/operators';
|
||||
import { catchError, debounceTime, first, switchMap, map, tap } from 'rxjs/operators';
|
||||
import { ArticleSearchService } from '../article-search.store';
|
||||
import { isEqual } from 'lodash';
|
||||
import { EnvironmentService } from '@core/environment';
|
||||
import { Filter, FilterInputGroupMainComponent } from 'apps/shared/components/filter/src/lib';
|
||||
import { ProductCatalogNavigationService } from '@shared/services';
|
||||
|
||||
@Component({
|
||||
selector: 'page-article-search-main',
|
||||
@@ -17,7 +18,10 @@ import { Filter, FilterInputGroupMainComponent } from 'apps/shared/components/fi
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ArticleSearchMainComponent implements OnInit, OnDestroy {
|
||||
readonly history$ = this.catalog.getSearchHistory({ take: 7 }).pipe(catchError(() => NEVER));
|
||||
readonly history$ = this.catalog.getSearchHistory({ take: 7 }).pipe(
|
||||
map((history) => history.filter((h) => !!h.friendlyName)),
|
||||
catchError(() => NEVER)
|
||||
);
|
||||
|
||||
fetching$ = this.searchService.fetching$;
|
||||
|
||||
@@ -49,7 +53,8 @@ export class ArticleSearchMainComponent implements OnInit, OnDestroy {
|
||||
private route: ActivatedRoute,
|
||||
private application: ApplicationService,
|
||||
private breadcrumb: BreadcrumbService,
|
||||
private _environment: EnvironmentService
|
||||
private _environment: EnvironmentService,
|
||||
private _navigationService: ProductCatalogNavigationService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
@@ -77,6 +82,14 @@ export class ArticleSearchMainComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
this.removeResultsAndDetailsBreadcrumbs(processId);
|
||||
|
||||
// #4519 und #4520 Durch den Performance Umbau, wird auf allen größen kleiner als der Splitscreen die Article-Search Komponente nicht geladen
|
||||
// Stattdessen werden die Komponenten search-main und search-filter geladen. Einige Funktionen von Article-Search müssen trotzdem aufgerufen werden
|
||||
if (!this._environment.matchDesktopLarge()) {
|
||||
this.resetFilter(queryParams);
|
||||
this.removeCheckoutBreadcrumb(processId);
|
||||
this.addOrUpdateBreadcrumbs(processId, queryParams);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
@@ -160,6 +173,27 @@ export class ArticleSearchMainComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
resetFilter(queryParams: Record<string, string>) {
|
||||
if (Object.keys(queryParams).length === 0) {
|
||||
this.searchService.resetFilter();
|
||||
}
|
||||
}
|
||||
|
||||
async removeCheckoutBreadcrumb(processId: number) {
|
||||
this.breadcrumb.removeBreadcrumbsByKeyAndTags(processId, ['checkout']);
|
||||
}
|
||||
|
||||
async addOrUpdateBreadcrumbs(processId: number, queryParams: Record<string, string>) {
|
||||
await this.breadcrumb.addBreadcrumbIfNotExists({
|
||||
key: processId,
|
||||
name: 'Artikelsuche',
|
||||
path: this._navigationService.getArticleSearchBasePath(processId).path,
|
||||
params: queryParams,
|
||||
tags: ['catalog', 'main'],
|
||||
section: 'customer',
|
||||
});
|
||||
}
|
||||
|
||||
async removeResultsAndDetailsBreadcrumbs(processId: number) {
|
||||
const resultsCrumbs = await this.breadcrumb.getBreadcrumbsByKeyAndTags$(processId, ['catalog', 'results']).pipe(first()).toPromise();
|
||||
const detailCrumbs = await this.breadcrumb.getBreadcrumbsByKeyAndTags$(processId, ['catalog', 'details']).pipe(first()).toPromise();
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
<a
|
||||
<div
|
||||
class="page-search-result-item__item-card hover p-5 desktop-small:px-4 desktop-small:py-[0.625rem] h-[13.25rem] desktop-small:h-[11.3125rem] bg-white border border-solid border-transparent rounded"
|
||||
[class.page-search-result-item__item-card-primary]="primaryOutletActive"
|
||||
[routerLink]="detailsPath"
|
||||
[routerLinkActive]="!isTablet && !primaryOutletActive ? 'active' : ''"
|
||||
queryParamsHandling="preserve"
|
||||
(click)="isDesktopLarge ? scrollIntoView() : ''"
|
||||
[class.active]="isActive"
|
||||
>
|
||||
<div class="page-search-result-item__item-thumbnail text-center mr-4 w-[3.125rem] h-[4.9375rem]">
|
||||
<img
|
||||
@@ -122,4 +119,4 @@
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DatePipe } from '@angular/common';
|
||||
import { Component, ChangeDetectionStrategy, Input, EventEmitter, Output, HostBinding, ElementRef } from '@angular/core';
|
||||
import { Component, ChangeDetectionStrategy, Input, EventEmitter, Output, HostBinding } from '@angular/core';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { EnvironmentService } from '@core/environment';
|
||||
import { DomainAvailabilityService, DomainInStockService } from '@domain/availability';
|
||||
@@ -54,6 +54,8 @@ export class SearchResultItemComponent extends ComponentStore<SearchResultItemCo
|
||||
@Input()
|
||||
primaryOutletActive?: boolean = false;
|
||||
|
||||
@Input() isActive: boolean;
|
||||
|
||||
@Output()
|
||||
selectedChange = new EventEmitter<ItemDTO>();
|
||||
|
||||
@@ -82,11 +84,6 @@ export class SearchResultItemComponent extends ComponentStore<SearchResultItemCo
|
||||
return this._environment.matchDesktopLarge();
|
||||
}
|
||||
|
||||
get detailsPath() {
|
||||
return this._navigationService.getArticleDetailsPath({ processId: this.applicationService.activatedProcessId, itemId: this.item?.id })
|
||||
.path;
|
||||
}
|
||||
|
||||
get resultsPath() {
|
||||
return this._navigationService.getArticleSearchResultsPath(this.applicationService.activatedProcessId).path;
|
||||
}
|
||||
@@ -141,7 +138,6 @@ export class SearchResultItemComponent extends ComponentStore<SearchResultItemCo
|
||||
private _availability: DomainAvailabilityService,
|
||||
private _environment: EnvironmentService,
|
||||
private _navigationService: ProductCatalogNavigationService,
|
||||
private _elRef: ElementRef<HTMLElement>,
|
||||
private _store: Store
|
||||
) {
|
||||
super({
|
||||
@@ -150,10 +146,6 @@ export class SearchResultItemComponent extends ComponentStore<SearchResultItemCo
|
||||
});
|
||||
}
|
||||
|
||||
scrollIntoView() {
|
||||
this._elRef.nativeElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
}
|
||||
|
||||
setSelected() {
|
||||
const isSelected = this._articleSearchService.selectedItemIds.includes(this.item?.id);
|
||||
this._articleSearchService.setSelected({ selected: !isSelected, itemId: this.item?.id });
|
||||
|
||||
@@ -46,29 +46,27 @@
|
||||
</shared-order-by-filter>
|
||||
</div>
|
||||
|
||||
<div class="h-full relative">
|
||||
<cdk-virtual-scroll-viewport
|
||||
#scrollContainer
|
||||
class="product-list h-full"
|
||||
[itemSize]="(primaryOutletActive$ | async) ? 98 : 181"
|
||||
minBufferPx="1200"
|
||||
[maxBufferPx]="maxBufferCdkScrollContainer$ | async"
|
||||
(scrolledIndexChange)="scrolledIndexChange($event)"
|
||||
>
|
||||
<search-result-item
|
||||
class="page-search-results__result-item"
|
||||
[class.page-search-results__result-item-primary]="primaryOutletActive$ | async"
|
||||
*cdkVirtualFor="let item of results$ | async; trackBy: trackByItemId"
|
||||
(selectedChange)="addToCart($event)"
|
||||
[selected]="isSelected(item)"
|
||||
[selectable]="isSelectable(item)"
|
||||
[item]="item"
|
||||
[primaryOutletActive]="primaryOutletActive$ | async"
|
||||
></search-result-item>
|
||||
<page-search-result-item-loading
|
||||
[primaryOutletActive]="primaryOutletActive$ | async"
|
||||
*ngIf="fetching$ | async"
|
||||
></page-search-result-item-loading>
|
||||
<ng-container *ngIf="primaryOutletActive$ | async; else sideOutlet">
|
||||
<cdk-virtual-scroll-viewport class="product-list" [itemSize]="106 * (scale$ | async)" (scrolledIndexChange)="scrolledIndexChange($event)">
|
||||
<a
|
||||
*cdkVirtualFor="let item of results$ | async; let i = index; trackBy: trackByItemId"
|
||||
[routerLink]="getDetailsPath(item.id)"
|
||||
routerLinkActive
|
||||
#rla="routerLinkActive"
|
||||
queryParamsHandling="preserve"
|
||||
(click)="scrollToItem(i)"
|
||||
>
|
||||
<search-result-item
|
||||
class="page-search-results__result-item page-search-results__result-item-primary"
|
||||
(selectedChange)="addToCart($event)"
|
||||
[selected]="isSelected(item)"
|
||||
[selectable]="isSelectable(item)"
|
||||
[item]="item"
|
||||
[primaryOutletActive]="true"
|
||||
[isActive]="rla.isActive"
|
||||
></search-result-item>
|
||||
</a>
|
||||
<page-search-result-item-loading [primaryOutletActive]="true" *ngIf="fetching$ | async"></page-search-result-item-loading>
|
||||
</cdk-virtual-scroll-viewport>
|
||||
<div class="actions z-sticky h-0">
|
||||
<button
|
||||
@@ -80,4 +78,38 @@
|
||||
<ui-spinner [show]="loading$ | async">In den Warenkorb legen</ui-spinner>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-template #sideOutlet>
|
||||
<cdk-virtual-scroll-viewport class="product-list" [itemSize]="222 * (scale$ | async)" (scrolledIndexChange)="scrolledIndexChange($event)">
|
||||
<a
|
||||
*cdkVirtualFor="let item of results$ | async; let i = index; trackBy: trackByItemId"
|
||||
[routerLink]="getDetailsPath(item.id)"
|
||||
routerLinkActive
|
||||
#rla="routerLinkActive"
|
||||
queryParamsHandling="preserve"
|
||||
(click)="scrollToItem(i)"
|
||||
>
|
||||
<search-result-item
|
||||
class="page-search-results__result-item"
|
||||
(selectedChange)="addToCart($event)"
|
||||
[selected]="isSelected(item)"
|
||||
[selectable]="isSelectable(item)"
|
||||
[item]="item"
|
||||
[primaryOutletActive]="false"
|
||||
[isActive]="rla.isActive"
|
||||
></search-result-item>
|
||||
</a>
|
||||
<page-search-result-item-loading [primaryOutletActive]="false" *ngIf="fetching$ | async"></page-search-result-item-loading>
|
||||
</cdk-virtual-scroll-viewport>
|
||||
<div class="actions z-sticky h-0">
|
||||
<button
|
||||
[disabled]="loading$ | async"
|
||||
*ngIf="(selectedItemIds$ | async)?.length > 0"
|
||||
class="cta-cart cta-action-primary"
|
||||
(click)="addToCart()"
|
||||
>
|
||||
<ui-spinner [show]="loading$ | async">In den Warenkorb legen</ui-spinner>
|
||||
</button>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
:host {
|
||||
@apply box-border grid h-split-screen-tablet desktop-small:h-split-screen-desktop;
|
||||
@apply box-border grid h-split-screen-tablet desktop-small:h-split-screen-desktop relative;
|
||||
grid-template-rows: auto auto 1fr;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
}
|
||||
|
||||
.page-search-results__result-item {
|
||||
@apply mb-px-10;
|
||||
@apply mb-[0.625rem];
|
||||
}
|
||||
|
||||
.page-search-results__result-item-primary {
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
QueryList,
|
||||
TrackByFunction,
|
||||
AfterViewInit,
|
||||
inject,
|
||||
} from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { ApplicationService } from '@core/application';
|
||||
@@ -19,7 +20,7 @@ import { ItemDTO } from '@swagger/cat';
|
||||
import { AddToShoppingCartDTO } from '@swagger/checkout';
|
||||
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
|
||||
import { CacheService } from 'apps/core/cache/src/public-api';
|
||||
import { isEqual } from 'lodash';
|
||||
import { debounce, isEqual } from 'lodash';
|
||||
import { BehaviorSubject, combineLatest, Subscription } from 'rxjs';
|
||||
import { debounceTime, first, map, switchMap, withLatestFrom } from 'rxjs/operators';
|
||||
import { ArticleSearchService } from '../article-search.store';
|
||||
@@ -28,6 +29,8 @@ import { SearchResultItemComponent } from './search-result-item.component';
|
||||
import { ProductCatalogNavigationService } from '@shared/services';
|
||||
import { Filter, FilterInputGroupMainComponent } from 'apps/shared/components/filter/src/lib';
|
||||
import { DomainAvailabilityService, ItemData } from '@domain/availability';
|
||||
import { asapScheduler } from 'rxjs';
|
||||
import { ShellService } from '@shared/shell';
|
||||
|
||||
@Component({
|
||||
selector: 'page-search-results',
|
||||
@@ -37,7 +40,7 @@ import { DomainAvailabilityService, ItemData } from '@domain/availability';
|
||||
})
|
||||
export class ArticleSearchResultsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
@ViewChildren(SearchResultItemComponent) listItems: QueryList<SearchResultItemComponent>;
|
||||
@ViewChild('scrollContainer', { static: true })
|
||||
@ViewChild(CdkVirtualScrollViewport, { static: false })
|
||||
scrollContainer: CdkVirtualScrollViewport;
|
||||
|
||||
@ViewChild(FilterInputGroupMainComponent, { static: false })
|
||||
@@ -59,6 +62,10 @@ export class ArticleSearchResultsComponent implements OnInit, OnDestroy, AfterVi
|
||||
})
|
||||
);
|
||||
|
||||
getProcessId(): number {
|
||||
return this.application.activatedProcessId;
|
||||
}
|
||||
|
||||
loading$ = new BehaviorSubject<boolean>(false);
|
||||
|
||||
private subscriptions = new Subscription();
|
||||
@@ -92,20 +99,11 @@ export class ArticleSearchResultsComponent implements OnInit, OnDestroy, AfterVi
|
||||
return this._environment.matchDesktop$.pipe(map((matches) => matches && this.route.outlet === 'primary'));
|
||||
}
|
||||
|
||||
// Ticket #4169 Splitscreen
|
||||
// Render genug Artikel um bei Navigation auf Trefferliste | PDP zum angewählten Artikel zu Scrollen
|
||||
maxBufferCdkScrollContainer$ = this.results$.pipe(
|
||||
withLatestFrom(this.primaryOutletActive$),
|
||||
map(([results, primaryOutlet]) => {
|
||||
if (!primaryOutlet && results?.length > 0) {
|
||||
// Splitscreen mode: Items Length * Item Pixel Height
|
||||
const maxBufferSize = results.length * 181;
|
||||
return maxBufferSize >= 1200 ? maxBufferSize : 1200;
|
||||
} else {
|
||||
return 1200;
|
||||
}
|
||||
})
|
||||
);
|
||||
private readonly SCROLL_INDEX_TOKEN = 'CATALOG_RESULTS_LIST_SCROLL_INDEX';
|
||||
|
||||
shellService = inject(ShellService);
|
||||
|
||||
scale$ = this.shellService.scale$;
|
||||
|
||||
constructor(
|
||||
public searchService: ArticleSearchService,
|
||||
@@ -156,9 +154,8 @@ export class ArticleSearchResultsComponent implements OnInit, OnDestroy, AfterVi
|
||||
|
||||
const cleanQueryParams = this.cleanupQueryParams(queryParams);
|
||||
|
||||
// Scroll to scroll_position in great result list
|
||||
if (!!queryParams?.scroll_position && this.route.outlet === 'primary') {
|
||||
this.scrollTop(Number(queryParams.scroll_position ?? 0));
|
||||
if (this.route.outlet === 'primary' && processChanged) {
|
||||
this.scrollToItem(this._getScrollIndexFromCache());
|
||||
}
|
||||
|
||||
if (!isEqual(cleanQueryParams, this.cleanupQueryParams(this.searchService.filter.getQueryParams()))) {
|
||||
@@ -174,11 +171,6 @@ export class ArticleSearchResultsComponent implements OnInit, OnDestroy, AfterVi
|
||||
) {
|
||||
this.search({ clear: true });
|
||||
} else {
|
||||
if (!this.isDesktopLarge || this.route.outlet === 'primary') {
|
||||
this.scrollTop(Number(queryParams.scroll_position ?? 0));
|
||||
} else {
|
||||
this.scrollItemIntoView();
|
||||
}
|
||||
const selectedItemIds: Array<string> = queryParams?.selected_item_ids?.split(',') ?? [];
|
||||
for (const id of selectedItemIds) {
|
||||
if (id) {
|
||||
@@ -236,8 +228,20 @@ export class ArticleSearchResultsComponent implements OnInit, OnDestroy, AfterVi
|
||||
})
|
||||
.navigate();
|
||||
}
|
||||
} else if ((searchCompleted?.clear || this.route.outlet === 'primary') && this.isDesktopLarge) {
|
||||
await this._navigationService.getArticleSearchResultsPath(processId, { queryParams: params }).navigate();
|
||||
} else if (searchCompleted?.clear || this.route.outlet === 'primary') {
|
||||
const ean = this.route?.snapshot?.params?.ean;
|
||||
|
||||
if (ean) {
|
||||
await this._navigationService
|
||||
.getArticleDetailsPathByEan({
|
||||
processId,
|
||||
ean,
|
||||
extras: { queryParams: params },
|
||||
})
|
||||
.navigate();
|
||||
} else {
|
||||
await this._navigationService.getArticleSearchResultsPath(processId, { queryParams: params }).navigate();
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -259,7 +263,41 @@ export class ArticleSearchResultsComponent implements OnInit, OnDestroy, AfterVi
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
this.scrollItemIntoView();
|
||||
this.scrollToItem(this._getScrollIndexFromCache());
|
||||
}
|
||||
|
||||
private _addScrollIndexToCache(index: number): void {
|
||||
this.cache.set<number>({ processId: this.getProcessId(), token: this.SCROLL_INDEX_TOKEN }, index);
|
||||
}
|
||||
|
||||
private _getScrollIndexFromCache(): number {
|
||||
return this.cache.get<number>({ processId: this.getProcessId(), token: this.SCROLL_INDEX_TOKEN });
|
||||
}
|
||||
|
||||
scrollToItem(i?: number) {
|
||||
let index = i;
|
||||
|
||||
if (!index) {
|
||||
index = this._getScrollIndexFromCache();
|
||||
} else {
|
||||
this._addScrollIndexToCache(index);
|
||||
}
|
||||
|
||||
asapScheduler.schedule(() => {
|
||||
this.scrollContainer.scrollToIndex(index, 'smooth');
|
||||
}, 150);
|
||||
}
|
||||
|
||||
scrolledIndexChange(index: number) {
|
||||
const completeListFetched = this.searchService.items.length === this.searchService.hits;
|
||||
|
||||
if (index && !completeListFetched && this.searchService.items.length <= this.scrollContainer?.getRenderedRange()?.end) {
|
||||
this.search({ clear: false });
|
||||
}
|
||||
|
||||
if (this.getProcessId() === this.searchService.processId) {
|
||||
this._addScrollIndexToCache(index);
|
||||
}
|
||||
}
|
||||
|
||||
async ngOnDestroy() {
|
||||
@@ -290,42 +328,17 @@ export class ArticleSearchResultsComponent implements OnInit, OnDestroy, AfterVi
|
||||
this.sharedFilterInputGroupMain.cancelAutocomplete();
|
||||
}
|
||||
|
||||
this.searchService.search({ clear, orderBy });
|
||||
this.searchService.search({ clear, orderBy, doNotTrack: true });
|
||||
}
|
||||
|
||||
scrollTop(scrollPos: number) {
|
||||
setTimeout(() => this.scrollContainer.scrollTo({ top: scrollPos }), 0);
|
||||
}
|
||||
|
||||
scrollItemIntoView() {
|
||||
setTimeout(() => {
|
||||
const item = this.listItems?.find((item) => item.item.id === Number(this.route?.snapshot?.params?.id));
|
||||
item?.scrollIntoView();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
async scrolledIndexChange(index: number) {
|
||||
const results = await this.results$.pipe(first()).toPromise();
|
||||
const hits = await this.hits$.pipe(first()).toPromise();
|
||||
|
||||
if (results.length >= hits) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.route.outlet === 'primary') {
|
||||
this.searchService.setScrollPosition(this.scrollContainer.measureScrollOffset('top'));
|
||||
}
|
||||
|
||||
if (index >= results.length - 20 && results.length - 20 > 0) {
|
||||
this.search({ clear: false });
|
||||
}
|
||||
getDetailsPath(itemId: number) {
|
||||
return this._navigationService.getArticleDetailsPath({ processId: this.application.activatedProcessId, itemId }).path;
|
||||
}
|
||||
|
||||
async updateBreadcrumbs(
|
||||
processId: number = this.searchService.processId,
|
||||
queryParams: Record<string, string> = this.searchService.filter?.getQueryParams()
|
||||
) {
|
||||
const scroll_position = this.searchService.scrollPosition;
|
||||
const selected_item_ids = this.searchService?.selectedItemIds?.toString();
|
||||
|
||||
if (queryParams) {
|
||||
@@ -335,7 +348,7 @@ export class ArticleSearchResultsComponent implements OnInit, OnDestroy, AfterVi
|
||||
.toPromise();
|
||||
|
||||
const name = queryParams.main_qs ? queryParams.main_qs : 'Alle Artikel';
|
||||
const params = { ...queryParams, scroll_position, selected_item_ids };
|
||||
const params = { ...queryParams, selected_item_ids };
|
||||
|
||||
for (const crumb of crumbs) {
|
||||
this.breadcrumb.patchBreadcrumb(crumb.id, {
|
||||
@@ -388,7 +401,6 @@ export class ArticleSearchResultsComponent implements OnInit, OnDestroy, AfterVi
|
||||
|
||||
cleanupQueryParams(params: Record<string, string> = {}) {
|
||||
const clean = { ...params };
|
||||
delete clean['scroll_position'];
|
||||
delete clean['selected_item_ids'];
|
||||
|
||||
for (const key in clean) {
|
||||
@@ -480,6 +492,9 @@ export class ArticleSearchResultsComponent implements OnInit, OnDestroy, AfterVi
|
||||
// #4180 Für Download Artikel muss hier immer zwingend der logistician gesetzt werden, da diese Artikel direkt zugeordnet dem Warenkorb hinzugefügt werden
|
||||
const downloadAvailability = await this._availability.getDownloadAvailability({ item: downloadItem }).pipe(first()).toPromise();
|
||||
shoppingCartItem.destination = { data: { target: 16, logistician: downloadAvailability?.logistician } };
|
||||
if (downloadAvailability) {
|
||||
shoppingCartItem.availability = { ...shoppingCartItem.availability, ...downloadAvailability };
|
||||
}
|
||||
canAddItemsPayload.push({
|
||||
availabilities: [{ ...item.catalogAvailability, format: 'DL' }],
|
||||
id: item.product.catalogProductNumber,
|
||||
|
||||
@@ -81,8 +81,11 @@
|
||||
*ngIf="group?.orderType !== undefined && (item.features?.orderType === 'Abholung' || item.features?.orderType === 'Rücklage')"
|
||||
>
|
||||
<ng-container *ngIf="item?.destination?.data?.targetBranch?.data; let targetBranch">
|
||||
<ng-container *ngIf="i === 0 || targetBranch.id !== group.items[i - 1].destination?.data?.targetBranch?.data.id">
|
||||
<div class="flex flex-row items-center px-5 pt-0 pb-[0.875rem] -mt-2 bg-[#F5F7FA]">
|
||||
<ng-container *ngIf="i === 0 || checkIfMultipleDestinationsForOrderTypeExist(targetBranch, group, i)">
|
||||
<div
|
||||
class="flex flex-row items-center px-5 pt-0 pb-[0.875rem] -mt-2 bg-[#F5F7FA]"
|
||||
[class.multiple-destinations]="checkIfMultipleDestinationsForOrderTypeExist(targetBranch, group, i)"
|
||||
>
|
||||
<span class="branch-name">{{ targetBranch?.name }} | {{ targetBranch | branchAddress }}</span>
|
||||
</div>
|
||||
<hr />
|
||||
|
||||
@@ -105,6 +105,10 @@ h1 {
|
||||
}
|
||||
}
|
||||
|
||||
.multiple-destinations {
|
||||
@apply py-[0.875rem] mt-0;
|
||||
}
|
||||
|
||||
.icon-order-type {
|
||||
@apply text-black mr-2;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import { Router } from '@angular/router';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { DomainAvailabilityService } from '@domain/availability';
|
||||
import { DomainCheckoutService } from '@domain/checkout';
|
||||
import { AvailabilityDTO, DestinationDTO, ShoppingCartItemDTO } from '@swagger/checkout';
|
||||
import { AvailabilityDTO, BranchDTO, DestinationDTO, ShoppingCartItemDTO } from '@swagger/checkout';
|
||||
import { UiMessageModalComponent, UiModalService } from '@ui/modal';
|
||||
import { PrintModalData, PrintModalComponent } from '@modal/printer';
|
||||
import { delay, first, map, switchMap, takeUntil, tap } from 'rxjs/operators';
|
||||
@@ -254,6 +254,10 @@ export class CheckoutReviewComponent implements OnInit, OnDestroy, AfterViewInit
|
||||
});
|
||||
}
|
||||
|
||||
checkIfMultipleDestinationsForOrderTypeExist(targetBranch: BranchDTO, group: { items: ShoppingCartItemDTO[] }, i: number) {
|
||||
return i === 0 ? false : targetBranch.id !== group.items[i - 1].destination?.data?.targetBranch?.data.id;
|
||||
}
|
||||
|
||||
async refreshAvailabilities() {
|
||||
this.checkingOla$.next(true);
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ import { NotificationChannel } from '@swagger/checkout';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class CheckoutReviewDetailsComponent implements OnInit {
|
||||
control = this._store.notificationsControl;
|
||||
control: UntypedFormGroup;
|
||||
|
||||
customerFeatures$ = this._store.customerFeatures$;
|
||||
|
||||
@@ -96,15 +96,18 @@ export class CheckoutReviewDetailsComponent implements OnInit {
|
||||
selectedNotificationChannel = 1;
|
||||
}
|
||||
|
||||
this.control = fb.group({
|
||||
notificationChannel: new UntypedFormGroup({
|
||||
selected: new UntypedFormControl(selectedNotificationChannel),
|
||||
email: new UntypedFormControl(communicationDetails ? communicationDetails.email : '', emailNotificationValidator),
|
||||
mobile: new UntypedFormControl(communicationDetails ? communicationDetails.mobile : '', mobileNotificationValidator),
|
||||
}),
|
||||
});
|
||||
|
||||
this._store.notificationsControl = this.control;
|
||||
if (!this._store.notificationsControl) {
|
||||
this.control = fb.group({
|
||||
notificationChannel: new UntypedFormGroup({
|
||||
selected: new UntypedFormControl(selectedNotificationChannel),
|
||||
email: new UntypedFormControl(communicationDetails ? communicationDetails.email : '', emailNotificationValidator),
|
||||
mobile: new UntypedFormControl(communicationDetails ? communicationDetails.mobile : '', mobileNotificationValidator),
|
||||
}),
|
||||
});
|
||||
this._store.notificationsControl = this.control;
|
||||
} else {
|
||||
this.control = this._store.notificationsControl;
|
||||
}
|
||||
}
|
||||
|
||||
setAgentComment(agentComment: string) {
|
||||
|
||||
@@ -222,22 +222,24 @@
|
||||
<div class="fetching" *ngIf="!!(updatingPreferredPickUpDate$ | async)[(order?.subsetItems)[0].id]"></div>
|
||||
</ng-template>
|
||||
|
||||
<div class="absolute left-1/2 bottom-10 transform -translate-x-1/2 inline-grid grid-flow-col gap-4">
|
||||
<button
|
||||
*ifRole="'Store'"
|
||||
type="button"
|
||||
class="px-6 py-2 rounded-full border-2 border-solid border-brand text-brand bg-white font-bold text-lg whitespace-nowrap h-14"
|
||||
(click)="printOrderConfirmation()"
|
||||
>
|
||||
Bestellbestätigung drucken
|
||||
</button>
|
||||
<div class="relative">
|
||||
<div class="absolute left-1/2 bottom-10 inline-grid grid-flow-col gap-4 justify-center transform -translate-x-1/2">
|
||||
<button
|
||||
*ifRole="'Store'"
|
||||
type="button"
|
||||
class="px-6 py-2 rounded-full border-2 border-solid border-brand text-brand bg-white font-bold text-lg whitespace-nowrap h-14"
|
||||
(click)="printOrderConfirmation()"
|
||||
>
|
||||
Bestellbestätigung drucken
|
||||
</button>
|
||||
|
||||
<button
|
||||
*ngIf="hasAbholung$ | async"
|
||||
type="button"
|
||||
class="px-6 py-2 rounded-full border-2 border-solid border-brand text-brand bg-white font-bold text-lg whitespace-nowrap h-14"
|
||||
(click)="sendOrderConfirmation()"
|
||||
>
|
||||
Bestellbestätigung senden
|
||||
</button>
|
||||
<button
|
||||
*ngIf="hasAbholung$ | async"
|
||||
type="button"
|
||||
class="px-6 py-2 rounded-full border-2 border-solid border-brand text-brand bg-white font-bold text-lg whitespace-nowrap h-14"
|
||||
(click)="sendOrderConfirmation()"
|
||||
>
|
||||
Bestellbestätigung senden
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
<div class="label">ISBN/EAN</div>
|
||||
<div class="value">{{ orderItem.product?.ean }}</div>
|
||||
</div>
|
||||
<div class="detail" *ngIf="!!orderItem.price">
|
||||
<div class="detail" *ngIf="orderItem.price !== undefined">
|
||||
<div class="label">Preis</div>
|
||||
<div class="value">{{ orderItem.price | currency: 'EUR' }}</div>
|
||||
</div>
|
||||
|
||||
@@ -103,6 +103,33 @@ export class CustomerOrderSearchFilterComponent implements OnInit, OnDestroy {
|
||||
this._customerOrdersSearchStore.setQueryParams(queryParams);
|
||||
});
|
||||
|
||||
this._customerOrdersSearchStore.searchResultSubject.pipe(takeUntil(this._onDestroy$)).subscribe(async (result) => {
|
||||
if (result.results.error) {
|
||||
} else {
|
||||
if (result.results.hits > 0) {
|
||||
const queryParams = this._customerOrdersSearchStore.filter.getQueryParams();
|
||||
if (result.results.hits === 1) {
|
||||
const orderItem = result.results.result[0];
|
||||
await this._navigationService
|
||||
.getCustomerOrdersDetailsPath({
|
||||
processId: this.processId,
|
||||
processingStatus: orderItem?.processingStatus,
|
||||
compartmentCode: orderItem?.compartmentCode ? encodeURIComponent(orderItem.compartmentCode) : undefined,
|
||||
orderId: orderItem?.orderId ? orderItem.orderId : undefined,
|
||||
extras: { queryParams },
|
||||
})
|
||||
.navigate();
|
||||
} else {
|
||||
await this._navigationService.getCustomerOrdersResultsPath(this.processId, { queryParams }).navigate();
|
||||
}
|
||||
} else {
|
||||
this._customerOrdersSearchStore.setMessage('keine Suchergebnisse');
|
||||
}
|
||||
|
||||
this._cdr.markForCheck();
|
||||
}
|
||||
});
|
||||
|
||||
this._initSettings();
|
||||
this._initLoading$();
|
||||
}
|
||||
@@ -133,34 +160,6 @@ export class CustomerOrderSearchFilterComponent implements OnInit, OnDestroy {
|
||||
const queryParams = filter.getQueryParams();
|
||||
this._customerOrdersSearchStore.setQueryParams(queryParams);
|
||||
await this.updateQueryParams(queryParams);
|
||||
|
||||
this._customerOrdersSearchStore.searchResultSubject.pipe(takeUntil(this._onDestroy$)).subscribe(async (result) => {
|
||||
if (result.results.error) {
|
||||
} else {
|
||||
if (result.results.hits > 0) {
|
||||
const queryParams = this._customerOrdersSearchStore.filter.getQueryParams();
|
||||
if (result.results.hits === 1) {
|
||||
const orderItem = result.results.result[0];
|
||||
await this._navigationService
|
||||
.getCustomerOrdersDetailsPath({
|
||||
processId: this.processId,
|
||||
processingStatus: orderItem?.processingStatus,
|
||||
compartmentCode: orderItem?.compartmentCode ? encodeURIComponent(orderItem.compartmentCode) : undefined,
|
||||
orderId: orderItem?.orderId ? orderItem.orderId : undefined,
|
||||
extras: { queryParams },
|
||||
})
|
||||
.navigate();
|
||||
} else {
|
||||
await this._navigationService.getCustomerOrdersResultsPath(this.processId, { queryParams }).navigate();
|
||||
}
|
||||
} else {
|
||||
this._customerOrdersSearchStore.setMessage('keine Suchergebnisse');
|
||||
}
|
||||
|
||||
this._cdr.markForCheck();
|
||||
}
|
||||
});
|
||||
|
||||
this._customerOrdersSearchStore.search({ clear: true });
|
||||
}
|
||||
|
||||
|
||||
@@ -174,8 +174,7 @@ export class CustomerOrderSearchStore extends ComponentStore<CustomerOrderSearch
|
||||
this.setSearchHistory(searchHistory?.slice(0, 7));
|
||||
}
|
||||
|
||||
getCachedData() {
|
||||
const queryToken = { ...this.filter?.getQueryToken(), processId: this.processId, branchId: String(this.selectedBranch?.id) } ?? {};
|
||||
getCachedData({ queryToken }: { queryToken: Record<string, any> }) {
|
||||
return (
|
||||
this._cache.get<{
|
||||
hits: number;
|
||||
@@ -185,6 +184,14 @@ export class CustomerOrderSearchStore extends ComponentStore<CustomerOrderSearch
|
||||
);
|
||||
}
|
||||
|
||||
setCache({ queryToken, hits, results }: { queryToken: Record<string, any>; hits: number; results: OrderItemListItemDTO[] }) {
|
||||
this._cache.set(queryToken, {
|
||||
hits,
|
||||
results,
|
||||
fetching: false,
|
||||
});
|
||||
}
|
||||
|
||||
search = this.effect((options$: Observable<{ clear?: boolean; siletReload?: boolean }>) =>
|
||||
options$.pipe(
|
||||
tap((_) => {
|
||||
@@ -201,7 +208,6 @@ export class CustomerOrderSearchStore extends ComponentStore<CustomerOrderSearch
|
||||
|
||||
if (options?.clear) {
|
||||
this.searchResultClearedSubject.next();
|
||||
this._cache.delete(filter?.getQueryToken());
|
||||
}
|
||||
}),
|
||||
switchMap(([options, results, filter, branch]) => {
|
||||
@@ -227,7 +233,7 @@ export class CustomerOrderSearchStore extends ComponentStore<CustomerOrderSearch
|
||||
queryToken.take = 50;
|
||||
}
|
||||
|
||||
if (branch?.id) {
|
||||
if (branch?.id && !!queryToken.filter) {
|
||||
queryToken.filter['branch_id'] = String(branch?.id);
|
||||
}
|
||||
|
||||
@@ -250,18 +256,10 @@ export class CustomerOrderSearchStore extends ComponentStore<CustomerOrderSearch
|
||||
silentFetching: false,
|
||||
});
|
||||
|
||||
const queryToken = { ...filter?.getQueryToken(), processId: this.processId, branchId: String(branch?.id) };
|
||||
|
||||
if (res?.hits > 0 && options?.clear) {
|
||||
this.saveSearchHistoryToSessionStorage();
|
||||
}
|
||||
|
||||
this._cache.set(queryToken, {
|
||||
hits: res.hits,
|
||||
results: _results,
|
||||
fetching: false,
|
||||
});
|
||||
|
||||
this.searchResultSubject.next({ results: res, cached, clear: options?.clear });
|
||||
|
||||
if (res?.hits === 0) {
|
||||
|
||||
@@ -62,15 +62,12 @@ export class CustomerOrderSearchMainComponent implements OnInit, OnDestroy {
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
// Clear scroll position
|
||||
localStorage.removeItem(`SCROLL_POSITION_${this.processId}`);
|
||||
|
||||
this._subscriptions.add(
|
||||
combineLatest([this.processId$, this._activatedRoute.queryParams])
|
||||
.pipe(debounceTime(50))
|
||||
.subscribe(([processId, queryParams]) => {
|
||||
this.removeBreadcrumbs(processId);
|
||||
this.updateBreadcrumb(processId, queryParams);
|
||||
this.updateBreadcrumb(queryParams);
|
||||
})
|
||||
);
|
||||
|
||||
@@ -88,17 +85,19 @@ export class CustomerOrderSearchMainComponent implements OnInit, OnDestroy {
|
||||
})
|
||||
);
|
||||
|
||||
// #4143 To make Splitscreen Search and Filter work combined
|
||||
this._subscriptions.add(
|
||||
this._customerOrderSearchStore.searchStarted.subscribe(async (_) => {
|
||||
const queryParams = {
|
||||
...this.cleanupQueryParams(this._customerOrderSearchStore.filter.getQueryParams()),
|
||||
main_qs: this.filterInputGroup?.uiInput?.value,
|
||||
};
|
||||
// Im Zuge des Tickets #4256 auskommentiert, da es zu Problemen geführt hat
|
||||
// In der Filter Komponente wird dies schon gemacht
|
||||
|
||||
this._customerOrderSearchStore.setQueryParams(queryParams);
|
||||
})
|
||||
);
|
||||
// // #4143 To make Splitscreen Search and Filter work combined
|
||||
// this._subscriptions.add(
|
||||
// this._customerOrderSearchStore.searchStarted.subscribe(async (_) => {
|
||||
// const queryParams = {
|
||||
// ...this.cleanupQueryParams(this._customerOrderSearchStore.filter.getQueryParams()),
|
||||
// main_qs: this.filterInputGroup?.uiInput?.value,
|
||||
// };
|
||||
// this._customerOrderSearchStore.setQueryParams(queryParams);
|
||||
// })
|
||||
// );
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
@@ -147,15 +146,19 @@ export class CustomerOrderSearchMainComponent implements OnInit, OnDestroy {
|
||||
async search(filter: Filter) {
|
||||
this._customerOrderSearchStore.setMessage('');
|
||||
this.filterInputGroup?.cancelAutocomplete();
|
||||
|
||||
const queryParams = filter.getQueryParams();
|
||||
this._customerOrderSearchStore.setQueryParams(queryParams);
|
||||
await this.updateQueryParams(queryParams);
|
||||
|
||||
this._customerOrderSearchStore.search({ clear: true });
|
||||
await this.updateQueryParams(this.processId);
|
||||
}
|
||||
|
||||
async updateBreadcrumb(processId: number, params: Record<string, string>) {
|
||||
async updateBreadcrumb(params: Record<string, string>) {
|
||||
await this._breadcrumb.addOrUpdateBreadcrumbIfNotExists({
|
||||
key: processId,
|
||||
key: this.processId,
|
||||
name: 'Kundenbestellung',
|
||||
path: this._navigationService.getCustomerOrdersBasePath(processId).path,
|
||||
path: this._navigationService.getCustomerOrdersBasePath(this.processId).path,
|
||||
tags: ['customer-order', 'main', 'filter'],
|
||||
section: 'customer',
|
||||
params,
|
||||
@@ -164,12 +167,12 @@ export class CustomerOrderSearchMainComponent implements OnInit, OnDestroy {
|
||||
|
||||
setQueryHistory(filter: Filter, query: string) {
|
||||
filter.fromQueryParams({ main_qs: query });
|
||||
const queryParams = filter.getQueryParams();
|
||||
this._customerOrderSearchStore.setQueryParams(queryParams);
|
||||
}
|
||||
|
||||
async updateQueryParams(processId: number) {
|
||||
const queryParams = { ...this._customerOrderSearchStore.filter?.getQueryParams() };
|
||||
queryParams.main_qs = queryParams.main_qs ?? '';
|
||||
async updateQueryParams(queryParams: Record<string, string>) {
|
||||
await this._router.navigate([], { queryParams });
|
||||
this.updateBreadcrumb(processId, queryParams);
|
||||
await this.updateBreadcrumb(queryParams);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,10 +8,12 @@ import {
|
||||
ViewChildren,
|
||||
QueryList,
|
||||
AfterViewInit,
|
||||
inject,
|
||||
DestroyRef,
|
||||
} from '@angular/core';
|
||||
import { debounceTime, filter, first, map, shareReplay, switchMap, take, takeUntil, withLatestFrom } from 'rxjs/operators';
|
||||
import { debounceTime, filter, first, map, shareReplay, switchMap, takeUntil, withLatestFrom } from 'rxjs/operators';
|
||||
import { KeyValueDTOOfStringAndString, OrderItemListItemDTO } from '@swagger/oms';
|
||||
import { ActivatedRoute, Params } from '@angular/router';
|
||||
import { ActivatedRoute, NavigationStart, Router } from '@angular/router';
|
||||
import { CustomerOrderSearchStore } from '../customer-order-search.store';
|
||||
import { BehaviorSubject, combineLatest, Observable, Subject, Subscription } from 'rxjs';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
@@ -27,6 +29,8 @@ import { CustomerOrdersNavigationService } from '@shared/services';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { Filter, FilterInputGroupMainComponent } from '@shared/components/filter';
|
||||
import { CustomerOrderItemComponent } from './customer-order-item.component';
|
||||
import { CacheService } from '@core/cache';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
|
||||
export interface CustomerOrderSearchResultsState {
|
||||
selectedOrderItemSubsetIds: number[];
|
||||
@@ -46,6 +50,8 @@ export class CustomerOrderSearchResultsComponent extends ComponentStore<Customer
|
||||
@ViewChild(FilterInputGroupMainComponent, { static: false })
|
||||
sharedFilterInputGroupMain: FilterInputGroupMainComponent;
|
||||
|
||||
destroyRef = inject(DestroyRef);
|
||||
|
||||
items$: Observable<OrderItemListItemDTO[]> = this._customerOrderSearchStore.results$;
|
||||
|
||||
itemLength$ = this.items$.pipe(map((items) => items?.length));
|
||||
@@ -99,10 +105,7 @@ export class CustomerOrderSearchResultsComponent extends ComponentStore<Customer
|
||||
|
||||
private _searchResultSubscription = new Subscription();
|
||||
|
||||
filter$ = this._customerOrderSearchStore.filter$.pipe(
|
||||
filter((f) => !!f),
|
||||
take(1)
|
||||
);
|
||||
filter$ = this._customerOrderSearchStore.filter$.pipe(filter((f) => !!f));
|
||||
|
||||
hasFilter$ = combineLatest([this.filter$, this._customerOrderSearchStore.defaultSettings$]).pipe(
|
||||
map(([filter, defaultFilter]) => !isEqual(filter?.getQueryParams(), Filter.create(defaultFilter).getQueryParams()))
|
||||
@@ -133,6 +136,8 @@ export class CustomerOrderSearchResultsComponent extends ComponentStore<Customer
|
||||
return this._environment.matchTablet$.pipe(map((matches) => !matches && this._activatedRoute.outlet === 'primary'));
|
||||
}
|
||||
|
||||
private readonly SCROLL_POSITION_TOKEN = 'CUSTOMER_ORDERS_LIST_SCROLL_POSITION';
|
||||
|
||||
constructor(
|
||||
private _customerOrderSearchStore: CustomerOrderSearchStore,
|
||||
private _activatedRoute: ActivatedRoute,
|
||||
@@ -141,7 +146,9 @@ export class CustomerOrderSearchResultsComponent extends ComponentStore<Customer
|
||||
private _modal: UiModalService,
|
||||
private _environment: EnvironmentService,
|
||||
private _navigationService: CustomerOrdersNavigationService,
|
||||
private _application: ApplicationService
|
||||
private _application: ApplicationService,
|
||||
private _cache: CacheService,
|
||||
private _router: Router
|
||||
) {
|
||||
super({
|
||||
selectedOrderItemSubsetIds: [],
|
||||
@@ -165,7 +172,7 @@ export class CustomerOrderSearchResultsComponent extends ComponentStore<Customer
|
||||
this._searchResultSubscription.add(
|
||||
this.processId$
|
||||
.pipe(
|
||||
debounceTime(10),
|
||||
debounceTime(150),
|
||||
withLatestFrom(this._activatedRoute.queryParams),
|
||||
switchMap(([processId, params]) =>
|
||||
this._application.getSelectedBranch$(processId).pipe(map((selectedBranch) => ({ processId, params, selectedBranch })))
|
||||
@@ -174,46 +181,67 @@ export class CustomerOrderSearchResultsComponent extends ComponentStore<Customer
|
||||
.subscribe(async ({ processId, params, selectedBranch }) => {
|
||||
const branchChanged = selectedBranch?.id !== this._customerOrderSearchStore?.selectedBranch?.id;
|
||||
|
||||
const processChanged = processId !== this._customerOrderSearchStore.processId;
|
||||
|
||||
if (processChanged) {
|
||||
if (!!this._customerOrderSearchStore.processId && this._customerOrderSearchStore.filter instanceof Filter) {
|
||||
const queryToken = {
|
||||
...this._customerOrderSearchStore.filter?.getQueryParams(),
|
||||
processId,
|
||||
branchId: String(selectedBranch?.id),
|
||||
};
|
||||
this._customerOrderSearchStore.setCache({
|
||||
queryToken,
|
||||
hits: this._customerOrderSearchStore.hits,
|
||||
results: this._customerOrderSearchStore.results,
|
||||
});
|
||||
await this.updateBreadcrumb(processId, this._customerOrderSearchStore.filter?.getQueryParams());
|
||||
}
|
||||
this._customerOrderSearchStore.patchState({ processId });
|
||||
}
|
||||
|
||||
if (branchChanged) {
|
||||
this._customerOrderSearchStore.setBranch(selectedBranch);
|
||||
}
|
||||
|
||||
this._customerOrderSearchStore.setQueryParams(params);
|
||||
|
||||
if (!(this._customerOrderSearchStore.filter instanceof UiFilter)) {
|
||||
await this._customerOrderSearchStore.loadDefaultSettings();
|
||||
}
|
||||
|
||||
if (this._customerOrderSearchStore.processId !== processId) {
|
||||
this.scrollItemIntoView();
|
||||
this.removeBreadcrumbs(processId);
|
||||
const cleanQueryParams = this.cleanupQueryParams(params);
|
||||
|
||||
this._customerOrderSearchStore.patchState({ processId });
|
||||
if (!isEqual(cleanQueryParams, this.cleanupQueryParams(this._customerOrderSearchStore.filter.getQueryParams()))) {
|
||||
this._customerOrderSearchStore.setQueryParams(params);
|
||||
const queryToken = {
|
||||
...this._customerOrderSearchStore.filter.getQueryParams(),
|
||||
processId,
|
||||
branchId: String(selectedBranch?.id),
|
||||
};
|
||||
const data = this._customerOrderSearchStore.getCachedData({ queryToken });
|
||||
|
||||
const cache = this._customerOrderSearchStore.getCachedData();
|
||||
|
||||
if (cache?.results?.length > 0) {
|
||||
if (data?.results?.length > 0) {
|
||||
this._customerOrderSearchStore.patchState({
|
||||
results: cache?.results,
|
||||
hits: cache?.hits,
|
||||
results: data?.results,
|
||||
hits: data?.hits,
|
||||
});
|
||||
await this.updateBreadcrumb(
|
||||
this._customerOrderSearchStore.processId,
|
||||
this._customerOrderSearchStore.filter?.getQueryParams()
|
||||
);
|
||||
} else if (this._customerOrderSearchStore?.results?.length === 0) {
|
||||
this._customerOrderSearchStore.search({ siletReload: true });
|
||||
}
|
||||
|
||||
if (
|
||||
data.results?.length === 0 &&
|
||||
this._activatedRoute?.parent?.children?.find((childRoute) => childRoute?.outlet === 'side')?.snapshot?.routeConfig?.path !==
|
||||
'filter'
|
||||
) {
|
||||
this.search({ clear: true });
|
||||
}
|
||||
} else if (branchChanged) {
|
||||
this.search({ clear: true });
|
||||
this._customerOrderSearchStore.search({ siletReload: true });
|
||||
}
|
||||
|
||||
if (this._customerOrderSearchStore.getCachedData()?.results?.length > 0 && !this.isDesktopLarge) {
|
||||
const scrollPos = params?.scroll_position;
|
||||
if (!!scrollPos) {
|
||||
const scrollPos = this._getScrollPositionFromCache();
|
||||
if (!!scrollPos && this._activatedRoute.outlet === 'primary') {
|
||||
setTimeout(() => {
|
||||
this.scrollContainer?.scrollTo(scrollPos);
|
||||
}
|
||||
this.removeScrollPosition(params);
|
||||
}, 150);
|
||||
}
|
||||
|
||||
const process = await this._application.getProcessById$(processId).pipe(first()).toPromise();
|
||||
@@ -231,7 +259,7 @@ export class CustomerOrderSearchResultsComponent extends ComponentStore<Customer
|
||||
this._searchResultSubscription.add(
|
||||
this._customerOrderSearchStore.searchResultSubject.pipe(withLatestFrom(this.processId$)).subscribe(async ([result, processId]) => {
|
||||
const queryParams = this._customerOrderSearchStore.filter?.getQueryParams();
|
||||
await this.createBreadcrumb(processId, queryParams);
|
||||
this.createBreadcrumb(processId, queryParams);
|
||||
|
||||
if (result.results.hits === 0) {
|
||||
return;
|
||||
@@ -257,25 +285,68 @@ export class CustomerOrderSearchResultsComponent extends ComponentStore<Customer
|
||||
this._searchResultSubscription.add(
|
||||
this._customerOrderSearchStore.searchStarted.subscribe(async (_) => {
|
||||
const queryParams = {
|
||||
...this.cleanupQueryParams(this._customerOrderSearchStore.filter.getQueryParams()),
|
||||
...this.cleanupQueryParams(this._customerOrderSearchStore?.filter?.getQueryParams()),
|
||||
main_qs: this.sharedFilterInputGroupMain?.uiInput?.value,
|
||||
};
|
||||
|
||||
this._customerOrderSearchStore.setQueryParams(queryParams);
|
||||
this._customerOrderSearchStore?.setQueryParams(queryParams);
|
||||
})
|
||||
);
|
||||
|
||||
this._router.events.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((event) => {
|
||||
if (event instanceof NavigationStart) {
|
||||
this._addScrollPositionToCache();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
this.scrollItemIntoView();
|
||||
}
|
||||
|
||||
private _removeScrollPositionFromCache(): void {
|
||||
this._cache.delete({ processId: this._customerOrderSearchStore.processId, token: this.SCROLL_POSITION_TOKEN });
|
||||
}
|
||||
|
||||
private _addScrollPositionToCache(): void {
|
||||
if (this._activatedRoute.outlet === 'primary') {
|
||||
this._cache.set<number>(
|
||||
{ processId: this._customerOrderSearchStore.processId, token: this.SCROLL_POSITION_TOKEN },
|
||||
this.scrollContainer?.scrollPos
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private _getScrollPositionFromCache(): number {
|
||||
return this._cache.get<number>({ processId: this._customerOrderSearchStore.processId, token: this.SCROLL_POSITION_TOKEN });
|
||||
}
|
||||
|
||||
// After Navigating to Result Side Outlet
|
||||
scrollItemIntoView() {
|
||||
setTimeout(() => {
|
||||
const getPrimaryRouteParams = this._activatedRoute.parent?.children[0]?.snapshot?.params;
|
||||
const item = this.listItems?.find((item) => item.item.orderId === Number(getPrimaryRouteParams?.orderId));
|
||||
item?.scrollIntoView();
|
||||
}, 150);
|
||||
}
|
||||
|
||||
async ngOnDestroy() {
|
||||
this._onDestroy$.next();
|
||||
this._onDestroy$.complete();
|
||||
|
||||
this._searchResultSubscription.unsubscribe();
|
||||
|
||||
const queryToken = {
|
||||
...this._customerOrderSearchStore.filter?.getQueryParams(),
|
||||
processId: this._customerOrderSearchStore.processId,
|
||||
branchId: String(this._customerOrderSearchStore.selectedBranch?.id),
|
||||
};
|
||||
this._customerOrderSearchStore.setCache({
|
||||
queryToken,
|
||||
hits: this._customerOrderSearchStore.hits,
|
||||
results: this._customerOrderSearchStore.results,
|
||||
});
|
||||
|
||||
await this.updateBreadcrumb(this._customerOrderSearchStore.processId, this._customerOrderSearchStore.filter?.getQueryParams());
|
||||
}
|
||||
|
||||
@@ -293,14 +364,6 @@ export class CustomerOrderSearchResultsComponent extends ComponentStore<Customer
|
||||
return clean;
|
||||
}
|
||||
|
||||
removeScrollPosition(queryParams: Params) {
|
||||
const scroll_pos = queryParams?.scroll_position;
|
||||
if (!!scroll_pos) {
|
||||
const clean = { ...queryParams };
|
||||
delete clean['scroll_position'];
|
||||
}
|
||||
}
|
||||
|
||||
async removeBreadcrumbs(processId: number) {
|
||||
const editCrumbs = await this._breadcrumb.getBreadcrumbsByKeyAndTags$(processId, ['customer-order', 'edit']).pipe(first()).toPromise();
|
||||
|
||||
@@ -349,15 +412,12 @@ export class CustomerOrderSearchResultsComponent extends ComponentStore<Customer
|
||||
.pipe(first())
|
||||
.toPromise();
|
||||
|
||||
const scroll_position = this.scrollContainer?.scrollPos;
|
||||
|
||||
const name = queryParams.main_qs ? queryParams.main_qs : 'Alle Artikel';
|
||||
const params = { ...queryParams, scroll_position };
|
||||
|
||||
for (const crumb of crumbs) {
|
||||
this._breadcrumb.patchBreadcrumb(crumb.id, {
|
||||
name,
|
||||
params,
|
||||
params: queryParams,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -369,13 +429,6 @@ export class CustomerOrderSearchResultsComponent extends ComponentStore<Customer
|
||||
return input?.replace('ORD:', '') ?? 'Alle';
|
||||
}
|
||||
|
||||
scrollItemIntoView() {
|
||||
setTimeout(() => {
|
||||
const item = this.listItems?.find((item) => item.item.orderId === Number(this._activatedRoute?.snapshot?.params?.orderId));
|
||||
item?.scrollIntoView();
|
||||
}, 150);
|
||||
}
|
||||
|
||||
async loadMore() {
|
||||
if (
|
||||
this._customerOrderSearchStore.hits > this._customerOrderSearchStore.results.length &&
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
export const CustomerLabelColor = {
|
||||
Abholfachbestellung: '#EDEFF0',
|
||||
'Versandbestellung (oder gemischt)': '#EDEFF0',
|
||||
'Bestellung ohne Konto': '#EDEFF0',
|
||||
Onlinekonto: '#804279',
|
||||
'Onlinekonto mit Kundenkarte': '#804279',
|
||||
'Business Konto (auf Rechnung)': '#804279',
|
||||
@@ -10,6 +11,7 @@ export const CustomerLabelColor = {
|
||||
|
||||
export const CustomerLabelTextColor = {
|
||||
Abholfachbestellung: '#000000',
|
||||
'Bestellung ohne Konto': '#000000',
|
||||
'Versandbestellung (oder gemischt)': '#000000',
|
||||
Onlinekonto: '#FFFFFF',
|
||||
'Onlinekonto mit Kundenkarte': '#FFFFFF',
|
||||
|
||||
@@ -4,10 +4,10 @@ import { AbstractControl, AsyncValidatorFn, UntypedFormControl, UntypedFormGroup
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { CrmCustomerService } from '@domain/crm';
|
||||
import { AddressDTO, CustomerDTO, PayerDTO, ShippingAddressDTO } from '@swagger/crm';
|
||||
import { AddressDTO, CustomerDTO, CustomerInfoDTO, PayerDTO, ShippingAddressDTO } from '@swagger/crm';
|
||||
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
|
||||
import { UiValidators } from '@ui/validators';
|
||||
import { isNull } from 'lodash';
|
||||
import { isNull, merge } from 'lodash';
|
||||
import { BehaviorSubject, Observable, of, Subject } from 'rxjs';
|
||||
import {
|
||||
first,
|
||||
@@ -24,7 +24,12 @@ import {
|
||||
} from 'rxjs/operators';
|
||||
import { AddressFormBlockComponent, DeviatingAddressFormBlockComponent, DeviatingAddressFormBlockData } from '../components/form-blocks';
|
||||
import { FormBlock } from '../components/form-blocks/form-block';
|
||||
import { CustomerCreateFormData, decodeFormData, encodeFormData } from './customer-create-form-data';
|
||||
import {
|
||||
CustomerCreateFormData,
|
||||
decodeFormData,
|
||||
encodeFormData,
|
||||
mapCustomerInfoDtoToCustomerCreateFormData,
|
||||
} from './customer-create-form-data';
|
||||
import { AddressSelectionModalService } from '../modals';
|
||||
import { CustomerCreateNavigation, CustomerSearchNavigation } from '@shared/services';
|
||||
|
||||
@@ -224,7 +229,33 @@ export abstract class AbstractCreateCustomer implements OnInit, OnDestroy {
|
||||
const customerId = this.formData?._meta?.customerDto?.id ?? this.formData?._meta?.customerInfoDto?.id;
|
||||
return this.customerService.checkLoyaltyCard({ loyaltyCardNumber: value, customerId }).pipe(
|
||||
map((response) => {
|
||||
return !response?.error && (response as any)?.result === 1 ? null : { invalid: 'Kundenkartencode ist ungültig' };
|
||||
if (response.error) {
|
||||
throw response.message;
|
||||
}
|
||||
|
||||
/**
|
||||
* #4485 Kubi // Verhalten mit angelegte aber nicht verknüpfte Kundenkartencode in Kundensuche und Kundendaten erfassen ist nicht gleich
|
||||
* Fall1: Kundenkarte hat Daten in point4more:
|
||||
* Sobald Kundenkartencode in Feld "Kundenkartencode" reingegeben wird- werden die Daten von point4more in Formular "Kundendaten Erfassen" eingefügt und ersetzen (im Ganzen, nicht inkremental) die Daten in Felder, falls welche schon reingetippt werden.
|
||||
* Fall2: Kundenkarte hat keine Daten in point4more:
|
||||
* Sobald Kundenkartencode in Feld "Kundenkartencode" reingegeben wird- bleiben die Daten in Formular "Kundendaten Erfassen" in Felder, falls welche schon reingetippt werden.
|
||||
*/
|
||||
if (response.result && response.result.customer) {
|
||||
const customer = response.result.customer;
|
||||
const data = mapCustomerInfoDtoToCustomerCreateFormData(customer);
|
||||
|
||||
if (data.name.firstName && data.name.lastName) {
|
||||
// Fall1
|
||||
this._formData.next(data);
|
||||
} else {
|
||||
// Fall2 Hier müssen die Metadaten gesetzt werden um eine verknüfung zur kundenkarte zu ermöglichen.
|
||||
const current = this.formData;
|
||||
current._meta = data._meta;
|
||||
current.p4m = data.p4m;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}),
|
||||
catchError((error) => {
|
||||
if (error instanceof HttpErrorResponse) {
|
||||
|
||||
@@ -56,7 +56,7 @@ export class CreateWebshopCustomerComponent extends AbstractCreateCustomer {
|
||||
async saveCustomer(customer: CustomerDTO): Promise<CustomerDTO> {
|
||||
const { customerDto, customerInfoDto } = this.formData?._meta ?? {};
|
||||
|
||||
const isUpgrade = !!(customerDto || customerInfoDto);
|
||||
const isUpgrade = !!(customerDto || customerInfoDto)?.id;
|
||||
|
||||
if (isUpgrade) {
|
||||
if (customerDto) {
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
[checked]="(selectedPayer$ | async)?.payer.id === assignedPayer.payer.id"
|
||||
(change)="selectPayer(assignedPayer)"
|
||||
/>
|
||||
<div class="flex flex-row justify-between items-start grow">
|
||||
<div class="ml-2 flex flex-row justify-between items-start grow">
|
||||
<span class="mr-4">
|
||||
{{ assignedPayer.payer.data | address }}
|
||||
</span>
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
<div class="customer-details-customer-main-data px-5 py-3 grid grid-flow-row gap-3">
|
||||
<div class="flex flex-row">
|
||||
<div class="data-label">Erstellungsdatum</div>
|
||||
<div class="data-value">{{ created$ | async | date: 'dd.MM.yyyy' }} | {{ created$ | async | date: 'hh:mm' }} Uhr</div>
|
||||
<div class="data-value">{{ created$ | async | date: 'dd.MM.yyyy' }} | {{ created$ | async | date: 'HH:mm' }} Uhr</div>
|
||||
</div>
|
||||
<div class="flex flex-row">
|
||||
<div class="data-label">Kundennummer</div>
|
||||
|
||||
@@ -13,6 +13,7 @@ import { CheckoutNavigationService, ProductCatalogNavigationService } from '@sha
|
||||
import { Router } from '@angular/router';
|
||||
import { log, logAsync } from '@utils/common';
|
||||
import { CrmCustomerService } from '@domain/crm';
|
||||
import { MessageModalComponent, MessageModalData } from '@shared/modals/message-modal';
|
||||
|
||||
const GENDER_MAP = {
|
||||
2: 'Herr',
|
||||
@@ -332,21 +333,44 @@ export class CustomerDetailsViewMainComponent extends ComponentStore<CustomerDet
|
||||
|
||||
@logAsync
|
||||
async _canAddShippingAddressAsync() {
|
||||
const res = await this._checkoutService
|
||||
.canAddDestination({
|
||||
processId: this.processId,
|
||||
destinationDTO: {
|
||||
target: 2,
|
||||
shippingAddress: this.shippingAddress,
|
||||
},
|
||||
})
|
||||
.toPromise();
|
||||
try {
|
||||
const res = await this._checkoutService
|
||||
.canAddDestination({
|
||||
processId: this.processId,
|
||||
destinationDTO: {
|
||||
target: 2,
|
||||
shippingAddress: this.shippingAddress,
|
||||
},
|
||||
})
|
||||
.toPromise();
|
||||
|
||||
if (typeof res === 'string') {
|
||||
throw new Error(res);
|
||||
if (typeof res === 'string') {
|
||||
throw new Error(res);
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
this._modalService.open({
|
||||
content: MessageModalComponent,
|
||||
title: 'Warenkorb kann dem Kunden nicht zugewiesen werden',
|
||||
data: {
|
||||
message: 'Dieser Versand ist nur innerhalb Deutschlands möglich.',
|
||||
actions: [
|
||||
{ label: 'OK' },
|
||||
{
|
||||
label: 'Lieferadresse hinzufügen',
|
||||
primary: true,
|
||||
action: () => {
|
||||
const nav = this._navigation.addShippingAddressRoute({ processId: this.processId, customerId: this.customer.id });
|
||||
this._router.navigate(nav.path, { queryParams: nav.queryParams, queryParamsHandling: 'merge' });
|
||||
},
|
||||
},
|
||||
],
|
||||
} as MessageModalData,
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@logAsync
|
||||
@@ -387,7 +411,9 @@ export class CustomerDetailsViewMainComponent extends ComponentStore<CustomerDet
|
||||
_patchProcessName() {
|
||||
let name = `${this.customer.firstName} ${this.customer.lastName}`;
|
||||
|
||||
if (this._store.isBusinessKonto) {
|
||||
// Ticket #4458 Es kann vorkommen, dass B2B Konten keinen Firmennamen hinterlegt haben
|
||||
// zusätzlich kanne es bei Mitarbeiter Konten vorkommen, dass die Namen in der Organisation statt im Kundennamen hinterlegt sind
|
||||
if ((this._store.isBusinessKonto && this.customer.organisation?.name) || (!this.customer.firstName && !this.customer.lastName)) {
|
||||
name = `${this.customer.organisation?.name}`;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,4 +10,10 @@
|
||||
[loading]="fetching$ | async"
|
||||
[hint]="message$ | async"
|
||||
></shared-filter-input-group-main>
|
||||
<p class="mt-6">
|
||||
Kunde nicht gefunden?
|
||||
<a class="text-brand" *ngIf="createRoute$ | async; let route" [routerLink]="route.path" [queryParams]="route.queryParams">
|
||||
Neue Kundendaten erfassen
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -3,6 +3,10 @@ import { Component, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { Filter, FilterModule } from '@shared/components/filter';
|
||||
import { CustomerSearchStore } from '../store';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { CustomerCreateNavigation } from '@shared/services';
|
||||
import { combineLatest } from 'rxjs';
|
||||
import { CustomerInfoDTO } from '@swagger/crm';
|
||||
|
||||
@Component({
|
||||
selector: 'page-customer-main-side-view',
|
||||
@@ -19,7 +23,29 @@ export class MainSideViewComponent {
|
||||
|
||||
fetching$ = this._store.fetchingCustomerList$;
|
||||
|
||||
constructor(private _store: CustomerSearchStore) {}
|
||||
createRoute$ = combineLatest(this.filter$, this._store.processId$).pipe(
|
||||
map(([filter, processId]) => {
|
||||
const queryParams = filter?.getQueryParams();
|
||||
|
||||
let customerInfo: CustomerInfoDTO;
|
||||
|
||||
if (queryParams?.main_qs) {
|
||||
const isMail = queryParams.main_qs.includes('@');
|
||||
customerInfo = {
|
||||
lastName: !isMail ? queryParams.main_qs : undefined,
|
||||
communicationDetails: isMail
|
||||
? {
|
||||
email: queryParams.main_qs,
|
||||
}
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
return this._customerCreateNavigation.createCustomerRoute({ processId, customerInfo });
|
||||
})
|
||||
);
|
||||
|
||||
constructor(private _store: CustomerSearchStore, private _customerCreateNavigation: CustomerCreateNavigation) {}
|
||||
|
||||
search(filter: Filter) {
|
||||
this._store.setFilter(filter);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
:host {
|
||||
@apply bg-surface text-surface-content rounded grid grid-flow-row h-full;
|
||||
@apply bg-surface text-surface-content rounded grid grid-flow-row h-full side-view-shadow;
|
||||
}
|
||||
|
||||
.side-view-shadow {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div class="desktop-large:hidden">
|
||||
<div class="text-center pt-10 px-8 rounded-card side-view-shadow grow">
|
||||
<div class="text-center pt-10 px-8 rounded-card grow">
|
||||
<h1 class="text-[1.625rem] font-bold">Kundensuche</h1>
|
||||
<p class="text-lg mt-2 mb-6">
|
||||
Haben Sie ein Konto bei uns?
|
||||
@@ -28,6 +28,12 @@
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
<p class="mt-6">
|
||||
Kunde nicht gefunden?
|
||||
<a class="text-brand" *ngIf="createRoute$ | async; let route" [routerLink]="route.path" [queryParams]="route.queryParams">
|
||||
Neue Kundendaten erfassen
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hidden desktop-large:block">
|
||||
|
||||
@@ -6,9 +6,10 @@ import { AsyncPipe, NgIf } from '@angular/common';
|
||||
import { Router, RouterLink } from '@angular/router';
|
||||
import { IconComponent } from '@shared/components/icon';
|
||||
import { combineLatest } from 'rxjs';
|
||||
import { CustomerSearchNavigation } from '@shared/services';
|
||||
import { CustomerSearchNavigation, CustomerCreateNavigation } from '@shared/services';
|
||||
import { CustomerFilterMainViewModule } from '../filter-main-view/filter-main-view.module';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { CustomerInfoDTO } from '@swagger/crm';
|
||||
|
||||
@Component({
|
||||
selector: 'page-customer-main-view',
|
||||
@@ -29,6 +30,28 @@ export class CustomerMainViewComponent {
|
||||
})
|
||||
);
|
||||
|
||||
createRoute$ = combineLatest(this._store.filter$, this._store.processId$).pipe(
|
||||
map(([filter, processId]) => {
|
||||
const queryParams = filter?.getQueryParams();
|
||||
|
||||
let customerInfo: CustomerInfoDTO;
|
||||
|
||||
if (queryParams?.main_qs) {
|
||||
const isMail = queryParams.main_qs.includes('@');
|
||||
customerInfo = {
|
||||
lastName: !isMail ? queryParams.main_qs : undefined,
|
||||
communicationDetails: isMail
|
||||
? {
|
||||
email: queryParams.main_qs,
|
||||
}
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
return this._customerCreateNavigation.createCustomerRoute({ processId, customerInfo });
|
||||
})
|
||||
);
|
||||
|
||||
filter$ = this._store.filter$;
|
||||
|
||||
hasFilter$ = this.filter$.pipe(
|
||||
@@ -43,7 +66,12 @@ export class CustomerMainViewComponent {
|
||||
|
||||
message$ = this._store.message$;
|
||||
|
||||
constructor(private _searchNavigation: CustomerSearchNavigation, private _store: CustomerSearchStore, private _router: Router) {}
|
||||
constructor(
|
||||
private _searchNavigation: CustomerSearchNavigation,
|
||||
private _customerCreateNavigation: CustomerCreateNavigation,
|
||||
private _store: CustomerSearchStore,
|
||||
private _router: Router
|
||||
) {}
|
||||
|
||||
search(filter: Filter) {
|
||||
this._store.setFilter(filter);
|
||||
|
||||
@@ -12,6 +12,7 @@ import { isEmpty } from 'lodash';
|
||||
import { DomainOmsService } from '@domain/oms';
|
||||
import { OrderDTO, OrderListItemDTO } from '@swagger/oms';
|
||||
import { hash } from '@utils/common';
|
||||
import { UiModalService } from '@ui/modal';
|
||||
|
||||
@Injectable()
|
||||
export class CustomerSearchStore extends ComponentStore<CustomerSearchState> implements OnStoreInit, OnDestroy {
|
||||
@@ -163,7 +164,7 @@ export class CustomerSearchStore extends ComponentStore<CustomerSearchState> imp
|
||||
|
||||
selectedOrderItem$ = this.select(S.selectSelectedOrderItem);
|
||||
|
||||
constructor(private _customerService: CrmCustomerService, private _omsService: DomainOmsService) {
|
||||
constructor(private _customerService: CrmCustomerService, private _omsService: DomainOmsService, private _modal: UiModalService) {
|
||||
super({ customerListCount: 0 });
|
||||
}
|
||||
|
||||
@@ -205,7 +206,8 @@ export class CustomerSearchStore extends ComponentStore<CustomerSearchState> imp
|
||||
};
|
||||
|
||||
handleSelectCustomerError = (err: any) => {
|
||||
console.error(err);
|
||||
this._modal.error('Fehler beim Auswählen des Kundens', err);
|
||||
this.patchState({ fetchingCustomer: false });
|
||||
};
|
||||
|
||||
handleSelectCustomerComplete = () => {
|
||||
@@ -230,7 +232,8 @@ export class CustomerSearchStore extends ComponentStore<CustomerSearchState> imp
|
||||
};
|
||||
|
||||
handleSelectOrderError = (err: any) => {
|
||||
console.error(err);
|
||||
this._modal.error('Fehler beim Auswählen der Bestellung', err);
|
||||
this.patchState({ fetchingOrder: false });
|
||||
};
|
||||
|
||||
handleSelectOrderComplete = () => {
|
||||
@@ -259,7 +262,8 @@ export class CustomerSearchStore extends ComponentStore<CustomerSearchState> imp
|
||||
};
|
||||
|
||||
handleFetchCustomerOrdersError = (err: any) => {
|
||||
console.error(err);
|
||||
this._modal.error('Fehler beim Laden der Kundenbestellungen', err);
|
||||
this.patchState({ fetchingCustomerOrders: false });
|
||||
};
|
||||
|
||||
handleFetchCustomerOrdersComplete = () => {
|
||||
@@ -282,7 +286,8 @@ export class CustomerSearchStore extends ComponentStore<CustomerSearchState> imp
|
||||
};
|
||||
|
||||
handleFetchFilterError = (err: any) => {
|
||||
console.error(err);
|
||||
this._modal.error('Fehler beim Laden der Filter', err);
|
||||
this.patchState({ fetchingFilter: false });
|
||||
};
|
||||
|
||||
handleFetchFilterComplete = () => {
|
||||
@@ -341,7 +346,8 @@ export class CustomerSearchStore extends ComponentStore<CustomerSearchState> imp
|
||||
};
|
||||
|
||||
handleSearchError = (err: any) => {
|
||||
console.error(err);
|
||||
this._modal.error('Fehler beim Laden der Liste', err);
|
||||
this.patchState({ fetchingCustomerList: false });
|
||||
};
|
||||
|
||||
handleSearchComplete = () => {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { Component, ChangeDetectionStrategy, Input } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { ProductsFeed } from '@domain/isa';
|
||||
import { ProductCatalogNavigationService } from '@shared/services';
|
||||
import { first } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'page-products-card',
|
||||
@@ -12,9 +14,19 @@ export class ProductsCardComponent {
|
||||
@Input()
|
||||
feed: ProductsFeed;
|
||||
|
||||
constructor(private _router: Router) {}
|
||||
constructor(private _navigation: ProductCatalogNavigationService, private _app: ApplicationService) {}
|
||||
|
||||
navigatetToProduct(ean: string) {
|
||||
this._router.navigate(['/kunde/product/details/ean', ean]);
|
||||
async navigatetToProduct(ean: string) {
|
||||
let processes = await this._app.getProcesses$('customer').pipe(first()).toPromise();
|
||||
|
||||
processes = processes.sort((a, b) => b.activated - a.activated);
|
||||
|
||||
this._navigation
|
||||
.getArticleDetailsPathByEan({
|
||||
processId: processes[0]?.id ?? Date.now(),
|
||||
ean,
|
||||
extras: { queryParams: { main_qs: this.feed.items.map((i) => i.product.ean).join(';') } },
|
||||
})
|
||||
.navigate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,6 +100,7 @@ export class GoodsInCleanupListComponent implements OnInit, OnDestroy {
|
||||
key: this._config.get('process.ids.goodsIn'),
|
||||
name: 'Abholfachbereinigungsliste',
|
||||
path: '/filiale/goods/in/cleanup',
|
||||
params: { view: 'cleanup' },
|
||||
section: 'branch',
|
||||
tags: ['goods-in', 'cleanup'],
|
||||
});
|
||||
@@ -188,9 +189,9 @@ export class GoodsInCleanupListComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
navigateToDetails(orderItem: OrderItemListItemDTO) {
|
||||
const nav = this._pickupShelfInNavigationService.detailRoute({ item: orderItem });
|
||||
const nav = this._pickupShelfInNavigationService.detailRoute({ item: orderItem, side: false });
|
||||
|
||||
this._router.navigate(nav.path, { queryParams: nav.queryParams });
|
||||
this._router.navigate(nav.path, { queryParams: { ...nav.queryParams, view: 'cleanup' } });
|
||||
}
|
||||
|
||||
async handleAction(action: KeyValueDTOOfStringAndString) {
|
||||
|
||||
@@ -107,8 +107,8 @@ export class GoodsInListComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
await this.updateBreadcrumb({ queryParams: params });
|
||||
await this.createBreadcrumb({ queryParams: params });
|
||||
await this.updateBreadcrumb(params);
|
||||
await this.createBreadcrumb(params);
|
||||
await this.removeBreadcrumbs();
|
||||
});
|
||||
}
|
||||
@@ -124,6 +124,7 @@ export class GoodsInListComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
const clean = { ...params };
|
||||
delete clean['scroll_position'];
|
||||
delete clean['take'];
|
||||
delete clean['view'];
|
||||
|
||||
for (const key in clean) {
|
||||
if (Object.prototype.hasOwnProperty.call(clean, key)) {
|
||||
@@ -147,9 +148,9 @@ export class GoodsInListComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
return;
|
||||
}
|
||||
|
||||
const nav = this._pickupShelfInNavigationService.detailRoute({ item: orderItem });
|
||||
const nav = this._pickupShelfInNavigationService.detailRoute({ item: orderItem, side: false });
|
||||
|
||||
this._router.navigate(nav.path, { queryParams: nav.queryParams });
|
||||
this._router.navigate(nav.path, { queryParams: { ...nav.queryParams, view: 'wareneingangsliste' } });
|
||||
}
|
||||
|
||||
async removeBreadcrumbs() {
|
||||
@@ -207,7 +208,7 @@ export class GoodsInListComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
name: 'Wareneingangsliste',
|
||||
path: '/filiale/goods/in/list',
|
||||
section: 'branch',
|
||||
params: queryParams,
|
||||
params: { ...queryParams, view: 'wareneingangsliste' },
|
||||
tags: ['goods-in', 'list'],
|
||||
});
|
||||
}
|
||||
|
||||
@@ -88,7 +88,11 @@ export class GoodsInListStore extends ComponentStore<GoodsInListState> {
|
||||
const path = '/filiale/goods/in/list/';
|
||||
if (!this._router.isActive(path, false)) {
|
||||
this._router.navigate([path], {
|
||||
queryParams: { ...this.filter.getQueryParams(), take: res.result.length + _results?.length },
|
||||
queryParams: {
|
||||
...this.filter.getQueryParams(),
|
||||
take: res.result.length + _results?.length,
|
||||
view: 'wareneingangsliste',
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,6 +80,7 @@ export class GoodsInRemissionPreviewComponent implements OnInit, OnDestroy {
|
||||
name: 'Abholfachremissionsvorschau',
|
||||
path: '/filiale/goods/in/preview',
|
||||
section: 'branch',
|
||||
params: { view: 'remission' },
|
||||
tags: ['goods-in', 'preview'],
|
||||
});
|
||||
}
|
||||
@@ -148,9 +149,9 @@ export class GoodsInRemissionPreviewComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
navigateToDetails(orderItem: OrderItemListItemDTO) {
|
||||
const nav = this._pickupShelfInNavigationService.detailRoute({ item: orderItem });
|
||||
const nav = this._pickupShelfInNavigationService.detailRoute({ item: orderItem, side: false });
|
||||
|
||||
this._router.navigate(nav.path, { queryParams: nav.queryParams });
|
||||
this._router.navigate(nav.path, { queryParams: { ...nav.queryParams, view: 'remission' } });
|
||||
}
|
||||
|
||||
async handleAction(action: KeyValueDTOOfStringAndString) {
|
||||
|
||||
@@ -102,6 +102,7 @@ export class GoodsInReservationComponent implements OnInit, OnDestroy {
|
||||
name: 'Reservierungen',
|
||||
path: '/filiale/goods/in/reservation',
|
||||
section: 'branch',
|
||||
params: { view: 'reservation' },
|
||||
tags: ['goods-in', 'reservation'],
|
||||
});
|
||||
}
|
||||
@@ -150,22 +151,18 @@ export class GoodsInReservationComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
navigateToDetails(orderItem: OrderItemListItemDTO) {
|
||||
const nav = this._pickupShelfInNavigationService.detailRoute({ item: orderItem });
|
||||
const nav = this._pickupShelfInNavigationService.detailRoute({ item: orderItem, side: false });
|
||||
|
||||
this._router.navigate(nav.path, { queryParams: nav.queryParams });
|
||||
this._router.navigate(nav.path, { queryParams: { ...nav.queryParams, view: 'reservation' } });
|
||||
}
|
||||
|
||||
initInitialSearch() {
|
||||
if (this._store.hits === 0) {
|
||||
this._store.searchResult$.pipe(takeUntil(this._onDestroy$)).subscribe(async (result) => {
|
||||
await this.createBreadcrumb();
|
||||
if (result.hits === 1) {
|
||||
this.navigateToDetails(result.result[0]);
|
||||
} else {
|
||||
if (!!this._store.searchOptions?.take) {
|
||||
this._store.searchOptions = undefined;
|
||||
this.scrollContainer.scrollTo(Number(scroll_position ?? 0));
|
||||
}
|
||||
if (!!this._store.searchOptions?.take) {
|
||||
this._store.searchOptions = undefined;
|
||||
this.scrollContainer.scrollTo(Number(scroll_position ?? 0));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { Component, ChangeDetectionStrategy, OnInit, inject } from '@angular/core';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { Component, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { Config } from '@core/config';
|
||||
import { PickupShelfInNavigationService } from '@shared/services';
|
||||
|
||||
@Component({
|
||||
selector: 'page-goods-in',
|
||||
@@ -9,24 +7,8 @@ import { PickupShelfInNavigationService } from '@shared/services';
|
||||
styleUrls: ['goods-in.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class GoodsInComponent implements OnInit {
|
||||
private _pickupShelfInNavigationService = inject(PickupShelfInNavigationService);
|
||||
|
||||
export class GoodsInComponent {
|
||||
goodsInKey = this._config.get('process.ids.goodsIn');
|
||||
pickupShelfKey = this._config.get('process.ids.pickupShelf');
|
||||
|
||||
constructor(private readonly _config: Config, private _breadcrumb: BreadcrumbService) {}
|
||||
|
||||
ngOnInit() {
|
||||
const nav = this._pickupShelfInNavigationService.defaultRoute();
|
||||
|
||||
this._breadcrumb.addOrUpdateBreadcrumbIfNotExists({
|
||||
key: this._config.get('process.ids.goodsIn'),
|
||||
name: 'Abholfach',
|
||||
path: nav.path,
|
||||
params: nav.queryParams,
|
||||
tags: ['goods-in', 'main', 'filter'],
|
||||
section: 'branch',
|
||||
});
|
||||
}
|
||||
constructor(private readonly _config: Config) {}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import { RunCheckTrigger } from './trigger';
|
||||
import { OrderItemsContext } from '@domain/oms';
|
||||
import { ActionHandlerService } from './services/action-handler.service';
|
||||
import { Config } from '@core/config';
|
||||
import { debounce } from '@utils/common';
|
||||
|
||||
export type GetNameForBreadcrumbData = {
|
||||
processId: number;
|
||||
@@ -64,15 +65,21 @@ export abstract class PickupShelfBaseComponent implements OnInit {
|
||||
this._runChecks();
|
||||
}
|
||||
|
||||
// der debounce soll verhindern, dass die breadcrumb zu oft aktualisiert,
|
||||
// besonders bei asynchronen calls kommt es sonst zu fehlern
|
||||
// @debounce(500)
|
||||
// Auskommentiert, da es zu anderen Problemen führt, siehe z.B. Ticket #4538 oder #4540
|
||||
// Ursprungsproblem des Tickets #4533 konnte anders gelöst werden, somit wird debounce hier nicht mehr benötigt
|
||||
private _runChecks() {
|
||||
const processId = this._checkAndUpdateProcessId();
|
||||
const queryParams = this._checkAndUpdateQueryParams();
|
||||
const view: string = this.activatedRoute.snapshot.data.view;
|
||||
const view: string = this.activatedRoute.snapshot.queryParams.view ?? this.activatedRoute.snapshot.data.view;
|
||||
|
||||
this._checkBreadcrumbs({ processId, queryParams, view });
|
||||
}
|
||||
|
||||
private async _checkBreadcrumbs(data: GetNameAndPathForBreadcrumbData) {
|
||||
await this._removeAllBreadcrumbs();
|
||||
await this._chackAndUpdateMainBreadcrumb(data);
|
||||
await this._checkAndUpdateListBreadcrumb(data);
|
||||
await this._checkAndUpdateFilterBreadcrumb(data);
|
||||
@@ -141,14 +148,17 @@ export abstract class PickupShelfBaseComponent implements OnInit {
|
||||
* @param tag Der gesuchte Tag
|
||||
* @returns Breadcumb
|
||||
*/
|
||||
private _getBreadcrumbByTag(tag: string) {
|
||||
private _getBreadcrumb(tag?: string) {
|
||||
return this.breadcrumbService
|
||||
.getBreadcrumbsByKeyAndTags$(this.listStore.processId, ['pickup-shelf', tag])
|
||||
.getBreadcrumbsByKeyAndTags$(
|
||||
this.listStore.processId,
|
||||
['pickup-shelf', tag].filter((t) => !!t)
|
||||
)
|
||||
.pipe(takeUntilDestroyed(this.destroyRef), take(1))
|
||||
.toPromise();
|
||||
}
|
||||
|
||||
private async _removeExcessBreadcrumbs(breadcrumbs: Breadcrumb[]) {
|
||||
private async _removeBreadcrumbs(breadcrumbs: Breadcrumb[]) {
|
||||
for (let crumb of breadcrumbs) {
|
||||
await this.breadcrumbService.removeBreadcrumb(crumb.id);
|
||||
}
|
||||
@@ -156,14 +166,37 @@ export abstract class PickupShelfBaseComponent implements OnInit {
|
||||
|
||||
abstract getNameForMainBreadcrumb(data: GetNameForBreadcrumbData): string | Promise<string>;
|
||||
|
||||
abstract getPathForMainBreadcrumb(data: GetPathForBreadcrumbData): NavigationRoute | Promise<NavigationRoute>;
|
||||
abstract getPathForMainBreadcrumb(data: GetPathForBreadcrumbData): NavigationRoute | null | Promise<NavigationRoute | null>;
|
||||
|
||||
private async _chackAndUpdateMainBreadcrumb({ processId, queryParams }: GetNameAndPathForBreadcrumbData) {
|
||||
const breadcrumbs = await this._getBreadcrumbByTag('main');
|
||||
private async _removeAllBreadcrumbs() {
|
||||
const crumbs = await this._getBreadcrumb();
|
||||
await this._removeBreadcrumbs(crumbs);
|
||||
}
|
||||
|
||||
private async _chackAndUpdateMainBreadcrumb({ processId, queryParams, view }: GetNameAndPathForBreadcrumbData) {
|
||||
if (view === 'reservation') {
|
||||
await this._checkAndUpdateReservationBreadcrumb({ processId, queryParams, view });
|
||||
return;
|
||||
} else if (view === 'cleanup') {
|
||||
await this._checkAndUpdateCleanupBreadcrumb({ processId, queryParams, view });
|
||||
return;
|
||||
} else if (view === 'wareneingangsliste') {
|
||||
await this._checkAndUpdateWareneingangsListeBreadcrumb({ processId, queryParams, view });
|
||||
return;
|
||||
} else if (view === 'remission') {
|
||||
await this._checkAndUpdateAbholfachremissionsvorschauBreadcrumb({ processId, queryParams, view });
|
||||
return;
|
||||
}
|
||||
|
||||
const breadcrumbs = await this._getBreadcrumb('main');
|
||||
let mainBreadcrumb: Breadcrumb = breadcrumbs.pop();
|
||||
const name = await this.getNameForMainBreadcrumb({ processId, queryParams });
|
||||
const path = await this.getPathForMainBreadcrumb({ processId, queryParams });
|
||||
|
||||
if (!path) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mainBreadcrumb) {
|
||||
mainBreadcrumb = {
|
||||
key: this.listStore.processId,
|
||||
@@ -182,16 +215,146 @@ export abstract class PickupShelfBaseComponent implements OnInit {
|
||||
};
|
||||
this.breadcrumbService.patchBreadcrumb(mainBreadcrumb.id, changes);
|
||||
}
|
||||
}
|
||||
|
||||
await this._removeExcessBreadcrumbs(breadcrumbs);
|
||||
private async _checkAndUpdateReservationBreadcrumb({ processId, queryParams }: GetNameAndPathForBreadcrumbData) {
|
||||
const breadcrumbs = await this._getBreadcrumb('reservation');
|
||||
let reservationBreadcrumb: Breadcrumb = breadcrumbs.pop();
|
||||
|
||||
const name = 'Reservierungen';
|
||||
const path: NavigationRoute = {
|
||||
path: ['/filiale', 'goods', 'in', 'reservation'],
|
||||
queryParams: { view: 'reservation' },
|
||||
urlTree: this.router.createUrlTree(['/filiale', 'goods', 'in', 'reservation'], {}),
|
||||
};
|
||||
|
||||
if (!reservationBreadcrumb) {
|
||||
reservationBreadcrumb = {
|
||||
key: processId,
|
||||
name,
|
||||
tags: ['pickup-shelf', 'reservation'],
|
||||
path: path?.path,
|
||||
section: this.getSection(),
|
||||
params: { ...queryParams, ...path?.queryParams },
|
||||
};
|
||||
this.breadcrumbService.addBreadcrumb(reservationBreadcrumb);
|
||||
} else {
|
||||
const changes: Partial<Breadcrumb> = {
|
||||
name,
|
||||
path: path?.path,
|
||||
params: { ...queryParams, ...path?.queryParams },
|
||||
};
|
||||
this.breadcrumbService.patchBreadcrumb(reservationBreadcrumb.id, changes);
|
||||
}
|
||||
|
||||
await this._removeBreadcrumbs(breadcrumbs);
|
||||
}
|
||||
|
||||
private async _checkAndUpdateCleanupBreadcrumb({ processId, queryParams }: GetNameAndPathForBreadcrumbData) {
|
||||
const breadcrumbs = await this._getBreadcrumb('cleanup');
|
||||
let cleanupBreadcrumb: Breadcrumb = breadcrumbs.pop();
|
||||
|
||||
const name = 'Abholfachbereinigungsliste';
|
||||
const path: NavigationRoute = {
|
||||
path: ['/filiale', 'goods', 'in', 'cleanup'],
|
||||
queryParams: { view: 'cleanup' },
|
||||
urlTree: this.router.createUrlTree(['/filiale', 'goods', 'in', 'cleanup'], {}),
|
||||
};
|
||||
|
||||
if (!cleanupBreadcrumb) {
|
||||
cleanupBreadcrumb = {
|
||||
key: processId,
|
||||
name,
|
||||
tags: ['pickup-shelf', 'cleanup'],
|
||||
path: path?.path,
|
||||
section: this.getSection(),
|
||||
params: { ...queryParams, ...path?.queryParams },
|
||||
};
|
||||
this.breadcrumbService.addBreadcrumb(cleanupBreadcrumb);
|
||||
} else {
|
||||
const changes: Partial<Breadcrumb> = {
|
||||
name,
|
||||
path: path?.path,
|
||||
params: { ...queryParams, ...path?.queryParams },
|
||||
};
|
||||
this.breadcrumbService.patchBreadcrumb(cleanupBreadcrumb.id, changes);
|
||||
}
|
||||
|
||||
await this._removeBreadcrumbs(breadcrumbs);
|
||||
}
|
||||
|
||||
private async _checkAndUpdateWareneingangsListeBreadcrumb({ processId, queryParams }: GetNameAndPathForBreadcrumbData) {
|
||||
const breadcrumbs = await this._getBreadcrumb('wareneingangsliste');
|
||||
let listBreadcrumb: Breadcrumb = breadcrumbs.pop();
|
||||
|
||||
const name = 'Wareneingangsliste';
|
||||
const path: NavigationRoute = {
|
||||
path: ['/filiale', 'goods', 'in', 'list'],
|
||||
queryParams: { view: 'wareneingangsliste' },
|
||||
urlTree: this.router.createUrlTree(['/filiale', 'goods', 'in', 'list'], {}),
|
||||
};
|
||||
|
||||
if (!listBreadcrumb) {
|
||||
listBreadcrumb = {
|
||||
key: processId,
|
||||
name,
|
||||
tags: ['pickup-shelf', 'wareneingangsliste'],
|
||||
path: path?.path,
|
||||
section: this.getSection(),
|
||||
params: { ...queryParams, ...path?.queryParams },
|
||||
};
|
||||
this.breadcrumbService.addBreadcrumb(listBreadcrumb);
|
||||
} else {
|
||||
const changes: Partial<Breadcrumb> = {
|
||||
name,
|
||||
path: path?.path,
|
||||
params: { ...queryParams, ...path?.queryParams },
|
||||
};
|
||||
this.breadcrumbService.patchBreadcrumb(listBreadcrumb.id, changes);
|
||||
}
|
||||
|
||||
await this._removeBreadcrumbs(breadcrumbs);
|
||||
}
|
||||
|
||||
private async _checkAndUpdateAbholfachremissionsvorschauBreadcrumb({ processId, queryParams }: GetNameAndPathForBreadcrumbData) {
|
||||
const breadcrumbs = await this._getBreadcrumb('preview');
|
||||
let listBreadcrumb: Breadcrumb = breadcrumbs.pop();
|
||||
|
||||
const name = 'Abholfachremissionsvorschau';
|
||||
const path: NavigationRoute = {
|
||||
path: ['/filiale', 'goods', 'in', 'preview'],
|
||||
queryParams: { view: 'remission' },
|
||||
urlTree: this.router.createUrlTree(['/filiale', 'goods', 'in', 'preview'], {}),
|
||||
};
|
||||
|
||||
if (!listBreadcrumb) {
|
||||
listBreadcrumb = {
|
||||
key: processId,
|
||||
name,
|
||||
tags: ['pickup-shelf', 'preview'],
|
||||
path: path?.path,
|
||||
section: this.getSection(),
|
||||
params: { ...queryParams, ...path?.queryParams },
|
||||
};
|
||||
this.breadcrumbService.addBreadcrumb(listBreadcrumb);
|
||||
} else {
|
||||
const changes: Partial<Breadcrumb> = {
|
||||
name,
|
||||
path: path?.path,
|
||||
params: { ...queryParams, ...path?.queryParams },
|
||||
};
|
||||
this.breadcrumbService.patchBreadcrumb(listBreadcrumb.id, changes);
|
||||
}
|
||||
|
||||
await this._removeBreadcrumbs(breadcrumbs);
|
||||
}
|
||||
|
||||
abstract getNameForListBreadcrumb(data: GetNameForBreadcrumbData): string | Promise<string>;
|
||||
|
||||
abstract getPathFoListBreadcrumb(data: GetPathForBreadcrumbData): NavigationRoute | Promise<NavigationRoute>;
|
||||
abstract getPathFoListBreadcrumb(data: GetPathForBreadcrumbData): NavigationRoute | null | Promise<NavigationRoute | null>;
|
||||
|
||||
private async _checkAndUpdateListBreadcrumb({ processId, queryParams, view }: GetNameAndPathForBreadcrumbData) {
|
||||
const breadcrumbs = await this._getBreadcrumbByTag('list');
|
||||
const breadcrumbs = await this._getBreadcrumb('list');
|
||||
let listBreadcrumb: Breadcrumb = breadcrumbs.pop();
|
||||
|
||||
const shouldHaveBreadcrumb = ['list', 'filter', 'details', 'edit', 'history'].includes(view);
|
||||
@@ -199,6 +362,11 @@ export abstract class PickupShelfBaseComponent implements OnInit {
|
||||
if (shouldHaveBreadcrumb) {
|
||||
const name = await this.getNameForListBreadcrumb({ processId, queryParams });
|
||||
const path = await this.getPathFoListBreadcrumb({ processId, queryParams });
|
||||
|
||||
if (!path) {
|
||||
return;
|
||||
}
|
||||
|
||||
listBreadcrumb = {
|
||||
key: processId,
|
||||
name,
|
||||
@@ -212,15 +380,15 @@ export abstract class PickupShelfBaseComponent implements OnInit {
|
||||
this.breadcrumbService.removeBreadcrumb(listBreadcrumb.id);
|
||||
}
|
||||
|
||||
this._removeExcessBreadcrumbs(breadcrumbs);
|
||||
this._removeBreadcrumbs(breadcrumbs);
|
||||
}
|
||||
|
||||
abstract getNameForFilterBreadcrumb(data: GetNameForBreadcrumbData): string | Promise<string>;
|
||||
|
||||
abstract getPathForFilter(data: GetPathForBreadcrumbData): NavigationRoute | Promise<NavigationRoute>;
|
||||
abstract getPathForFilter(data: GetPathForBreadcrumbData): NavigationRoute | null | Promise<NavigationRoute | null>;
|
||||
|
||||
private async _checkAndUpdateFilterBreadcrumb({ processId, queryParams, view }: GetNameAndPathForBreadcrumbData) {
|
||||
const breadcrumbs = await this._getBreadcrumbByTag('filter');
|
||||
const breadcrumbs = await this._getBreadcrumb('filter');
|
||||
let filterBreadcrumb: Breadcrumb = breadcrumbs.pop();
|
||||
|
||||
const shouldHaveBreadcrumb = ['filter'].includes(view);
|
||||
@@ -229,6 +397,10 @@ export abstract class PickupShelfBaseComponent implements OnInit {
|
||||
const name = await this.getNameForFilterBreadcrumb({ processId, queryParams });
|
||||
const path = await this.getPathForFilter({ processId, queryParams });
|
||||
|
||||
if (!path) {
|
||||
return;
|
||||
}
|
||||
|
||||
filterBreadcrumb = {
|
||||
key: processId,
|
||||
name,
|
||||
@@ -242,25 +414,29 @@ export abstract class PickupShelfBaseComponent implements OnInit {
|
||||
this.breadcrumbService.removeBreadcrumb(filterBreadcrumb.id);
|
||||
}
|
||||
|
||||
this._removeExcessBreadcrumbs(breadcrumbs);
|
||||
this._removeBreadcrumbs(breadcrumbs);
|
||||
}
|
||||
|
||||
abstract getNameForDetailBreadcrumb(data: GetNameForBreadcrumbData): string | Promise<string>;
|
||||
|
||||
abstract getPathForDetail(item: DBHOrderItemListItemDTO): Observable<NavigationRoute>;
|
||||
|
||||
abstract getPathForDetailForDetailBreadcrumb(data: GetPathForBreadcrumbData): NavigationRoute | Promise<NavigationRoute>;
|
||||
abstract getPathForDetailForDetailBreadcrumb(data: GetPathForBreadcrumbData): NavigationRoute | null | Promise<NavigationRoute | null>;
|
||||
|
||||
private async _checkAndUpdateDetailBreadcrumb({ processId, queryParams, view }: GetNameAndPathForBreadcrumbData) {
|
||||
const breadcrumbs = await this._getBreadcrumbByTag('details');
|
||||
const breadcrumbs = await this._getBreadcrumb('details');
|
||||
let detailBreadcrumb: Breadcrumb = breadcrumbs.pop();
|
||||
|
||||
const shouldHaveBreadcrumb = ['details', 'edit', 'history'].includes(view);
|
||||
const shouldHaveBreadcrumb = ['details', 'edit', 'history', 'reservation', 'cleanup', 'wareneingangsliste', 'remission'].includes(view);
|
||||
|
||||
if (shouldHaveBreadcrumb) {
|
||||
const name = await this.getNameForDetailBreadcrumb({ processId, queryParams });
|
||||
const path = await this.getPathForDetailForDetailBreadcrumb({ processId, queryParams });
|
||||
|
||||
if (!path) {
|
||||
return;
|
||||
}
|
||||
|
||||
detailBreadcrumb = {
|
||||
key: processId,
|
||||
name,
|
||||
@@ -274,17 +450,17 @@ export abstract class PickupShelfBaseComponent implements OnInit {
|
||||
this.breadcrumbService.removeBreadcrumb(detailBreadcrumb.id);
|
||||
}
|
||||
|
||||
this._removeExcessBreadcrumbs(breadcrumbs);
|
||||
this._removeBreadcrumbs(breadcrumbs);
|
||||
}
|
||||
|
||||
abstract getPathForEdit(item: DBHOrderItemListItemDTO): Observable<NavigationRoute>;
|
||||
|
||||
abstract getPathForEditBreadcrumb(data: GetPathForBreadcrumbData): NavigationRoute | Promise<NavigationRoute>;
|
||||
abstract getPathForEditBreadcrumb(data: GetPathForBreadcrumbData): NavigationRoute | null | Promise<NavigationRoute | null>;
|
||||
|
||||
abstract getNameForEditBreadcrumb(data: GetNameForBreadcrumbData): string | Promise<string>;
|
||||
|
||||
private async _checkAndUpdateEditBreadcrumb({ processId, queryParams, view }: GetNameAndPathForBreadcrumbData) {
|
||||
const breadcrumbs = await this._getBreadcrumbByTag('edit');
|
||||
const breadcrumbs = await this._getBreadcrumb('edit');
|
||||
let editBreadcrumb: Breadcrumb = breadcrumbs.pop();
|
||||
|
||||
const shouldHaveBreadcrumb = ['edit'].includes(view);
|
||||
@@ -293,6 +469,10 @@ export abstract class PickupShelfBaseComponent implements OnInit {
|
||||
const name = await this.getNameForEditBreadcrumb({ processId, queryParams });
|
||||
const path = await this.getPathForEditBreadcrumb({ processId, queryParams });
|
||||
|
||||
if (!path) {
|
||||
return;
|
||||
}
|
||||
|
||||
editBreadcrumb = {
|
||||
key: processId,
|
||||
name,
|
||||
@@ -306,17 +486,17 @@ export abstract class PickupShelfBaseComponent implements OnInit {
|
||||
this.breadcrumbService.removeBreadcrumb(editBreadcrumb.id);
|
||||
}
|
||||
|
||||
this._removeExcessBreadcrumbs(breadcrumbs);
|
||||
this._removeBreadcrumbs(breadcrumbs);
|
||||
}
|
||||
|
||||
abstract getPathForHistory(item: DBHOrderItemListItemDTO): Observable<NavigationRoute>;
|
||||
|
||||
abstract getPathForHistoryBreadcrumb(data: GetPathForBreadcrumbData): NavigationRoute | Promise<NavigationRoute>;
|
||||
abstract getPathForHistoryBreadcrumb(data: GetPathForBreadcrumbData): NavigationRoute | null | Promise<NavigationRoute | null>;
|
||||
|
||||
abstract getNameForHistoryBreadcrumb(data: GetNameForBreadcrumbData): string | Promise<string>;
|
||||
|
||||
private async _checkAndUpdateHistoryBreadcrumb({ processId, queryParams, view }: GetNameAndPathForBreadcrumbData) {
|
||||
const breadcrumbs = await this._getBreadcrumbByTag('history');
|
||||
const breadcrumbs = await this._getBreadcrumb('history');
|
||||
let historyBreadcrumb: Breadcrumb = breadcrumbs.pop();
|
||||
|
||||
const shouldHaveBreadcrumb = ['history'].includes(view);
|
||||
@@ -325,6 +505,10 @@ export abstract class PickupShelfBaseComponent implements OnInit {
|
||||
const name = await this.getNameForHistoryBreadcrumb({ processId, queryParams });
|
||||
const path = await this.getPathForHistoryBreadcrumb({ processId, queryParams });
|
||||
|
||||
if (!path) {
|
||||
return;
|
||||
}
|
||||
|
||||
historyBreadcrumb = {
|
||||
key: processId,
|
||||
name,
|
||||
@@ -338,7 +522,7 @@ export abstract class PickupShelfBaseComponent implements OnInit {
|
||||
this.breadcrumbService.removeBreadcrumb(historyBreadcrumb.id);
|
||||
}
|
||||
|
||||
this._removeExcessBreadcrumbs(breadcrumbs);
|
||||
this._removeBreadcrumbs(breadcrumbs);
|
||||
}
|
||||
|
||||
execAction(action: KeyValueDTOOfStringAndString): Promise<OrderItemsContext> {
|
||||
|
||||
@@ -5,6 +5,8 @@ import { PickupShelfDetailsStore, PickupShelfStore } from './store';
|
||||
import { ActionHandlerService } from './services/action-handler.service';
|
||||
import { DBHOrderItemListItemDTO, KeyValueDTOOfStringAndString } from '@swagger/oms';
|
||||
import { OrderItemsContext } from '@domain/oms';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { coerceBooleanProperty } from '@angular/cdk/coercion';
|
||||
|
||||
export abstract class PickupShelfDetailsBaseComponent {
|
||||
protected destroyRef = inject(DestroyRef);
|
||||
@@ -18,14 +20,35 @@ export abstract class PickupShelfDetailsBaseComponent {
|
||||
store = inject(PickupShelfDetailsStore);
|
||||
listStore = inject(PickupShelfStore);
|
||||
|
||||
get side() {
|
||||
if (this.activatedRoute.snapshot.queryParams.side !== undefined) {
|
||||
return coerceBooleanProperty(this.activatedRoute.snapshot.queryParams.side);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
side$ = this.activatedRoute.queryParams.pipe(
|
||||
map((params) => {
|
||||
if (params.side !== undefined) {
|
||||
return coerceBooleanProperty(params.side);
|
||||
}
|
||||
return true;
|
||||
})
|
||||
);
|
||||
|
||||
constructor() {
|
||||
this.activatedRoute.params.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((params) => {
|
||||
// // Fix #4508 - Always Reset Cover Items before fetching new ones inside pickup-shelf-in-details.component
|
||||
if (!!this.store.coverOrderItems?.length) {
|
||||
this.store.resetCoverItems();
|
||||
}
|
||||
this.store.fetchOrder({ orderId: Number(params.orderId) });
|
||||
this.store.fetchOrderItems({
|
||||
orderNumber: params.orderNumber ? decodeURIComponent(params.orderNumber) : undefined,
|
||||
compartmentCode: params.compartmentCode ? decodeURIComponent(params.compartmentCode) : undefined,
|
||||
compartmentInfo: params.compartmentInfo ? decodeURIComponent(params.compartmentInfo) : undefined,
|
||||
orderItemProcessingStatus: (params.orderItemProcessingStatus ? +params.orderItemProcessingStatus : undefined) as any,
|
||||
orderItemSubsetId: params.orderItemSubsetId ? +params.orderItemSubsetId : undefined,
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -57,12 +80,26 @@ export abstract class PickupShelfDetailsBaseComponent {
|
||||
itemQuantity: this.store.selectedOrderItemQuantity,
|
||||
});
|
||||
|
||||
const ctxItem = ctx?.items[0];
|
||||
|
||||
// Ticket #4466 - Nach der nachbestellung wurde der Artikel in den Details nicht mehr angezeigt - änderung #4459 rückgängig gemacht
|
||||
// Ticket #4459 - Usecase Abholfach Nachbestellen - Wenn das selektierte Item nicht verändert wurde z.B. beim schließen des nachbestellen Modals
|
||||
// soll hier returned werden, da der unveränderte Stand angezeigt werden soll
|
||||
// if (
|
||||
// !ctxItem ||
|
||||
// (action.command.includes('REORDER') &&
|
||||
// ctxItem.orderItemSubsetId === this.store.selectedOrderItems.find((_) => true).orderItemSubsetId)
|
||||
// ) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
this.store.setFetchPartial(false);
|
||||
this.store.resetSelectedOrderItems();
|
||||
this.listStore.resetSelectedListItems();
|
||||
this.store.resetSelectedOrderItemQuantity();
|
||||
this.store.setSelectedCompartmentInfo(undefined);
|
||||
|
||||
const ctxItem = ctx?.items.find((_) => true);
|
||||
if (!ctxItem) return;
|
||||
|
||||
const updatedDetailsItems = await new Promise<DBHOrderItemListItemDTO[]>((resolve, reject) => {
|
||||
this.store.fetchOrderItems({
|
||||
@@ -70,6 +107,7 @@ export abstract class PickupShelfDetailsBaseComponent {
|
||||
compartmentCode: ctxItem.processingStatus === 128 ? ctxItem.compartmentCode : undefined,
|
||||
compartmentInfo: ctxItem.processingStatus === 128 ? ctxItem.compartmentInfo : undefined,
|
||||
orderItemProcessingStatus: ctxItem.processingStatus,
|
||||
orderItemSubsetId: ctxItem.orderItemSubsetId,
|
||||
done: { resolve, reject },
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
:host {
|
||||
@apply box-border grid overflow-y-scroll h-split-screen-tablet desktop-small:h-split-screen-desktop;
|
||||
@apply box-border overflow-y-scroll h-split-screen-tablet desktop-small:h-split-screen-desktop;
|
||||
}
|
||||
|
||||
.page-pickup-shelf-in-details__container {
|
||||
grid-template-rows: auto 1fr;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,52 +1,67 @@
|
||||
<div class="mb-4">
|
||||
<page-pickup-shelf-details-header
|
||||
(handleAction)="handleAction({ action: $event })"
|
||||
(updateDate)="updateDate($event)"
|
||||
(editClick)="navigateToEditPage($event)"
|
||||
></page-pickup-shelf-details-header>
|
||||
<page-pickup-shelf-details-item
|
||||
*ngIf="selectedItem$ | async; let item"
|
||||
class="mb-px-2"
|
||||
[orderItem]="item"
|
||||
[selected]="true"
|
||||
(historyClick)="navigateToHistoryPage($event)"
|
||||
[order]="order$ | async"
|
||||
(specialCommentChanged)="updateSpecialComment(item, $event)"
|
||||
(sharedOnInit)="fetchNotifications(item)"
|
||||
></page-pickup-shelf-details-item>
|
||||
<page-pickup-shelf-details-tags class="mb-px-2" *ngIf="showTagsComponent$ | async"></page-pickup-shelf-details-tags>
|
||||
<page-pickup-shelf-details-covers
|
||||
*ngIf="(coverOrderItems$ | async)?.length > 0"
|
||||
[coverItems]="coverOrderItems$ | async"
|
||||
[selectedOrderItem]="selectedItem$ | async"
|
||||
(coverClick)="coverClick($event)"
|
||||
></page-pickup-shelf-details-covers>
|
||||
</div>
|
||||
<div class="grid overflow-y-scroll h-full" [class.page-pickup-shelf-in-details__container]="!(viewFetching$ | async)">
|
||||
<div *ngIf="noOrderItemsFound$ | async" class="text-center text-2xl font-bold bg-white rounded px-4 py-8">
|
||||
<h4>Posten wurde nicht gefunden</h4>
|
||||
</div>
|
||||
|
||||
<div class="page-pickup-shelf-in-details__action-wrapper">
|
||||
<ng-container *ngIf="latestCompartmentInfos$ | async; let latestCompartmentInfos">
|
||||
<button
|
||||
[disabled]="addToPreviousCompartmentActionDisabled$ | async"
|
||||
*ngIf="addToPreviousCompartmentAction$ | async; let action"
|
||||
class="cta-action shadow-action"
|
||||
[class.cta-action-primary]="action.selected"
|
||||
[class.cta-action-secondary]="!action.selected"
|
||||
(click)="handleAction({action, latestCompartmentCode: latestCompartmentInfos?.latestCompartmentCode, latestCompartmentInfo: latestCompartmentInfos?.latestCompartmentInfo })"
|
||||
>
|
||||
<ui-spinner [show]="(changeActionLoader$ | async) === action.command"
|
||||
>{{ latestDisplayedCompartmentInfos$ | async | addToPreviousCompartmentCodeLabelPipe }} zubuchen</ui-spinner
|
||||
>
|
||||
</button>
|
||||
<ng-container *ngIf="viewFetching$ | async; else showView">
|
||||
<div class="w-full flex flex-col justify-center items-center">
|
||||
<ui-spinner [show]="true"></ui-spinner>
|
||||
<div class="mt-6">Daten werden geladen ...</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<button
|
||||
[disabled]="actionsDisabled$ | async"
|
||||
class="cta-action shadow-action"
|
||||
[class.cta-action-primary]="action.selected"
|
||||
[class.cta-action-secondary]="!action.selected"
|
||||
*ngFor="let action of mainActions$ | async"
|
||||
(click)="handleAction({action})"
|
||||
>
|
||||
<ui-spinner [show]="(changeActionLoader$ | async) === action.command">{{ action.label }}</ui-spinner>
|
||||
</button>
|
||||
<ng-template #showView>
|
||||
<div class="mb-4">
|
||||
<page-pickup-shelf-details-header
|
||||
(handleAction)="handleAction({ action: $event })"
|
||||
(updateDate)="updateDate($event)"
|
||||
(editClick)="navigateToEditPage($event)"
|
||||
></page-pickup-shelf-details-header>
|
||||
<page-pickup-shelf-details-item
|
||||
*ngIf="selectedItem$ | async; let item"
|
||||
class="mb-px-2"
|
||||
[orderItem]="item"
|
||||
[selected]="true"
|
||||
(historyClick)="navigateToHistoryPage($event)"
|
||||
[order]="order$ | async"
|
||||
(specialCommentChanged)="updateSpecialComment(item, $event)"
|
||||
(sharedOnInit)="fetchNotifications(item)"
|
||||
></page-pickup-shelf-details-item>
|
||||
<page-pickup-shelf-details-tags class="mb-px-2" *ngIf="showTagsComponent$ | async"></page-pickup-shelf-details-tags>
|
||||
<page-pickup-shelf-details-covers
|
||||
*ngIf="(coverOrderItems$ | async)?.length > 0"
|
||||
[coverItems]="coverOrderItems$ | async"
|
||||
[selectedOrderItem]="selectedItem$ | async"
|
||||
(coverClick)="coverClick($event)"
|
||||
></page-pickup-shelf-details-covers>
|
||||
</div>
|
||||
|
||||
<div class="page-pickup-shelf-in-details__action-wrapper">
|
||||
<button
|
||||
[disabled]="actionsDisabled$ | async"
|
||||
class="cta-action shadow-action"
|
||||
[class.cta-action-primary]="action.selected"
|
||||
[class.cta-action-secondary]="!action.selected"
|
||||
*ngFor="let action of mainActions$ | async"
|
||||
(click)="handleAction({action})"
|
||||
>
|
||||
<ui-spinner [show]="(changeActionLoader$ | async) === action.command">{{ action.label }}</ui-spinner>
|
||||
</button>
|
||||
|
||||
<ng-container *ngIf="latestCompartmentInfos$ | async; let latestCompartmentInfos">
|
||||
<button
|
||||
[disabled]="addToPreviousCompartmentActionDisabled$ | async"
|
||||
*ngIf="addToPreviousCompartmentAction$ | async; let action"
|
||||
class="cta-action shadow-action"
|
||||
[class.cta-action-primary]="action.selected"
|
||||
[class.cta-action-secondary]="!action.selected"
|
||||
(click)="handleAction({action, latestCompartmentCode: latestCompartmentInfos?.latestCompartmentCode, latestCompartmentInfo: latestCompartmentInfos?.latestCompartmentInfo })"
|
||||
>
|
||||
<ui-spinner [show]="(changeActionLoader$ | async) === action.command"
|
||||
>{{ latestDisplayedCompartmentInfos$ | async | addToPreviousCompartmentCodeLabelPipe }} zubuchen</ui-spinner
|
||||
>
|
||||
</button>
|
||||
</ng-container>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
|
||||
@@ -9,12 +9,13 @@ import { PickupShelfAddToPreviousCompartmentCodeLabelPipe } from '../../shared/p
|
||||
import { UiSpinnerModule } from '@ui/spinner';
|
||||
import { OnInitDirective } from '@shared/directives/element-lifecycle';
|
||||
import { PickupShelfInNavigationService } from '@shared/services';
|
||||
import { BehaviorSubject, combineLatest } from 'rxjs';
|
||||
import { BehaviorSubject, asapScheduler, combineLatest } from 'rxjs';
|
||||
import { map, shareReplay } from 'rxjs/operators';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
import { DBHOrderItemListItemDTO, KeyValueDTOOfStringAndString } from '@swagger/oms';
|
||||
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { RunCheckTrigger } from '../../trigger';
|
||||
|
||||
@Component({
|
||||
selector: 'page-pickup-shelf-in-details',
|
||||
@@ -37,6 +38,8 @@ import { ActivatedRoute } from '@angular/router';
|
||||
],
|
||||
})
|
||||
export class PickupShelfInDetailsComponent extends PickupShelfDetailsBaseComponent implements OnInit, AfterViewInit {
|
||||
runCheckTrigger = inject(RunCheckTrigger);
|
||||
|
||||
@ViewChild(PickUpShelfDetailsTagsComponent, { static: false })
|
||||
pickUpShelfDetailsTags: PickUpShelfDetailsTagsComponent;
|
||||
|
||||
@@ -46,7 +49,15 @@ export class PickupShelfInDetailsComponent extends PickupShelfDetailsBaseCompone
|
||||
|
||||
orderItems$ = this.store.orderItems$.pipe(shareReplay());
|
||||
|
||||
noOrderItemsFound$ = this.store.noOrderItemsFound$;
|
||||
|
||||
fetching$ = this.store.fetchingOrder$;
|
||||
fetchingItems$ = this.store.fetchingOrderItems$;
|
||||
fetchingCoverItems$ = this.store.fetchingCoverOrderItems$;
|
||||
|
||||
viewFetching$ = combineLatest([this.fetching$, this.fetchingItems$, this.fetchingCoverItems$]).pipe(
|
||||
map(([fetching, fetchingItems, fetchingCoverItems]) => fetching || fetchingItems || fetchingCoverItems)
|
||||
);
|
||||
|
||||
selectedCompartmentInfo = this.store.selectedCompartmentInfo;
|
||||
|
||||
@@ -126,29 +137,36 @@ export class PickupShelfInDetailsComponent extends PickupShelfDetailsBaseCompone
|
||||
try {
|
||||
this.changeActionLoader$.next(action.command);
|
||||
this.store.setDisableHeaderStatusDropdown(true);
|
||||
|
||||
const context = await this.execAction({ action, latestCompartmentCode, latestCompartmentInfo });
|
||||
|
||||
if (!!context) {
|
||||
if (action.command.includes('ARRIVED') || action.command.includes('PRINT_PRICEDIFFQRCODELABEL')) {
|
||||
await this.router.navigate(this._pickupShelfInNavigationService.defaultRoute().path);
|
||||
asapScheduler.schedule(async () => {
|
||||
await this.navigateBasedOnCurrentView();
|
||||
}, 100);
|
||||
} else {
|
||||
const item = context?.items.find((_) => true);
|
||||
await this.router.navigate(
|
||||
this._pickupShelfInNavigationService.detailRoute({
|
||||
item: {
|
||||
compartmentCode: item.compartmentCode,
|
||||
orderId: item.orderId,
|
||||
orderNumber: item.orderNumber,
|
||||
processingStatus: item.processingStatus,
|
||||
orderItemSubsetId: item.orderItemSubsetId,
|
||||
compartmentInfo: item.compartmentInfo,
|
||||
},
|
||||
}).path,
|
||||
{ queryParamsHandling: 'preserve' }
|
||||
);
|
||||
this.listStore.patchOrderItem({
|
||||
orderItemSubsetId: item.orderItemSubsetId,
|
||||
changes: { processingStatus: item.processingStatus },
|
||||
});
|
||||
if (!!item) {
|
||||
await this.router.navigate(
|
||||
this._pickupShelfInNavigationService.detailRoute({
|
||||
item: {
|
||||
compartmentCode: item.compartmentCode,
|
||||
orderId: item.orderId,
|
||||
orderNumber: item.orderNumber,
|
||||
processingStatus: item.processingStatus,
|
||||
orderItemSubsetId: item.orderItemSubsetId,
|
||||
compartmentInfo: item.compartmentInfo,
|
||||
},
|
||||
side: false,
|
||||
}).path,
|
||||
{ queryParamsHandling: 'preserve' }
|
||||
);
|
||||
this.listStore.patchOrderItem({
|
||||
orderItemSubsetId: item.orderItemSubsetId,
|
||||
changes: { processingStatus: item.processingStatus },
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -159,12 +177,32 @@ export class PickupShelfInDetailsComponent extends PickupShelfDetailsBaseCompone
|
||||
});
|
||||
}
|
||||
|
||||
asapScheduler.schedule(() => {
|
||||
this.runCheckTrigger.next();
|
||||
}, 100);
|
||||
|
||||
setTimeout(() => {
|
||||
this.store.setDisableHeaderStatusDropdown(false);
|
||||
this.changeActionLoader$.next(undefined);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
async navigateBasedOnCurrentView() {
|
||||
const currentView = await this._activatedRoute?.snapshot?.queryParams?.view;
|
||||
switch (currentView) {
|
||||
case 'reservation':
|
||||
return this.router.navigate(['/filiale', 'goods', 'in', 'reservation'], { queryParamsHandling: 'preserve' });
|
||||
case 'cleanup':
|
||||
return this.router.navigate(['/filiale', 'goods', 'in', 'cleanup'], { queryParamsHandling: 'preserve' });
|
||||
case 'remission':
|
||||
return this.router.navigate(['/filiale', 'goods', 'in', 'preview'], { queryParamsHandling: 'preserve' });
|
||||
case 'wareneingangsliste':
|
||||
return this.router.navigate(['/filiale', 'goods', 'in', 'list'], { queryParamsHandling: 'preserve' });
|
||||
default:
|
||||
return this.router.navigate(this._pickupShelfInNavigationService.defaultRoute().path);
|
||||
}
|
||||
}
|
||||
|
||||
updateDate({ date, type }: { date: Date; type?: 'delivery' | 'pickup' | 'preferred' }) {
|
||||
switch (type) {
|
||||
case 'delivery':
|
||||
@@ -194,28 +232,34 @@ export class PickupShelfInDetailsComponent extends PickupShelfDetailsBaseCompone
|
||||
|
||||
async navigateToEditPage(orderItem: DBHOrderItemListItemDTO) {
|
||||
await this.router.navigate(
|
||||
this._pickupShelfInNavigationService.editRoute({
|
||||
compartmentCode: orderItem.compartmentCode,
|
||||
orderId: orderItem.orderId,
|
||||
orderNumber: orderItem.orderNumber,
|
||||
processingStatus: orderItem.processingStatus,
|
||||
orderItemSubsetId: orderItem.orderItemSubsetId,
|
||||
compartmentInfo: orderItem.compartmentInfo,
|
||||
}).path,
|
||||
this._pickupShelfInNavigationService.editRoute(
|
||||
{
|
||||
compartmentCode: orderItem.compartmentCode,
|
||||
orderId: orderItem.orderId,
|
||||
orderNumber: orderItem.orderNumber,
|
||||
processingStatus: orderItem.processingStatus,
|
||||
orderItemSubsetId: orderItem.orderItemSubsetId,
|
||||
compartmentInfo: orderItem.compartmentInfo,
|
||||
},
|
||||
{ side: this.side }
|
||||
).path,
|
||||
{ queryParams: { buyerNumber: orderItem?.buyerNumber }, queryParamsHandling: 'merge' }
|
||||
);
|
||||
}
|
||||
|
||||
async navigateToHistoryPage(orderItem: DBHOrderItemListItemDTO) {
|
||||
await this.router.navigate(
|
||||
this._pickupShelfInNavigationService.historyRoute({
|
||||
compartmentCode: orderItem.compartmentCode,
|
||||
orderId: orderItem.orderId,
|
||||
orderNumber: orderItem.orderNumber,
|
||||
processingStatus: orderItem.processingStatus,
|
||||
orderItemSubsetId: orderItem.orderItemSubsetId,
|
||||
compartmentInfo: orderItem.compartmentInfo,
|
||||
}).path,
|
||||
this._pickupShelfInNavigationService.historyRoute(
|
||||
{
|
||||
compartmentCode: orderItem.compartmentCode,
|
||||
orderId: orderItem.orderId,
|
||||
orderNumber: orderItem.orderNumber,
|
||||
processingStatus: orderItem.processingStatus,
|
||||
orderItemSubsetId: orderItem.orderItemSubsetId,
|
||||
compartmentInfo: orderItem.compartmentInfo,
|
||||
},
|
||||
{ side: this.side }
|
||||
).path,
|
||||
{ queryParams: { orderItemSubsetId: orderItem.orderItemSubsetId }, queryParamsHandling: 'merge' }
|
||||
);
|
||||
}
|
||||
@@ -227,6 +271,7 @@ export class PickupShelfInDetailsComponent extends PickupShelfDetailsBaseCompone
|
||||
async coverClick(orderItems: DBHOrderItemListItemDTO[]) {
|
||||
if (orderItems.length === 1) {
|
||||
const item = orderItems.find((_) => true);
|
||||
|
||||
await this.router.navigate(
|
||||
this._pickupShelfInNavigationService.detailRoute({
|
||||
item: {
|
||||
@@ -237,6 +282,7 @@ export class PickupShelfInDetailsComponent extends PickupShelfDetailsBaseCompone
|
||||
processingStatus: item.processingStatus,
|
||||
orderItemSubsetId: item.orderItemSubsetId,
|
||||
},
|
||||
side: this.side,
|
||||
}).path,
|
||||
{ queryParamsHandling: 'preserve' }
|
||||
);
|
||||
|
||||
@@ -40,6 +40,7 @@ export class PickupShelfInEditComponent extends PickupShelfDetailsBaseComponent
|
||||
compartmentInfo,
|
||||
orderItemSubsetId: this.store?.selectPreviousSelectedOrderItemSubsetId,
|
||||
},
|
||||
side: this.side,
|
||||
}).path,
|
||||
{ queryParamsHandling: 'preserve' }
|
||||
);
|
||||
|
||||
@@ -34,14 +34,3 @@ page-pickup-shelf-list-item {
|
||||
.cta-action-secondary {
|
||||
@apply bg-white text-brand;
|
||||
}
|
||||
|
||||
::ng-deep page-pickup-shelf-in-list ui-scroll-container {
|
||||
height: 100% !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
::ng-deep page-pickup-shelf-in-list ui-scroll-container .scroll-container {
|
||||
height: 100% !important;
|
||||
max-height: 100% !important;
|
||||
gap: 0.625rem !important;
|
||||
}
|
||||
|
||||
@@ -40,57 +40,43 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="h-full relative overflow-hidden overflow-y-scroll">
|
||||
<ui-scroll-container
|
||||
*ngIf="!(listEmpty$ | async); else emptyMessage"
|
||||
class="page-pickup-shelf-in-list__scroll-container m-0 p-0"
|
||||
(reachEnd)="loadMore()"
|
||||
[deltaEnd]="150"
|
||||
[showScrollbar]="false"
|
||||
[containerHeight]="25"
|
||||
[showScrollArrow]="false"
|
||||
[showSpacer]="(primaryOutletActive$ | async) || (isTablet$ | async) || (isDesktopSmall$ | async)"
|
||||
>
|
||||
<div
|
||||
class="page-pickup-shelf-in-list__items-list w-full"
|
||||
*ngFor="let bueryNumberGroup of list$ | async | groupBy: byBuyerNumberFn; trackBy: trackByGroupFn"
|
||||
>
|
||||
<ng-container *ngIf="bueryNumberGroup.items[0]; let firstItem">
|
||||
<div
|
||||
class="page-pickup-shelf-in-list__item-header-group w-full grid grid-flow-col gap-x-4 items-center justify-between bg-white text-xl rounded-t p-4 font-bold mb-px-2"
|
||||
>
|
||||
<h3>
|
||||
{{ firstItem?.organisation }}
|
||||
<ng-container *ngIf="!!firstItem?.organisation && (!!firstItem?.firstName || !!firstItem?.lastName)"> - </ng-container>
|
||||
{{ firstItem?.lastName }}
|
||||
{{ firstItem?.firstName }}
|
||||
</h3>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngFor="let orderNumberGroup of bueryNumberGroup.items | groupBy: byOrderNumberFn; trackBy: trackByGroupFn">
|
||||
<ng-container *ngFor="let processingStatusGroup of orderNumberGroup.items | groupBy: byProcessingStatusFn; trackBy: trackByGroupFn">
|
||||
<ng-container
|
||||
*ngFor="let compartmentCodeGroup of processingStatusGroup.items | groupBy: byCompartmentCodeFn; trackBy: trackByGroupFn"
|
||||
>
|
||||
<page-pickup-shelf-list-item
|
||||
*ngFor="let item of compartmentCodeGroup.items; let firstItem = first; trackBy: trackByFn"
|
||||
class="page-pickup-shelf-in-list__result-item mb-[0.125rem]"
|
||||
[item]="item"
|
||||
[primaryOutletActive]="primaryOutletActive$ | async"
|
||||
[itemDetailsLink]="getItemDetailsLink(item)"
|
||||
></page-pickup-shelf-list-item>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</div>
|
||||
<page-pickup-shelf-list-item-loader *ngIf="fetching$ | async"></page-pickup-shelf-list-item-loader>
|
||||
</ui-scroll-container>
|
||||
</div>
|
||||
|
||||
<ng-template #emptyMessage>
|
||||
<div class="empty-message">
|
||||
<div sharedScrollContainer class="overflow-scroll" (scrolledToBottom)="loadMore()">
|
||||
<div class="empty-message" *ngIf="listEmpty$ | async">
|
||||
Es sind im Moment keine Bestellposten vorhanden,<br />
|
||||
die bearbeitet werden können.
|
||||
</div>
|
||||
</ng-template>
|
||||
<div
|
||||
class="page-pickup-shelf-in-list__items-list mb-[0.625rem]"
|
||||
*ngFor="let bueryNumberGroup of list$ | async | groupBy: byBuyerNumberFn; trackBy: trackByGroupFn"
|
||||
>
|
||||
<ng-container *ngIf="bueryNumberGroup.items[0]; let firstItem">
|
||||
<div
|
||||
class="page-pickup-shelf-in-list__item-header-group w-full grid grid-flow-col gap-x-4 items-center justify-between bg-white text-xl rounded-t p-4 font-bold mb-px-2"
|
||||
>
|
||||
<h3>
|
||||
{{ firstItem?.organisation }}
|
||||
<ng-container *ngIf="!!firstItem?.organisation && (!!firstItem?.firstName || !!firstItem?.lastName)"> - </ng-container>
|
||||
{{ firstItem?.lastName }}
|
||||
{{ firstItem?.firstName }}
|
||||
</h3>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngFor="let orderNumberGroup of bueryNumberGroup.items | groupBy: byOrderNumberFn; trackBy: trackByGroupFn">
|
||||
<ng-container *ngFor="let processingStatusGroup of orderNumberGroup.items | groupBy: byProcessingStatusFn; trackBy: trackByGroupFn">
|
||||
<ng-container
|
||||
*ngFor="let compartmentCodeGroup of processingStatusGroup.items | groupBy: byCompartmentCodeFn; trackBy: trackByGroupFn"
|
||||
>
|
||||
<page-pickup-shelf-list-item
|
||||
*ngFor="let item of compartmentCodeGroup.items; let firstItem = first; trackBy: trackByFn"
|
||||
class="page-pickup-shelf-in-list__result-item mb-[0.125rem]"
|
||||
[item]="item"
|
||||
[primaryOutletActive]="primaryOutletActive$ | async"
|
||||
[itemDetailsLink]="getItemDetailsLink(item)"
|
||||
></page-pickup-shelf-list-item>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</div>
|
||||
<page-pickup-shelf-list-item-loader *ngIf="fetching$ | async"></page-pickup-shelf-list-item-loader>
|
||||
</div>
|
||||
|
||||
@@ -15,7 +15,6 @@ import { ActivatedRoute, NavigationStart, Router, RouterLink } from '@angular/ro
|
||||
import { Filter, FilterModule } from '@shared/components/filter';
|
||||
import { IconModule } from '@shared/components/icon';
|
||||
import { PickUpShelfListItemComponent } from '../../shared/pickup-shelf-list-item/pickup-shelf-list-item.component';
|
||||
import { UiScrollContainerComponent, UiScrollContainerModule } from '@ui/scroll-container';
|
||||
import { Group, GroupByPipe } from '@ui/common';
|
||||
import { UiSpinnerModule } from '@ui/spinner';
|
||||
import { PickupShelfInNavigationService } from '@shared/services';
|
||||
@@ -28,6 +27,7 @@ import { EnvironmentService } from '@core/environment';
|
||||
import { CacheService } from '@core/cache';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
import { PickupShelfListItemLoaderComponent } from '../../shared/pickup-shelf-list-item/pickup-shelf-list-item-loader.component';
|
||||
import { ScrollContainerDirective } from '@shared/directives/scroll-container';
|
||||
|
||||
@Component({
|
||||
selector: 'page-pickup-shelf-in-list',
|
||||
@@ -44,7 +44,7 @@ import { PickupShelfListItemLoaderComponent } from '../../shared/pickup-shelf-li
|
||||
IconModule,
|
||||
FilterModule,
|
||||
PickUpShelfListItemComponent,
|
||||
UiScrollContainerModule,
|
||||
ScrollContainerDirective,
|
||||
GroupByPipe,
|
||||
UiSpinnerModule,
|
||||
PickupShelfListItemLoaderComponent,
|
||||
@@ -52,7 +52,7 @@ import { PickupShelfListItemLoaderComponent } from '../../shared/pickup-shelf-li
|
||||
})
|
||||
export class PickUpShelfInListComponent implements OnInit, AfterViewInit {
|
||||
@ViewChildren(PickUpShelfListItemComponent) listItems: QueryList<PickUpShelfListItemComponent>;
|
||||
@ViewChild(UiScrollContainerComponent) scrollContainer: UiScrollContainerComponent;
|
||||
@ViewChild(ScrollContainerDirective) scrollContainer: ScrollContainerDirective;
|
||||
|
||||
private _pickupShelfInNavigationService = inject(PickupShelfInNavigationService);
|
||||
|
||||
@@ -121,21 +121,21 @@ export class PickUpShelfInListComponent implements OnInit, AfterViewInit {
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.store.processId$.pipe(takeUntilDestroyed(this.destroyRef), debounceTime(150)).subscribe((_) => {
|
||||
if (!this.store.list.length) {
|
||||
this.store.fetchList();
|
||||
}
|
||||
combineLatest([this.store.processId$, this._activatedRoute.queryParams])
|
||||
.pipe(takeUntilDestroyed(this.destroyRef))
|
||||
.subscribe(([_, queryParams]) => {
|
||||
if (!this.store.list.length || !isEqual(queryParams, this.store.filter.getQueryParams())) {
|
||||
this.store.fetchList();
|
||||
}
|
||||
|
||||
const scrollPos = this._getScrollPositionFromCache();
|
||||
if (!!scrollPos) {
|
||||
setTimeout(() => {
|
||||
if (this._activatedRoute.outlet === 'primary') {
|
||||
}
|
||||
this.scrollContainer?.scrollTo(scrollPos);
|
||||
this._removeScrollPositionFromCache();
|
||||
}, 150);
|
||||
}
|
||||
});
|
||||
const scrollPos = this._getScrollPositionFromCache();
|
||||
if (!!scrollPos && this._activatedRoute.outlet === 'primary') {
|
||||
setTimeout(() => {
|
||||
this.scrollContainer?.scrollTo(scrollPos);
|
||||
this._removeScrollPositionFromCache();
|
||||
}, 150);
|
||||
}
|
||||
});
|
||||
|
||||
this._router.events.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((event) => {
|
||||
if (event instanceof NavigationStart) {
|
||||
@@ -154,7 +154,7 @@ export class PickUpShelfInListComponent implements OnInit, AfterViewInit {
|
||||
|
||||
private _addScrollPositionToCache(): void {
|
||||
if (this._activatedRoute.outlet === 'primary') {
|
||||
this._cache.set<number>({ processId: this.store.processId, token: this.SCROLL_POSITION_TOKEN }, this.scrollContainer?.scrollPos);
|
||||
this._cache.set<number>({ processId: this.store.processId, token: this.SCROLL_POSITION_TOKEN }, this.scrollContainer?.scrollPosition);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import { ActionHandlerService } from '../services/action-handler.service';
|
||||
import { GetNameForBreadcrumbData, GetPathForBreadcrumbData, PickupShelfBaseComponent } from '../pickup-shelf-base.component';
|
||||
import { DBHOrderItemListItemDTO } from '@swagger/oms';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { take } from 'rxjs/operators';
|
||||
import { map, take } from 'rxjs/operators';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { FilterAutocompleteProvider } from '@shared/components/filter';
|
||||
import { PickUpShelfInAutocompleteProvider } from './providers/pickup-shelf-in-autocomplete.provider';
|
||||
@@ -104,10 +104,11 @@ export class PickupShelfInComponent extends PickupShelfBaseComponent {
|
||||
}
|
||||
|
||||
async getPathForDetailForDetailBreadcrumb(data: GetPathForBreadcrumbData): Promise<NavigationRoute> {
|
||||
const compartmentCode = await this.detailsStore.compartmentCode$.pipe(take(1)).toPromise();
|
||||
const order = await this.detailsStore.order$.pipe(take(1)).toPromise();
|
||||
const processingStatus = await this.detailsStore.processingStatus$.pipe(take(1)).toPromise();
|
||||
const item = await this.detailsStore.selectedOrderItems$.pipe(take(1)).toPromise();
|
||||
const compartmentCode = this.detailsStore.compartmentCode;
|
||||
const order = this.detailsStore.order;
|
||||
const processingStatus = this.detailsStore.processingStatus;
|
||||
const orderItemSubsetId = this.detailsStore.orderItemSubsetId;
|
||||
const compartmentInfo = this.detailsStore.compartmentInfo;
|
||||
|
||||
return this._pickupShelfInNavigationService.detailRoute({
|
||||
item: {
|
||||
@@ -115,9 +116,10 @@ export class PickupShelfInComponent extends PickupShelfBaseComponent {
|
||||
compartmentCode,
|
||||
orderNumber: order?.orderNumber,
|
||||
processingStatus: processingStatus,
|
||||
orderItemSubsetId: item?.find((_) => true)?.orderItemSubsetId,
|
||||
compartmentInfo: item?.find((_) => true)?.compartmentInfo,
|
||||
orderItemSubsetId,
|
||||
compartmentInfo,
|
||||
},
|
||||
side: data?.queryParams?.side === 'false' ? false : true, // Fix Ticket #4493 - Wenn man von einer anderen Liste aus kommt z.B. Reservieren Liste, dann soll side in der Breadcrumb false sein
|
||||
});
|
||||
}
|
||||
|
||||
@@ -150,18 +152,18 @@ export class PickupShelfInComponent extends PickupShelfBaseComponent {
|
||||
}
|
||||
|
||||
async getPathForEditBreadcrumb(data: GetPathForBreadcrumbData): Promise<NavigationRoute> {
|
||||
const compartmentCode = await this.detailsStore.compartmentCode$.pipe(take(1)).toPromise();
|
||||
const order = await this.detailsStore.order$.pipe(take(1)).toPromise();
|
||||
const processingStatus = await this.detailsStore.processingStatus$.pipe(take(1)).toPromise();
|
||||
const item = await this.detailsStore.selectedOrderItems$.pipe(take(1)).toPromise();
|
||||
const compartmentInfo = await this.detailsStore.compartmentInfo$.pipe(take(1)).toPromise();
|
||||
const compartmentCode = this.detailsStore.compartmentCode;
|
||||
const order = this.detailsStore.order;
|
||||
const processingStatus = this.detailsStore.processingStatus;
|
||||
const orderItemSubsetId = this.detailsStore.orderItemSubsetId;
|
||||
const compartmentInfo = this.detailsStore.compartmentInfo;
|
||||
|
||||
return this._pickupShelfInNavigationService.editRoute({
|
||||
orderId: order?.id,
|
||||
compartmentCode,
|
||||
orderNumber: order?.orderNumber,
|
||||
processingStatus: processingStatus,
|
||||
orderItemSubsetId: item?.find((_) => true)?.orderItemSubsetId,
|
||||
orderItemSubsetId: orderItemSubsetId,
|
||||
compartmentInfo,
|
||||
});
|
||||
}
|
||||
@@ -184,18 +186,18 @@ export class PickupShelfInComponent extends PickupShelfBaseComponent {
|
||||
}
|
||||
|
||||
async getPathForHistoryBreadcrumb(data: GetPathForBreadcrumbData): Promise<NavigationRoute> {
|
||||
const compartmentCode = await this.detailsStore.compartmentCode$.pipe(take(1)).toPromise();
|
||||
const order = await this.detailsStore.order$.pipe(take(1)).toPromise();
|
||||
const processingStatus = await this.detailsStore.processingStatus$.pipe(take(1)).toPromise();
|
||||
const item = await this.detailsStore.selectedOrderItems$.pipe(take(1)).toPromise();
|
||||
const compartmentInfo = await this.detailsStore.compartmentInfo$.pipe(take(1)).toPromise();
|
||||
const compartmentCode = this.detailsStore.compartmentCode;
|
||||
const order = this.detailsStore.order;
|
||||
const processingStatus = this.detailsStore.processingStatus;
|
||||
const orderItemSubsetId = this.detailsStore.orderItemSubsetId;
|
||||
const compartmentInfo = this.detailsStore.compartmentInfo;
|
||||
|
||||
return this._pickupShelfInNavigationService.historyRoute({
|
||||
orderId: order?.id,
|
||||
compartmentCode,
|
||||
orderNumber: order?.orderNumber,
|
||||
processingStatus: processingStatus,
|
||||
orderItemSubsetId: item?.find((_) => true)?.orderItemSubsetId,
|
||||
orderItemSubsetId: orderItemSubsetId,
|
||||
compartmentInfo,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
:host {
|
||||
@apply box-border grid overflow-y-scroll h-split-screen-tablet desktop-small:h-split-screen-desktop;
|
||||
@apply box-border overflow-y-scroll h-split-screen-tablet desktop-small:h-split-screen-desktop;
|
||||
}
|
||||
|
||||
.page-pickup-shelf-out-details__container {
|
||||
grid-template-rows: auto 1fr;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,36 +1,55 @@
|
||||
<div class="mb-4">
|
||||
<page-pickup-shelf-details-header
|
||||
[processId]="processId"
|
||||
(handleAction)="handleAction($event)"
|
||||
(updateDate)="updateDate($event)"
|
||||
(editClick)="navigateToEditPage($event)"
|
||||
></page-pickup-shelf-details-header>
|
||||
<page-pickup-shelf-details-item
|
||||
class="mb-px-2"
|
||||
*ngFor="let item of orderItems$ | async; trackBy: trackByFnDBHOrderItemListItemDTO"
|
||||
[orderItem]="item"
|
||||
[selected]="true"
|
||||
(historyClick)="navigateToHistoryPage($event)"
|
||||
[order]="order$ | async"
|
||||
(specialCommentChanged)="updateSpecialComment(item, $event)"
|
||||
(sharedOnInit)="fetchNotifications(item)"
|
||||
></page-pickup-shelf-details-item>
|
||||
<page-pickup-shelf-details-tags
|
||||
*ngIf="showTagsComponent$ | async"
|
||||
[ngModel]="selectedCompartmentInfo$ | async"
|
||||
(ngModelChange)="setSelectedCompartmentInfo($event)"
|
||||
></page-pickup-shelf-details-tags>
|
||||
</div>
|
||||
<div class="grid overflow-y-scroll h-full" [class.page-pickup-shelf-out-details__container]="!(viewFetching$ | async)">
|
||||
<ng-container *ngIf="viewFetching$ | async; else showView">
|
||||
<div class="w-full flex flex-col justify-center items-center">
|
||||
<ui-spinner [show]="true"></ui-spinner>
|
||||
<div class="mt-6">Daten werden geladen ...</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<div class="page-pickup-shelf-out-details__action-wrapper">
|
||||
<button
|
||||
[disabled]="actionsDisabled$ | async"
|
||||
class="cta-action shadow-action"
|
||||
[class.cta-action-primary]="action.selected"
|
||||
[class.cta-action-secondary]="!action.selected"
|
||||
*ngFor="let action of mainActions$ | async"
|
||||
(click)="handleAction(action)"
|
||||
>
|
||||
<ui-spinner [show]="(changeActionLoader$ | async) === action.command">{{ action.label }}</ui-spinner>
|
||||
</button>
|
||||
<ng-template #showView>
|
||||
<div class="mb-4">
|
||||
<page-pickup-shelf-details-header
|
||||
[processId]="processId"
|
||||
(handleAction)="handleAction($event)"
|
||||
(updateDate)="updateDate($event)"
|
||||
(editClick)="navigateToEditPage($event)"
|
||||
></page-pickup-shelf-details-header>
|
||||
|
||||
<ng-container *ngFor="let group of groupedItems$ | async; trackBy: trackByFnGroupDBHOrderItemListItemDTO">
|
||||
<page-pickup-shelf-details-items-group
|
||||
[orderType]="group.type"
|
||||
[groupedItems]="group.items"
|
||||
></page-pickup-shelf-details-items-group>
|
||||
<page-pickup-shelf-details-item
|
||||
class="mb-px-2"
|
||||
*ngFor="let item of group.items; trackBy: trackByFnDBHOrderItemListItemDTO"
|
||||
[orderItem]="item"
|
||||
[selected]="true"
|
||||
(historyClick)="navigateToHistoryPage($event)"
|
||||
[order]="order$ | async"
|
||||
(specialCommentChanged)="updateSpecialComment(item, $event)"
|
||||
(sharedOnInit)="fetchNotifications(item)"
|
||||
></page-pickup-shelf-details-item>
|
||||
</ng-container>
|
||||
|
||||
<page-pickup-shelf-details-tags
|
||||
*ngIf="showTagsComponent$ | async"
|
||||
[ngModel]="selectedCompartmentInfo$ | async"
|
||||
(ngModelChange)="setSelectedCompartmentInfo($event)"
|
||||
></page-pickup-shelf-details-tags>
|
||||
</div>
|
||||
|
||||
<div class="page-pickup-shelf-out-details__action-wrapper">
|
||||
<button
|
||||
[disabled]="actionsDisabled$ | async"
|
||||
class="cta-action shadow-action"
|
||||
[class.cta-action-primary]="action.selected"
|
||||
[class.cta-action-secondary]="!action.selected"
|
||||
*ngFor="let action of mainActions$ | async"
|
||||
(click)="handleAction(action)"
|
||||
>
|
||||
<ui-spinner [show]="(changeActionLoader$ | async) === action.command">{{ action.label }}</ui-spinner>
|
||||
</button>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
|
||||
@@ -3,15 +3,17 @@ import { PickupShelfDetailsBaseComponent } from '../../pickup-shelf-details-base
|
||||
import { AsyncPipe, NgFor, NgIf } from '@angular/common';
|
||||
import { PickUpShelfDetailsHeaderComponent } from '../../shared/pickup-shelf-details-header/pickup-shelf-details-header.component';
|
||||
import { PickUpShelfDetailsItemComponent } from '../../shared/pickup-shelf-details-item/pickup-shelf-details-item.component';
|
||||
import { DBHOrderItemListItemDTO, KeyValueDTOOfStringAndString, OrderItemProcessingStatusValue } from '@swagger/oms';
|
||||
import { DBHOrderItemListItemDTO, KeyValueDTOOfStringAndString } from '@swagger/oms';
|
||||
import { PickUpShelfOutNavigationService } from '@shared/services';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { BehaviorSubject, Observable, asapScheduler, combineLatest } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { PickUpShelfDetailsTagsComponent } from '../../shared/pickup-shelf-details-tags/pickup-shelf-details-tags.component';
|
||||
import { UiSpinnerModule } from '@ui/spinner';
|
||||
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
|
||||
import { OnInitDirective } from '@shared/directives/element-lifecycle';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { RunCheckTrigger } from '../../trigger';
|
||||
import { PickUpShelfDetailsItemsGroupComponent } from '../../shared/pickup-shelf-details-items-group/pickup-shelf-details-items-group.component';
|
||||
|
||||
@Component({
|
||||
selector: 'page-pickup-shelf-out-details',
|
||||
@@ -27,6 +29,7 @@ import { FormsModule } from '@angular/forms';
|
||||
PickUpShelfDetailsHeaderComponent,
|
||||
PickUpShelfDetailsItemComponent,
|
||||
PickUpShelfDetailsTagsComponent,
|
||||
PickUpShelfDetailsItemsGroupComponent,
|
||||
UiSpinnerModule,
|
||||
OnInitDirective,
|
||||
FormsModule,
|
||||
@@ -34,6 +37,8 @@ import { FormsModule } from '@angular/forms';
|
||||
providers: [],
|
||||
})
|
||||
export class PickupShelfOutDetailsComponent extends PickupShelfDetailsBaseComponent {
|
||||
runCheckTrigger = inject(RunCheckTrigger);
|
||||
|
||||
@ViewChild(PickUpShelfDetailsTagsComponent, { static: false })
|
||||
pickUpShelfDetailsTags: PickUpShelfDetailsTagsComponent;
|
||||
|
||||
@@ -41,9 +46,30 @@ export class PickupShelfOutDetailsComponent extends PickupShelfDetailsBaseCompon
|
||||
|
||||
order$ = this.store.order$;
|
||||
|
||||
orderItems$ = this.store.orderItems$;
|
||||
groupedItems$: Observable<Array<{ type: string; items: DBHOrderItemListItemDTO[] }>> = this.store.orderItems$.pipe(
|
||||
map((items) => {
|
||||
const groups: Array<{ type: string; items: DBHOrderItemListItemDTO[] }> = [];
|
||||
|
||||
// New Set to remove duplicates
|
||||
const types = Array.from(new Set(items.map((item) => item?.features?.orderType)));
|
||||
|
||||
for (let type of types) {
|
||||
const filteredItemsByType = items.filter((item) => item?.features?.orderType === type);
|
||||
|
||||
if (!!type && filteredItemsByType.length > 0) {
|
||||
// Add items to matching orderType group
|
||||
groups.push({ type, items: filteredItemsByType });
|
||||
}
|
||||
}
|
||||
|
||||
return groups;
|
||||
})
|
||||
);
|
||||
|
||||
fetching$ = this.store.fetchingOrder$;
|
||||
fetchingItems$ = this.store.fetchingOrderItems$;
|
||||
|
||||
viewFetching$ = combineLatest([this.fetching$, this.fetchingItems$]).pipe(map(([fetching, fetchingItems]) => fetching || fetchingItems));
|
||||
|
||||
selectedCompartmentInfo = this.store.selectedCompartmentInfo;
|
||||
|
||||
@@ -60,6 +86,8 @@ export class PickupShelfOutDetailsComponent extends PickupShelfDetailsBaseCompon
|
||||
|
||||
mainActions$ = this.store.mainActions$;
|
||||
|
||||
trackByFnGroupDBHOrderItemListItemDTO = (index: number, group: { type: string; items: DBHOrderItemListItemDTO[] }) => group.type;
|
||||
|
||||
trackByFnDBHOrderItemListItemDTO = (index: number, item: DBHOrderItemListItemDTO) => item.orderItemSubsetId;
|
||||
|
||||
get processId() {
|
||||
@@ -105,6 +133,10 @@ export class PickupShelfOutDetailsComponent extends PickupShelfDetailsBaseCompon
|
||||
});
|
||||
}
|
||||
|
||||
asapScheduler.schedule(() => {
|
||||
this.runCheckTrigger.next();
|
||||
}, 100);
|
||||
|
||||
setTimeout(() => {
|
||||
this.store.setDisableHeaderStatusDropdown(false);
|
||||
this.changeActionLoader$.next(undefined);
|
||||
|
||||
@@ -34,14 +34,3 @@ page-pickup-shelf-list-item {
|
||||
.cta-action-secondary {
|
||||
@apply bg-white text-brand;
|
||||
}
|
||||
|
||||
::ng-deep page-pcikup-shelf-out-list ui-scroll-container {
|
||||
height: 100% !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
::ng-deep page-pcikup-shelf-out-list ui-scroll-container .scroll-container {
|
||||
height: 100% !important;
|
||||
max-height: 100% !important;
|
||||
gap: 0.625rem !important;
|
||||
}
|
||||
|
||||
@@ -40,58 +40,47 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="h-full relative overflow-hidden overflow-y-scroll">
|
||||
<ui-scroll-container
|
||||
*ngIf="!(listEmpty$ | async); else emptyMessage"
|
||||
class="page-pickup-shelf-out-list__scroll-container m-0 p-0"
|
||||
(reachEnd)="loadMore()"
|
||||
[deltaEnd]="150"
|
||||
[showScrollbar]="false"
|
||||
[containerHeight]="25"
|
||||
[showScrollArrow]="false"
|
||||
[showSpacer]="(primaryOutletActive$ | async) || (isTablet$ | async) || (isDesktopSmall$ | async)"
|
||||
<div sharedScrollContainer class="overflow-scroll" (scrolledToBottom)="loadMore()">
|
||||
<div class="empty-message" *ngIf="listEmpty$ | async">
|
||||
Es sind im Moment keine Bestellposten vorhanden,<br />
|
||||
die bearbeitet werden können.
|
||||
</div>
|
||||
<div
|
||||
class="page-pickup-shelf-out-list__items-list w-full mb-[0.625rem]"
|
||||
*ngFor="let bueryNumberGroup of list$ | async | groupBy: byBuyerNumberFn; trackBy: trackByGroupFn"
|
||||
>
|
||||
<ng-container *ngIf="processId$ | async; let processId">
|
||||
<ng-container *ngIf="bueryNumberGroup.items[0]; let firstItem">
|
||||
<div
|
||||
class="page-pickup-shelf-out-list__items-list w-full"
|
||||
*ngFor="let bueryNumberGroup of list$ | async | groupBy: byBuyerNumberFn; trackBy: trackByGroupFn"
|
||||
class="page-pickup-shelf-out-list__item-header-group w-full grid grid-flow-col gap-x-4 items-center justify-between bg-white text-xl rounded-t p-4 font-bold mb-px-2"
|
||||
>
|
||||
<ng-container *ngIf="bueryNumberGroup.items[0]; let firstItem">
|
||||
<div
|
||||
class="page-pickup-shelf-out-list__item-header-group w-full grid grid-flow-col gap-x-4 items-center justify-between bg-white text-xl rounded-t p-4 font-bold mb-px-2"
|
||||
>
|
||||
<h3>
|
||||
{{ firstItem?.organisation }}
|
||||
<ng-container *ngIf="!!firstItem?.organisation && (!!firstItem?.firstName || !!firstItem?.lastName)"> - </ng-container>
|
||||
{{ firstItem?.lastName }}
|
||||
{{ firstItem?.firstName }}
|
||||
</h3>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngFor="let orderNumberGroup of bueryNumberGroup.items | groupBy: byOrderNumberFn; trackBy: trackByGroupFn">
|
||||
<ng-container
|
||||
*ngFor="let processingStatusGroup of orderNumberGroup.items | groupBy: byProcessingStatusFn; trackBy: trackByGroupFn"
|
||||
>
|
||||
<ng-container
|
||||
*ngFor="let compartmentCodeGroup of processingStatusGroup.items | groupBy: byCompartmentCodeFn; trackBy: trackByGroupFn"
|
||||
>
|
||||
<page-pickup-shelf-list-item
|
||||
*ngFor="let item of compartmentCodeGroup.items; let firstItem = first; trackBy: trackByFn"
|
||||
class="page-pickup-shelf-out-list__result-item mb-[0.125rem]"
|
||||
[item]="item"
|
||||
[primaryOutletActive]="primaryOutletActive$ | async"
|
||||
[itemDetailsLink]="getItemDetailsLink(item)"
|
||||
[selectedItem]="getSelectedItem$(item) | async"
|
||||
[isItemSelectable]="getIsItemSelectable$(item) | async"
|
||||
></page-pickup-shelf-list-item>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
<h3>
|
||||
{{ firstItem?.organisation }}
|
||||
<ng-container *ngIf="!!firstItem?.organisation && (!!firstItem?.firstName || !!firstItem?.lastName)"> - </ng-container>
|
||||
{{ firstItem?.lastName }}
|
||||
{{ firstItem?.firstName }}
|
||||
</h3>
|
||||
</div>
|
||||
</ng-container>
|
||||
<page-pickup-shelf-list-item-loader *ngIf="fetching$ | async"></page-pickup-shelf-list-item-loader>
|
||||
</ui-scroll-container>
|
||||
|
||||
<ng-container *ngFor="let orderNumberGroup of bueryNumberGroup.items | groupBy: byOrderNumberFn; trackBy: trackByGroupFn">
|
||||
<ng-container *ngFor="let processingStatusGroup of orderNumberGroup.items | groupBy: byProcessingStatusFn; trackBy: trackByGroupFn">
|
||||
<ng-container
|
||||
*ngFor="let compartmentCodeGroup of processingStatusGroup.items | groupBy: byCompartmentCodeFn; trackBy: trackByGroupFn"
|
||||
>
|
||||
<page-pickup-shelf-list-item
|
||||
*ngFor="let item of compartmentCodeGroup.items; let firstItem = first; trackBy: trackByFn"
|
||||
class="page-pickup-shelf-out-list__result-item mb-[0.125rem]"
|
||||
[item]="item"
|
||||
[primaryOutletActive]="primaryOutletActive$ | async"
|
||||
[itemDetailsLink]="getItemDetailsLink(item)"
|
||||
[selectedItem]="getSelectedItem$(item) | async"
|
||||
[isItemSelectable]="getIsItemSelectable$(item) | async"
|
||||
></page-pickup-shelf-list-item>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</div>
|
||||
<page-pickup-shelf-list-item-loader *ngIf="fetching$ | async"></page-pickup-shelf-list-item-loader>
|
||||
<div class="actions z-sticky h-0 gap-4" *ngIf="actions$ | async; let actions">
|
||||
<button
|
||||
[disabled]="(loadingFetchedActionButton$ | async) || (fetching$ | async)"
|
||||
@@ -105,10 +94,3 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ng-template #emptyMessage>
|
||||
<div class="empty-message">
|
||||
Es sind im Moment keine Bestellposten vorhanden,<br />
|
||||
die bearbeitet werden können.
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
@@ -21,7 +21,6 @@ import { Filter, FilterModule } from '@shared/components/filter';
|
||||
import { BehaviorSubject, Observable, combineLatest } from 'rxjs';
|
||||
import { isEqual } from 'lodash';
|
||||
import { PickUpShelfListItemComponent } from '../../shared/pickup-shelf-list-item/pickup-shelf-list-item.component';
|
||||
import { UiScrollContainerComponent, UiScrollContainerModule } from '@ui/scroll-container';
|
||||
import { DBHOrderItemListItemDTO, KeyValueDTOOfStringAndString, OrderItemProcessingStatusValue } from '@swagger/oms';
|
||||
import { Group, GroupByPipe } from '@shared/pipes/group-by';
|
||||
import { UiSpinnerModule } from '@ui/spinner';
|
||||
@@ -30,6 +29,7 @@ import { PickupShelfListItemLoaderComponent } from '../../shared/pickup-shelf-li
|
||||
import { ActionHandlerService } from '../../services/action-handler.service';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
import { CacheService } from '@core/cache';
|
||||
import { ScrollContainerDirective } from '@shared/directives/scroll-container';
|
||||
|
||||
@Component({
|
||||
selector: 'page-pcikup-shelf-out-list',
|
||||
@@ -39,6 +39,7 @@ import { CacheService } from '@core/cache';
|
||||
host: { class: 'page-pcikup-shelf-out-list' },
|
||||
standalone: true,
|
||||
imports: [
|
||||
ScrollContainerDirective,
|
||||
AsyncPipe,
|
||||
NgFor,
|
||||
RouterLink,
|
||||
@@ -46,7 +47,6 @@ import { CacheService } from '@core/cache';
|
||||
IconModule,
|
||||
FilterModule,
|
||||
PickUpShelfListItemComponent,
|
||||
UiScrollContainerModule,
|
||||
GroupByPipe,
|
||||
UiSpinnerModule,
|
||||
PickupShelfListItemLoaderComponent,
|
||||
@@ -54,7 +54,7 @@ import { CacheService } from '@core/cache';
|
||||
})
|
||||
export class PickupShelfOutListComponent implements OnInit, AfterViewInit {
|
||||
@ViewChildren(PickUpShelfListItemComponent) listItems: QueryList<PickUpShelfListItemComponent>;
|
||||
@ViewChild(UiScrollContainerComponent) scrollContainer: UiScrollContainerComponent;
|
||||
@ViewChild(ScrollContainerDirective) scrollContainer: ScrollContainerDirective;
|
||||
|
||||
private _pickupShelfOutNavigationService = inject(PickUpShelfOutNavigationService);
|
||||
|
||||
@@ -171,7 +171,7 @@ export class PickupShelfOutListComponent implements OnInit, AfterViewInit {
|
||||
|
||||
private _addScrollPositionToCache(): void {
|
||||
if (this._activatedRoute.outlet === 'primary') {
|
||||
this._cache.set<number>({ processId: this.store.processId, token: this.SCROLL_POSITION_TOKEN }, this.scrollContainer?.scrollPos);
|
||||
this._cache.set<number>({ processId: this.store.processId, token: this.SCROLL_POSITION_TOKEN }, this.scrollContainer?.scrollPosition);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -145,96 +145,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row items-center relative bg-[#F5F7FA] p-4 rounded-t">
|
||||
<div *ngIf="showFeature" class="flex flex-row items-center mr-3">
|
||||
<ng-container [ngSwitch]="order.features.orderType">
|
||||
<ng-container *ngSwitchCase="'Versand'">
|
||||
<div class="flex items-center justify-center bg-[#D8DFE5] w-[2.25rem] h-[2.25rem] rounded rounded-br-none mr-2">
|
||||
<shared-icon [size]="24" icon="isa-truck"></shared-icon>
|
||||
</div>
|
||||
<p class="font-bold text-p1">Versand</p>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="'DIG-Versand'">
|
||||
<div class="flex items-center justify-center bg-[#D8DFE5] w-[2.25rem] h-[2.25rem] rounded rounded-br-none mr-2">
|
||||
<shared-icon [size]="24" icon="isa-truck"></shared-icon>
|
||||
</div>
|
||||
<p class="font-bold text-p1">Versand</p>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="'B2B-Versand'">
|
||||
<div class="flex items-center justify-center bg-[#D8DFE5] w-[2.25rem] h-[2.25rem] rounded rounded-br-none mr-2">
|
||||
<shared-icon [size]="24" icon="isa-b2b-truck"></shared-icon>
|
||||
</div>
|
||||
<p class="font-bold text-p1">B2B-Versand</p>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="'Abholung'">
|
||||
<div class="flex items-center justify-center bg-[#D8DFE5] w-[2.25rem] h-[2.25rem] rounded rounded-br-none mr-2">
|
||||
<shared-icon [size]="24" icon="isa-box-out"></shared-icon>
|
||||
</div>
|
||||
<p class="font-bold text-p1 mr-3">Abholung</p>
|
||||
{{ orderItem.targetBranch }}
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="'Rücklage'">
|
||||
<div class="flex items-center justify-center bg-[#D8DFE5] w-[2.25rem] h-[2.25rem] rounded rounded-br-none mr-2">
|
||||
<shared-icon [size]="24" icon="isa-shopping-bag"></shared-icon>
|
||||
</div>
|
||||
<p class="font-bold text-p1">Rücklage</p>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="'Download'">
|
||||
<div class="flex items-center justify-center bg-[#D8DFE5] w-[2.25rem] h-[2.25rem] rounded rounded-br-none mr-2">
|
||||
<shared-icon [size]="24" icon="isa-download"></shared-icon>
|
||||
</div>
|
||||
<p class="font-bold text-p1">Download</p>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<div class="page-pickup-shelf-details-header__additional-addresses" *ngIf="showAddresses">
|
||||
<button (click)="openAddresses = !openAddresses" class="text-[#0556B4]">
|
||||
Lieferadresse / Rechnungsadresse {{ openAddresses ? 'ausblenden' : 'anzeigen' }}
|
||||
</button>
|
||||
|
||||
<div class="page-pickup-shelf-details-header__addresses-popover" *ngIf="openAddresses">
|
||||
<button (click)="openAddresses = !openAddresses" class="close">
|
||||
<shared-icon icon="close" [size]="24"></shared-icon>
|
||||
</button>
|
||||
|
||||
<div class="page-pickup-shelf-details-header__addresses-popover-data">
|
||||
<div *ngIf="order.shipping" class="page-pickup-shelf-details-header__addresses-popover-delivery">
|
||||
<p>Lieferadresse</p>
|
||||
<div class="page-pickup-shelf-details-header__addresses-popover-delivery-data">
|
||||
<ng-container *ngIf="order.shipping?.data?.organisation">
|
||||
<p>{{ order.shipping?.data?.organisation?.name }}</p>
|
||||
<p>{{ order.shipping?.data?.organisation?.department }}</p>
|
||||
</ng-container>
|
||||
<p>{{ order.shipping?.data?.firstName }} {{ order.shipping?.data?.lastName }}</p>
|
||||
<p>{{ order.shipping?.data?.address?.info }}</p>
|
||||
<p>{{ order.shipping?.data?.address?.street }} {{ order.shipping?.data?.address?.streetNumber }}</p>
|
||||
<p>{{ order.shipping?.data?.address?.zipCode }} {{ order.shipping?.data?.address?.city }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="order.billing" class="page-pickup-shelf-details-header__addresses-popover-billing">
|
||||
<p>Rechnungsadresse</p>
|
||||
<div class="page-pickup-shelf-details-header__addresses-popover-billing-data">
|
||||
<ng-container *ngIf="order.billing?.data?.organisation">
|
||||
<p>{{ order.billing?.data?.organisation?.name }}</p>
|
||||
<p>{{ order.billing?.data?.organisation?.department }}</p>
|
||||
</ng-container>
|
||||
<p>{{ order.billing?.data?.firstName }} {{ order.billing?.data?.lastName }}</p>
|
||||
<p>{{ order.billing?.data?.address?.info }}</p>
|
||||
<p>{{ order.billing?.data?.address?.street }} {{ order.billing?.data?.address?.streetNumber }}</p>
|
||||
<p>{{ order.billing?.data?.address?.zipCode }} {{ order.billing?.data?.address?.city }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="page-pickup-shelf-details-header__select grow" *ngIf="showMultiselect$ | async">
|
||||
<button class="cta-select-all" (click)="selectAll()">Alle auswählen</button>
|
||||
{{ selectedOrderItemCount$ | async }} von {{ orderItemCount$ | async }} Titeln
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ng-template #featureLoading>
|
||||
|
||||
@@ -81,51 +81,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.page-pickup-shelf-details-header__select {
|
||||
@apply flex flex-col items-end;
|
||||
}
|
||||
|
||||
.page-pickup-shelf-details-header__additional-addresses {
|
||||
.page-pickup-shelf-details-header__addresses-popover {
|
||||
@apply absolute inset-x-0 top-16 bottom-0 z-popover;
|
||||
|
||||
.close {
|
||||
@apply bg-white absolute right-0 p-6;
|
||||
}
|
||||
|
||||
.page-pickup-shelf-details-header__addresses-popover-data {
|
||||
@apply flex flex-col bg-white p-6 z-popover min-h-[200px];
|
||||
box-shadow: 0px 6px 24px rgba(206, 212, 219, 0.8);
|
||||
|
||||
.page-pickup-shelf-details-header__addresses-popover-delivery {
|
||||
@apply grid mb-6;
|
||||
grid-template-columns: 9.5625rem auto;
|
||||
|
||||
.page-pickup-shelf-details-header__addresses-popover-delivery-data {
|
||||
p {
|
||||
@apply font-bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.page-pickup-shelf-details-header__addresses-popover-billing {
|
||||
@apply grid;
|
||||
grid-template-columns: 9.5625rem auto;
|
||||
|
||||
.page-pickup-shelf-details-header__addresses-popover-billing-data {
|
||||
p {
|
||||
@apply font-bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cta-select-all {
|
||||
@apply text-brand bg-transparent text-p2 font-bold outline-none border-none;
|
||||
}
|
||||
|
||||
.fetch-wrapper {
|
||||
@apply grid grid-flow-col gap-4;
|
||||
}
|
||||
|
||||
@@ -92,10 +92,6 @@ export class PickUpShelfDetailsHeaderComponent {
|
||||
minDateDatepicker = this.dateAdapter.addCalendarDays(this.dateAdapter.today(), -1);
|
||||
today = this.dateAdapter.today();
|
||||
|
||||
selectedOrderItemCount$ = this._store.selectedOrderItemIds$.pipe(map((ids) => ids?.length ?? 0));
|
||||
|
||||
orderItemCount$ = this._store.orderItems$.pipe(map((items) => items?.length ?? 0));
|
||||
|
||||
orderItem$ = this._store.orderItems$.pipe(map((orderItems) => orderItems?.find((_) => true)));
|
||||
|
||||
changeDateLoader$ = new BehaviorSubject<boolean>(false);
|
||||
@@ -115,14 +111,6 @@ export class PickUpShelfDetailsHeaderComponent {
|
||||
|
||||
statusActions$ = this.orderItem$.pipe(map((orderItem) => orderItem?.actions?.filter((action) => action.enabled === false)));
|
||||
|
||||
get isItemSelectable() {
|
||||
return this._store.orderItems?.some((item) => !!item?.actions && item?.actions?.length > 0);
|
||||
}
|
||||
|
||||
showMultiselect$ = combineLatest([this._store.orderItems$, this._store.fetchPartial$]).pipe(
|
||||
map(([orderItems, fetchPartial]) => this.isItemSelectable && fetchPartial && orderItems?.length > 1)
|
||||
);
|
||||
|
||||
crudaUpdate$ = this.orderItem$.pipe(map((orederItem) => !!(orederItem?.cruda & 4)));
|
||||
|
||||
editButtonDisabled$ = combineLatest([this.changeStatusLoader$, this.crudaUpdate$]).pipe(
|
||||
@@ -133,20 +121,6 @@ export class PickUpShelfDetailsHeaderComponent {
|
||||
map(([statusActions, crudaUpdate]) => statusActions?.length > 0 && crudaUpdate)
|
||||
);
|
||||
|
||||
openAddresses: boolean = false;
|
||||
|
||||
get digOrderNumber(): string {
|
||||
return this.order?.linkedRecords?.find((_) => true)?.number;
|
||||
}
|
||||
|
||||
get showAddresses(): boolean {
|
||||
return (this.order?.orderType === 2 || this.order?.orderType === 4) && (!!this.order?.shipping || !!this.order?.billing);
|
||||
}
|
||||
|
||||
get showFeature(): boolean {
|
||||
return !!this.order?.features && !!this.order?.features?.orderType;
|
||||
}
|
||||
|
||||
constructor(private dateAdapter: DateAdapter, private cdr: ChangeDetectorRef) {}
|
||||
|
||||
async handleActionClick(action?: KeyValueDTOOfStringAndString) {
|
||||
@@ -156,10 +130,6 @@ export class PickUpShelfDetailsHeaderComponent {
|
||||
this.cdr.markForCheck();
|
||||
}
|
||||
|
||||
selectAll() {
|
||||
this._store.selectAllOrderItemIds();
|
||||
}
|
||||
|
||||
updatePickupDeadline(date: Date) {
|
||||
this.updateDate.emit({ date, type: 'pickup' });
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
|
||||
@Pipe({
|
||||
name: 'notificationType',
|
||||
standalone: true,
|
||||
})
|
||||
export class NotificationTypePipe implements PipeTransform {
|
||||
transform(notificationType: string): string {
|
||||
switch (notificationType) {
|
||||
case 'NOTIFICATION_EMAIL':
|
||||
return 'Benachrichtigung';
|
||||
case 'REMINDER_EMAIL':
|
||||
return 'Erinnerung';
|
||||
case 'ORDERCONFIRMATION_EMAIL':
|
||||
return 'Bestellbestätigung';
|
||||
case 'NOTIFICATION_SMS':
|
||||
return 'Benachrichtigung';
|
||||
default:
|
||||
return notificationType;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,8 +11,10 @@
|
||||
<img [uiOverlayTrigger]="emailTooltip" src="/assets/images/email_bookmark.svg" />
|
||||
<ui-tooltip yPosition="above" xPosition="after" [yOffset]="-11" [xOffset]="-8" #emailTooltip [closeable]="true">
|
||||
Per E-Mail benachrichtigt <br />
|
||||
<ng-container *ngFor="let notification of emailNotificationDates$ | async">
|
||||
{{ notification | date: 'dd.MM.yyyy | HH:mm' }} Uhr<br />
|
||||
<ng-container *ngFor="let notifications of emailNotificationDates$ | async">
|
||||
<ng-container *ngFor="let notificationDate of notifications.dates">
|
||||
{{ notifications.type | notificationType }} {{ notificationDate | date: 'dd.MM.yyyy | HH:mm' }} Uhr<br />
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ui-tooltip>
|
||||
</ng-container>
|
||||
@@ -20,8 +22,10 @@
|
||||
<img [uiOverlayTrigger]="smsTooltip" src="/assets/images/sms_bookmark.svg" />
|
||||
<ui-tooltip yPosition="above" xPosition="after" [yOffset]="-11" [xOffset]="-8" #smsTooltip [closeable]="true">
|
||||
Per SMS benachrichtigt <br />
|
||||
<ng-container *ngFor="let notification of smsNotificationDates$ | async">
|
||||
{{ notification | date: 'dd.MM.yyyy | HH:mm' }} Uhr<br />
|
||||
<ng-container *ngFor="let notifications of smsNotificationDates$ | async">
|
||||
<ng-container *ngFor="let notificationDate of notifications.dates">
|
||||
{{ notificationDate | date: 'dd.MM.yyyy | HH:mm' }} Uhr<br />
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ui-tooltip>
|
||||
</ng-container>
|
||||
@@ -86,7 +90,7 @@
|
||||
<div class="label">ISBN/EAN</div>
|
||||
<div class="value">{{ orderItem.product?.ean }}</div>
|
||||
</div>
|
||||
<div class="detail" *ngIf="!!orderItem.price">
|
||||
<div class="detail" *ngIf="orderItem.price !== undefined">
|
||||
<div class="label">Preis</div>
|
||||
<div class="value">{{ orderItem.price | currency: 'EUR' }}</div>
|
||||
</div>
|
||||
|
||||
@@ -24,6 +24,7 @@ import { map, switchMap } from 'rxjs/operators';
|
||||
import { Subject, combineLatest } from 'rxjs';
|
||||
import { PickupShelfDetailsStore } from '../../store';
|
||||
import { UiQuantityDropdownModule } from '@ui/quantity-dropdown';
|
||||
import { NotificationTypePipe } from './notification-type.pipe';
|
||||
|
||||
export interface PickUpShelfDetailsItemComponentState {
|
||||
orderItem?: DBHOrderItemListItemDTO;
|
||||
@@ -55,6 +56,7 @@ export interface PickUpShelfDetailsItemComponentState {
|
||||
PickupShelfPaymentTypePipe,
|
||||
IconModule,
|
||||
UiQuantityDropdownModule,
|
||||
NotificationTypePipe,
|
||||
],
|
||||
})
|
||||
export class PickUpShelfDetailsItemComponent extends ComponentStore<PickUpShelfDetailsItemComponentState> implements OnInit {
|
||||
@@ -117,6 +119,7 @@ export class PickUpShelfDetailsItemComponent extends ComponentStore<PickUpShelfD
|
||||
set quantity(quantity: number) {
|
||||
if (this.quantity !== quantity) {
|
||||
this.patchState({ quantity });
|
||||
this._store.setSelectedOrderItemQuantity({ orderItemSubsetId: this.orderItem.orderItemSubsetId, quantity });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
<div class="flex flex-row items-center relative bg-[#F5F7FA] p-4 rounded-t mb-[0.125rem]">
|
||||
<div *ngIf="showFeature" class="flex flex-row items-center mr-3">
|
||||
<ng-container [ngSwitch]="orderType">
|
||||
<ng-container *ngSwitchCase="'Versand'">
|
||||
<div class="flex items-center justify-center bg-[#D8DFE5] w-[2.25rem] h-[2.25rem] rounded rounded-br-none mr-2">
|
||||
<shared-icon [size]="24" icon="isa-truck"></shared-icon>
|
||||
</div>
|
||||
<p class="font-bold text-p1">Versand</p>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="'DIG-Versand'">
|
||||
<div class="flex items-center justify-center bg-[#D8DFE5] w-[2.25rem] h-[2.25rem] rounded rounded-br-none mr-2">
|
||||
<shared-icon [size]="24" icon="isa-truck"></shared-icon>
|
||||
</div>
|
||||
<p class="font-bold text-p1">Versand</p>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="'B2B-Versand'">
|
||||
<div class="flex items-center justify-center bg-[#D8DFE5] w-[2.25rem] h-[2.25rem] rounded rounded-br-none mr-2">
|
||||
<shared-icon [size]="24" icon="isa-b2b-truck"></shared-icon>
|
||||
</div>
|
||||
<p class="font-bold text-p1">B2B-Versand</p>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="'Abholung'">
|
||||
<div class="flex items-center justify-center bg-[#D8DFE5] w-[2.25rem] h-[2.25rem] rounded rounded-br-none mr-2">
|
||||
<shared-icon [size]="24" icon="isa-box-out"></shared-icon>
|
||||
</div>
|
||||
<p class="font-bold text-p1 mr-3">Abholung</p>
|
||||
{{ targetBranches }}
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="'Rücklage'">
|
||||
<div class="flex items-center justify-center bg-[#D8DFE5] w-[2.25rem] h-[2.25rem] rounded rounded-br-none mr-2">
|
||||
<shared-icon [size]="24" icon="isa-shopping-bag"></shared-icon>
|
||||
</div>
|
||||
<p class="font-bold text-p1">Rücklage</p>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="'Download'">
|
||||
<div class="flex items-center justify-center bg-[#D8DFE5] w-[2.25rem] h-[2.25rem] rounded rounded-br-none mr-2">
|
||||
<shared-icon [size]="24" icon="isa-download"></shared-icon>
|
||||
</div>
|
||||
<p class="font-bold text-p1">Download</p>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<div class="page-pickup-shelf-details-items-group__additional-addresses" *ngIf="showAddresses">
|
||||
<button (click)="openAddresses = !openAddresses" class="text-[#0556B4]">
|
||||
Lieferadresse / Rechnungsadresse {{ openAddresses ? 'ausblenden' : 'anzeigen' }}
|
||||
</button>
|
||||
|
||||
<div class="page-pickup-shelf-details-items-group__addresses-popover" *ngIf="openAddresses">
|
||||
<button (click)="openAddresses = !openAddresses" class="close">
|
||||
<shared-icon icon="close" [size]="24"></shared-icon>
|
||||
</button>
|
||||
|
||||
<div class="page-pickup-shelf-details-items-group__addresses-popover-data">
|
||||
<div *ngIf="order.shipping" class="page-pickup-shelf-details-items-group__addresses-popover-delivery">
|
||||
<p>Lieferadresse</p>
|
||||
<div class="page-pickup-shelf-details-items-group__addresses-popover-delivery-data">
|
||||
<ng-container *ngIf="order.shipping?.data?.organisation">
|
||||
<p>{{ order.shipping?.data?.organisation?.name }}</p>
|
||||
<p>{{ order.shipping?.data?.organisation?.department }}</p>
|
||||
</ng-container>
|
||||
<p>{{ order.shipping?.data?.firstName }} {{ order.shipping?.data?.lastName }}</p>
|
||||
<p>{{ order.shipping?.data?.address?.info }}</p>
|
||||
<p>{{ order.shipping?.data?.address?.street }} {{ order.shipping?.data?.address?.streetNumber }}</p>
|
||||
<p>{{ order.shipping?.data?.address?.zipCode }} {{ order.shipping?.data?.address?.city }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="order.billing" class="page-pickup-shelf-details-items-group__addresses-popover-billing">
|
||||
<p>Rechnungsadresse</p>
|
||||
<div class="page-pickup-shelf-details-items-group__addresses-popover-billing-data">
|
||||
<ng-container *ngIf="order.billing?.data?.organisation">
|
||||
<p>{{ order.billing?.data?.organisation?.name }}</p>
|
||||
<p>{{ order.billing?.data?.organisation?.department }}</p>
|
||||
</ng-container>
|
||||
<p>{{ order.billing?.data?.firstName }} {{ order.billing?.data?.lastName }}</p>
|
||||
<p>{{ order.billing?.data?.address?.info }}</p>
|
||||
<p>{{ order.billing?.data?.address?.street }} {{ order.billing?.data?.address?.streetNumber }}</p>
|
||||
<p>{{ order.billing?.data?.address?.zipCode }} {{ order.billing?.data?.address?.city }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="page-pickup-shelf-details-items-group__select grow" *ngIf="showMultiselect$ | async">
|
||||
<button class="cta-select-all" (click)="selectAll()">Alle auswählen</button>
|
||||
{{ selectedOrderItemCount$ | async }} von {{ groupedItems.length }} Titeln
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,48 @@
|
||||
:host {
|
||||
@apply grid grid-flow-row;
|
||||
}
|
||||
|
||||
.page-pickup-shelf-details-items-group__select {
|
||||
@apply flex flex-col items-end;
|
||||
}
|
||||
|
||||
.page-pickup-shelf-details-items-group__additional-addresses {
|
||||
.page-pickup-shelf-details-items-group__addresses-popover {
|
||||
@apply absolute inset-x-0 top-16 bottom-0 z-popover;
|
||||
|
||||
.close {
|
||||
@apply bg-white absolute right-0 p-6;
|
||||
}
|
||||
|
||||
.page-pickup-shelf-details-items-group__addresses-popover-data {
|
||||
@apply flex flex-col bg-white p-6 z-popover min-h-[200px];
|
||||
box-shadow: 0px 6px 24px rgba(206, 212, 219, 0.8);
|
||||
|
||||
.page-pickup-shelf-details-items-group__addresses-popover-delivery {
|
||||
@apply grid mb-6;
|
||||
grid-template-columns: 9.5625rem auto;
|
||||
|
||||
.page-pickup-shelf-details-items-group__addresses-popover-delivery-data {
|
||||
p {
|
||||
@apply font-bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.page-pickup-shelf-details-items-group__addresses-popover-billing {
|
||||
@apply grid;
|
||||
grid-template-columns: 9.5625rem auto;
|
||||
|
||||
.page-pickup-shelf-details-items-group__addresses-popover-billing-data {
|
||||
p {
|
||||
@apply font-bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cta-select-all {
|
||||
@apply text-brand bg-transparent text-p2 font-bold outline-none border-none;
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import { ChangeDetectionStrategy, Component, Input, OnInit, inject } from '@angular/core';
|
||||
import { PickupShelfDetailsStore } from '../../store';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { DBHOrderItemListItemDTO, OrderDTO } from '@swagger/oms';
|
||||
import { AsyncPipe, NgFor, NgIf, NgSwitch, NgSwitchCase } from '@angular/common';
|
||||
import { IconModule } from '@shared/components/icon';
|
||||
|
||||
@Component({
|
||||
selector: 'page-pickup-shelf-details-items-group',
|
||||
templateUrl: 'pickup-shelf-details-items-group.component.html',
|
||||
styleUrls: ['pickup-shelf-details-items-group.component.scss'],
|
||||
standalone: true,
|
||||
host: { class: 'page-pickup-shelf-details-items-group' },
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [NgIf, NgFor, NgSwitch, NgSwitchCase, IconModule, AsyncPipe],
|
||||
})
|
||||
export class PickUpShelfDetailsItemsGroupComponent implements OnInit {
|
||||
private _store = inject(PickupShelfDetailsStore);
|
||||
|
||||
get order(): OrderDTO {
|
||||
return this._store.order;
|
||||
}
|
||||
|
||||
@Input() orderType: string;
|
||||
|
||||
@Input() groupedItems: DBHOrderItemListItemDTO[];
|
||||
|
||||
get firstGroupedItem() {
|
||||
return this.groupedItems?.find((_) => true);
|
||||
}
|
||||
|
||||
openAddresses: boolean = false;
|
||||
|
||||
get showAddresses(): boolean {
|
||||
return (this.order?.orderType === 2 || this.order?.orderType === 4) && (!!this.order?.shipping || !!this.order?.billing);
|
||||
}
|
||||
|
||||
get showFeature(): boolean {
|
||||
return !!this.firstGroupedItem?.features && !!this.firstGroupedItem?.features?.orderType;
|
||||
}
|
||||
|
||||
get targetBranches(): string {
|
||||
return Array.from(new Set(this.groupedItems?.map((item) => item?.targetBranch))).join('; ');
|
||||
}
|
||||
|
||||
get isItemSelectable() {
|
||||
return this.groupedItems?.some((item) => !!item?.actions && item?.actions?.length > 0);
|
||||
}
|
||||
|
||||
showMultiselect$ = this._store.fetchPartial$.pipe(
|
||||
map((fetchPartial) => this.isItemSelectable && fetchPartial && this.groupedItems?.length > 1)
|
||||
);
|
||||
|
||||
selectedOrderItemCount$ = this._store.selectedOrderItemIds$.pipe(
|
||||
map((ids) => this.groupedItems?.filter((groupedItem) => ids?.includes(groupedItem?.orderItemSubsetId))?.length ?? 0)
|
||||
);
|
||||
|
||||
constructor() {}
|
||||
|
||||
ngOnInit() {}
|
||||
|
||||
selectAll() {
|
||||
this._store.selectAllOrderItemIds();
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,10 @@ export class PickupShelfFilterComponent {
|
||||
return this._environment.matchDesktopLarge$.pipe(map((matches) => !(matches && this.sideOutlet === 'search')));
|
||||
}
|
||||
|
||||
get isDesktopLarge() {
|
||||
return this._environment.matchDesktopLarge();
|
||||
}
|
||||
|
||||
get hasProcessId$() {
|
||||
return this._activatedRoute?.parent?.params?.pipe(map((params) => !!params?.processId));
|
||||
}
|
||||
@@ -53,7 +57,9 @@ export class PickupShelfFilterComponent {
|
||||
return this._activatedRoute?.parent?.params?.pipe(
|
||||
map((params) => {
|
||||
const hasProcessId = !!params?.processId;
|
||||
if (!!this.order) {
|
||||
|
||||
// Ticket #4457 only route to details view if in split-screen
|
||||
if (!!this.order && this.isDesktopLarge) {
|
||||
return this._routeToShelfDetails(hasProcessId);
|
||||
}
|
||||
|
||||
@@ -134,7 +140,8 @@ export class PickupShelfFilterComponent {
|
||||
}
|
||||
|
||||
clearFilter(filter: Filter) {
|
||||
this.store.setQueryParams({});
|
||||
const { main_qs } = filter.getQueryParams();
|
||||
this.store.setQueryParams({ main_qs });
|
||||
}
|
||||
|
||||
hasSelectedOptions(filter: Filter) {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*ngIf="historyItem$ | async; let item"
|
||||
class="self-end"
|
||||
type="button"
|
||||
(click)="store.processId ? navigateToShelfOutDetailsPage(item) : navigateToShelfInDetailsPage(item)"
|
||||
(click)="store.processId !== 7000 ? navigateToShelfOutDetailsPage(item) : navigateToShelfInDetailsPage(item)"
|
||||
>
|
||||
<shared-icon icon="close" [size]="26"></shared-icon>
|
||||
</button>
|
||||
|
||||
@@ -10,6 +10,7 @@ import { DBHOrderItemListItemDTO } from '@swagger/oms';
|
||||
import { Observable, combineLatest } from 'rxjs';
|
||||
import { map, shareReplay, switchMap, take } from 'rxjs/operators';
|
||||
import { PickupShelfStore } from '../../store';
|
||||
import { coerceBooleanProperty } from '@angular/cdk/coercion';
|
||||
|
||||
@Component({
|
||||
selector: 'page-pickup-shelf-history',
|
||||
@@ -90,6 +91,7 @@ export class PickUpShelfHistoryComponent {
|
||||
orderItemSubsetId: item.orderItemSubsetId,
|
||||
compartmentInfo: item.compartmentInfo,
|
||||
},
|
||||
side: coerceBooleanProperty(this._activatedRoute?.snapshot?.queryParams?.side),
|
||||
}).path,
|
||||
{ queryParamsHandling: 'preserve' }
|
||||
);
|
||||
|
||||
@@ -20,7 +20,10 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="page-pickup-shelf-list-item__item-title-contributors flex flex-col">
|
||||
<div
|
||||
class="page-pickup-shelf-list-item__item-title-contributors flex flex-col"
|
||||
[class.mr-32]="showCompartmentCode && item.features?.paid && (isTablet || isDesktopSmall || primaryOutletActive)"
|
||||
>
|
||||
<div
|
||||
class="page-pickup-shelf-list-item__item-contributors text-p2 font-normal text-ellipsis overflow-hidden max-w-[24rem] whitespace-nowrap mb-[0.375rem]"
|
||||
>
|
||||
@@ -36,23 +39,23 @@
|
||||
</div>
|
||||
|
||||
<div class="page-pickup-shelf-list-item__item-ean-quantity-changed flex flex-col">
|
||||
<div class="page-pickup-shelf-list-item__item-ean text-p2 flex flex-row mb-[0.375rem]">
|
||||
<div class="page-pickup-shelf-list-item__item-ean text-p2 flex flex-row mb-[0.375rem]" [attr.data-ean]="item?.product?.ean">
|
||||
<div class="min-w-[7.5rem]">EAN</div>
|
||||
<div class="font-bold">{{ item?.product?.ean }}</div>
|
||||
</div>
|
||||
|
||||
<div class="page-pickup-shelf-list-item__item-quantity flex flex-row text-p2 mb-[0.375rem]">
|
||||
<div class="page-pickup-shelf-list-item__item-quantity flex flex-row text-p2 mb-[0.375rem]" [attr.data-menge]="item.quantity">
|
||||
<div class="min-w-[7.5rem]">Menge</div>
|
||||
<div class="font-bold">{{ item.quantity }} x</div>
|
||||
</div>
|
||||
|
||||
<div class="page-pickup-shelf-list-item__item-changed text-p2">
|
||||
<div class="page-pickup-shelf-list-item__item-changed text-p2" [attr.data-geaendert]="item?.processingStatusDate">
|
||||
<div *ngIf="showChangeDate; else showOrderDate" class="flex flex-row">
|
||||
<div class="min-w-[7.5rem]">Geändert</div>
|
||||
<div class="font-bold">{{ item?.processingStatusDate | date: 'dd.MM.yy | HH:mm' }} Uhr</div>
|
||||
</div>
|
||||
<ng-template #showOrderDate>
|
||||
<div class="flex flex-row">
|
||||
<div class="flex flex-row" [attr.data-bestelldatum]="item?.orderDate">
|
||||
<div class="min-w-[7.5rem]">Bestelldatum</div>
|
||||
<div class="font-bold">{{ item?.orderDate | date: 'dd.MM.yy | HH:mm' }} Uhr</div>
|
||||
</div>
|
||||
@@ -64,6 +67,8 @@
|
||||
<div
|
||||
*ngIf="showCompartmentCode"
|
||||
class="page-pickup-shelf-list-item__item-order-number text-h3 mb-[0.375rem] self-end font-bold break-all text-right"
|
||||
[attr.data-compartment-code]="item?.compartmentCode"
|
||||
[attr.data-compartment-info]="item?.compartmentInfo"
|
||||
>
|
||||
{{ item?.compartmentCode }}{{ item?.compartmentInfo && '_' + item?.compartmentInfo }}
|
||||
</div>
|
||||
@@ -72,6 +77,7 @@
|
||||
<div
|
||||
class="page-pickup-shelf-list-item__item-processing-status flex flex-row mb-[0.375rem] rounded p-3 py-[0.125rem] text-white"
|
||||
[style]="processingStatusColor"
|
||||
[attr.data-processing-status]="item.processingStatus"
|
||||
>
|
||||
{{ item.processingStatus | processingStatus }}
|
||||
</div>
|
||||
@@ -80,6 +86,7 @@
|
||||
<div
|
||||
class="font-bold flex flex-row items-center justify-center text-p2 text-[#26830C]"
|
||||
*ngIf="item.features?.paid && (isTablet || isDesktopSmall || primaryOutletActive)"
|
||||
[attr.data-paid]="item.features?.paid"
|
||||
>
|
||||
<shared-icon class="flex items-center justify-center mr-[0.375rem]" [size]="24" icon="credit-card"></shared-icon>
|
||||
{{ item.features?.paid }}
|
||||
@@ -102,7 +109,10 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="page-pickup-shelf-list-item__item-special-comment break-words font-bold text-p2 mt-[0.375rem] text-[#996900]">
|
||||
<div
|
||||
[attr.data-special-comment]="item?.specialComment"
|
||||
class="page-pickup-shelf-list-item__item-special-comment break-words font-bold text-p2 mt-[0.375rem] text-[#996900]"
|
||||
>
|
||||
{{ item?.specialComment }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -15,6 +15,8 @@ export const selectDisplayedCompartmentCode = (s: PickupShelfDetailsState) => s.
|
||||
|
||||
export const selectDisplayedCompartmentInfo = (s: PickupShelfDetailsState) => s.displayedCompartmentInfo;
|
||||
|
||||
export const selectDisplayedOrderItemSubsetId = (s: PickupShelfDetailsState) => s.displayedOrderItemSubsetId;
|
||||
|
||||
export const selectOrderItems = (s: PickupShelfDetailsState): DBHOrderItemListItemDTO[] => {
|
||||
let items = selectOrderItemsRaw(s);
|
||||
|
||||
@@ -45,6 +47,10 @@ export const selectOrderItems = (s: PickupShelfDetailsState): DBHOrderItemListIt
|
||||
return items;
|
||||
};
|
||||
|
||||
export const selectNoOrderItemsFound = (s: PickupShelfDetailsState) => {
|
||||
return s.fetchingOrderItems === false && selectOrderItems(s)?.length === 0;
|
||||
};
|
||||
|
||||
export const selectCustomer = (s: PickupShelfDetailsState) => s.customer;
|
||||
|
||||
export const selectReceipts = (s: PickupShelfDetailsState) => s.receipts;
|
||||
@@ -160,22 +166,33 @@ export const selectNotifications = (orderItemSubsetId: number) => (s: PickupShel
|
||||
}, {} as Record<string, Date[]>);
|
||||
};
|
||||
|
||||
export const selectLatestNotificationDatesFor = (orderItemSubsetId: number, key: string) => (s: PickupShelfDetailsState) => {
|
||||
export const selectLatestNotificationDatesFor = (orderItemSubsetId: number, keys: string[]) => (s: PickupShelfDetailsState) => {
|
||||
const notifications = selectNotifications(orderItemSubsetId)(s);
|
||||
return (
|
||||
notifications?.[key]?.filter((date) => {
|
||||
let dates: Array<{ type: string; dates: Date[] }> = [];
|
||||
|
||||
for (const key of keys) {
|
||||
const notification = notifications?.[key] ?? [];
|
||||
|
||||
const validDates = notification.filter((date) => {
|
||||
// check if curr is an invalid date
|
||||
return !isNaN(date.getTime());
|
||||
}) ?? []
|
||||
);
|
||||
});
|
||||
|
||||
const mappedDates = { type: key, dates: validDates };
|
||||
|
||||
if (mappedDates.dates?.length > 0) {
|
||||
dates.push(mappedDates);
|
||||
}
|
||||
}
|
||||
return dates;
|
||||
};
|
||||
|
||||
export const selectLatestEmailNotificationDates = (orderItemSubsetId: number) => (s: PickupShelfDetailsState) => {
|
||||
return selectLatestNotificationDatesFor(orderItemSubsetId, 'NOTIFICATION_EMAIL')(s);
|
||||
return selectLatestNotificationDatesFor(orderItemSubsetId, ['NOTIFICATION_EMAIL', 'REMINDER_EMAIL', 'ORDERCONFIRMATION_EMAIL'])(s);
|
||||
};
|
||||
|
||||
export const selectLatestSmsNotificationDate2 = (orderItemSubsetId: number) => (s: PickupShelfDetailsState) => {
|
||||
return selectLatestNotificationDatesFor(orderItemSubsetId, 'NOTIFICATION_SMS')(s);
|
||||
return selectLatestNotificationDatesFor(orderItemSubsetId, ['NOTIFICATION_SMS'])(s);
|
||||
};
|
||||
|
||||
export const selectCanSelectAction = (s: PickupShelfDetailsState) => {
|
||||
@@ -225,8 +242,7 @@ export const selectMainActions = (s: PickupShelfDetailsState) => {
|
||||
|
||||
return firstItem?.actions
|
||||
?.filter((action) => typeof action?.enabled !== 'boolean')
|
||||
?.filter((action) => (fetchPartial ? !action.command.includes('FETCHED_PARTIAL') : true))
|
||||
?.sort((a, b) => (a.selected === b.selected ? 0 : a.selected ? -1 : 1));
|
||||
?.filter((action) => (fetchPartial ? !action.command.includes('FETCHED_PARTIAL') : true));
|
||||
};
|
||||
|
||||
export const selectCustomerNumber = (s: PickupShelfDetailsState) => {
|
||||
|
||||
@@ -20,6 +20,7 @@ export interface PickupShelfDetailsState {
|
||||
displayedOrderItemProcessingStatus?: OrderItemProcessingStatusValue;
|
||||
displayedCompartmentCode?: string;
|
||||
displayedCompartmentInfo?: string;
|
||||
displayedOrderItemSubsetId?: number;
|
||||
|
||||
fetchingCustomer?: boolean;
|
||||
customer?: CustomerInfoDTO;
|
||||
|
||||
@@ -26,9 +26,12 @@ import { CustomerInfoDTO, ListResponseArgsOfCustomerInfoDTO } from '@swagger/crm
|
||||
import { CacheService } from '@core/cache';
|
||||
import { RunCheckTrigger } from '../trigger';
|
||||
import { DomainReceiptService } from '@domain/oms';
|
||||
import { PickupShelfStore } from './pickup-shelf.store';
|
||||
|
||||
@Injectable()
|
||||
export class PickupShelfDetailsStore extends ComponentStore<PickupShelfDetailsState> {
|
||||
private _listStore = inject(PickupShelfStore);
|
||||
|
||||
private _cacheService = inject(CacheService);
|
||||
|
||||
private _pickupShelfService = inject(PickupShelfService);
|
||||
@@ -65,12 +68,24 @@ export class PickupShelfDetailsStore extends ComponentStore<PickupShelfDetailsSt
|
||||
return this.get(Selectors.selectFetchingOrderItems);
|
||||
}
|
||||
|
||||
noOrderItemsFound$ = this.select(Selectors.selectNoOrderItemsFound);
|
||||
|
||||
get noOrderItemsFound() {
|
||||
return this.get(Selectors.selectNoOrderItemsFound);
|
||||
}
|
||||
|
||||
processingStatus$ = this.select(Selectors.selectDisplayedOrderItemProcessingStatus);
|
||||
|
||||
get processingStatus() {
|
||||
return this.get(Selectors.selectDisplayedOrderItemProcessingStatus);
|
||||
}
|
||||
|
||||
orderItemSubsetId$ = this.select(Selectors.selectDisplayedOrderItemSubsetId);
|
||||
|
||||
get orderItemSubsetId() {
|
||||
return this.get(Selectors.selectDisplayedOrderItemSubsetId);
|
||||
}
|
||||
|
||||
customer$ = this.select(Selectors.selectCustomer);
|
||||
|
||||
get customer() {
|
||||
@@ -236,6 +251,10 @@ export class PickupShelfDetailsStore extends ComponentStore<PickupShelfDetailsSt
|
||||
this.patchState({ fetchPartial });
|
||||
}
|
||||
|
||||
resetCoverItems() {
|
||||
this.patchState({ coverOrderItems: [] });
|
||||
}
|
||||
|
||||
resetSelectedOrderItems() {
|
||||
this.patchState({ selectedOrderItemIds: [] });
|
||||
}
|
||||
@@ -244,6 +263,10 @@ export class PickupShelfDetailsStore extends ComponentStore<PickupShelfDetailsSt
|
||||
this.patchState({ selectedOrderItemQuantity: {} });
|
||||
}
|
||||
|
||||
setSelectedOrderItemQuantity = this.updater((state, { orderItemSubsetId, quantity }: { orderItemSubsetId: number; quantity: number }) => {
|
||||
return { ...state, selectedOrderItemQuantity: { ...state.selectedOrderItemQuantity, [orderItemSubsetId]: quantity } };
|
||||
});
|
||||
|
||||
setPreviousSelectedOrderItemSubsetId(previousSelectedOrderItemSubsetId: number) {
|
||||
this.patchState({ previousSelectedOrderItemSubsetId });
|
||||
}
|
||||
@@ -352,13 +375,16 @@ export class PickupShelfDetailsStore extends ComponentStore<PickupShelfDetailsSt
|
||||
compartmentCode?: string;
|
||||
compartmentInfo?: string;
|
||||
orderItemProcessingStatus?: OrderItemProcessingStatusValue;
|
||||
orderItemSubsetId?: number;
|
||||
orderDate?: string;
|
||||
done?: { resolve: (items: DBHOrderItemListItemDTO[]) => void; reject: (err?) => void };
|
||||
}>
|
||||
) =>
|
||||
trigger$.pipe(
|
||||
tap((data) => this.beforeFetchOrderItems(data)),
|
||||
switchMap(({ compartmentCode, orderNumber, compartmentInfo, orderItemProcessingStatus, done }) =>
|
||||
this._pickupShelfIOService.getOrderItemsByOrderNumberOrCompartmentCode({ compartmentCode, orderNumber }).pipe(
|
||||
withLatestFrom(this._listStore.filter$),
|
||||
switchMap(([{ compartmentCode, orderNumber, compartmentInfo, orderItemProcessingStatus, done }, filter]) =>
|
||||
this._pickupShelfIOService.getOrderItemsByOrderNumberOrCompartmentCode({ compartmentCode, orderNumber, filter }).pipe(
|
||||
tapResponse(
|
||||
(r) => this.fetchOrderItemsSuccess({ compartmentCode, compartmentInfo, orderItemProcessingStatus }, done?.resolve)(r),
|
||||
(e) => this.fetchOrderItemsFailed(done?.reject)(e)
|
||||
@@ -372,11 +398,13 @@ export class PickupShelfDetailsStore extends ComponentStore<PickupShelfDetailsSt
|
||||
compartmentCode,
|
||||
compartmentInfo,
|
||||
orderItemProcessingStatus,
|
||||
orderItemSubsetId,
|
||||
}: {
|
||||
orderNumber?: string;
|
||||
compartmentCode?: string;
|
||||
compartmentInfo?: string;
|
||||
orderItemProcessingStatus?: OrderItemProcessingStatusValue;
|
||||
orderItemSubsetId?: number;
|
||||
}) => {
|
||||
const orderItems =
|
||||
this._cacheService.get<DBHOrderItemListItemDTO[]>({
|
||||
@@ -394,6 +422,7 @@ export class PickupShelfDetailsStore extends ComponentStore<PickupShelfDetailsSt
|
||||
displayedCompartmentCode: compartmentCode,
|
||||
displayedCompartmentInfo: compartmentInfo,
|
||||
displayedOrderItemProcessingStatus: orderItemProcessingStatus,
|
||||
displayedOrderItemSubsetId: orderItemSubsetId,
|
||||
selectedOrderItemIds: [],
|
||||
orderItemSubsetTasks: {},
|
||||
fetchingOrderItemSubsetTasks: {},
|
||||
@@ -469,6 +498,12 @@ export class PickupShelfDetailsStore extends ComponentStore<PickupShelfDetailsSt
|
||||
}
|
||||
|
||||
const customer = res.result[0];
|
||||
|
||||
if (!customer) {
|
||||
this.patchState({ fetchingCustomer: false });
|
||||
return;
|
||||
}
|
||||
|
||||
const isKulturpass = this.order?.features?.orderSource === 'KulturPass';
|
||||
|
||||
// check if the customer is the same as the order's buyer
|
||||
@@ -514,10 +549,10 @@ export class PickupShelfDetailsStore extends ComponentStore<PickupShelfDetailsSt
|
||||
this.patchOrderItemSubsetInState({
|
||||
orderItemSubsetId: item.orderItemSubsetId,
|
||||
changes: {
|
||||
specialComment: res.result.specialComment,
|
||||
estimatedShippingDate: res.result.estimatedShippingDate,
|
||||
estimatedDelivery: res.result.estimatedDelivery,
|
||||
pickUpDeadline: res.result.compartmentStop,
|
||||
specialComment: res?.result?.specialComment ?? item.specialComment,
|
||||
estimatedShippingDate: res.result?.estimatedShippingDate ?? item.estimatedShippingDate,
|
||||
estimatedDelivery: res.result?.estimatedDelivery ?? item.estimatedDelivery,
|
||||
pickUpDeadline: res.result?.compartmentStop ?? item.pickUpDeadline,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -527,7 +562,8 @@ export class PickupShelfDetailsStore extends ComponentStore<PickupShelfDetailsSt
|
||||
};
|
||||
|
||||
private patchOrderItemSubsetError = (err: any) => {
|
||||
this._modal.error('Fehler beim Speichern des Kommentars', err);
|
||||
this._modal.error('Fehler beim Speichern der Position', err);
|
||||
console.error(err);
|
||||
};
|
||||
|
||||
patchPreferredPickUpDateOnOrderSubsetItemInState({
|
||||
@@ -539,11 +575,11 @@ export class PickupShelfDetailsStore extends ComponentStore<PickupShelfDetailsSt
|
||||
}) {
|
||||
// Filter selected order subset items from order
|
||||
const items = this.order.items;
|
||||
const subsetItems: EntityDTOContainerOfOrderItemSubsetDTO[] = items
|
||||
.reduce((acc, item) => {
|
||||
return [...acc, ...item.data.subsetItems];
|
||||
}, [])
|
||||
.filter((item) => this.selectedOrderItemIds.find((id) => id === item.data.id));
|
||||
const subsetItems: EntityDTOContainerOfOrderItemSubsetDTO[] = items.reduce((acc, item) => {
|
||||
return [...acc, ...item.data.subsetItems];
|
||||
}, []);
|
||||
// #4487 - RD // Abholfach - ändern des vsl. Lieferdatums und "zurückgelegt bis"-Datum wirft Fehler
|
||||
// .filter((item) => this.selectedOrderItemIds.find((id) => id === item.data.id));
|
||||
|
||||
// Update preferredPickUpDate on subsetItem from order and patch the state
|
||||
const subsetItem = subsetItems.find((subsetItem) => subsetItem.data.id === item.orderItemSubsetId);
|
||||
@@ -561,12 +597,13 @@ export class PickupShelfDetailsStore extends ComponentStore<PickupShelfDetailsSt
|
||||
(state, { orderItemSubsetId, changes }: { orderItemSubsetId: number; changes: Partial<DBHOrderItemListItemDTO> }) => {
|
||||
return {
|
||||
...state,
|
||||
orderItems: state.orderItems.map((i) => {
|
||||
if (i.orderItemSubsetId === orderItemSubsetId) {
|
||||
return { ...i, ...changes };
|
||||
}
|
||||
return i;
|
||||
}),
|
||||
orderItems:
|
||||
state.orderItems?.map((i) => {
|
||||
if (i.orderItemSubsetId === orderItemSubsetId) {
|
||||
return { ...i, ...changes };
|
||||
}
|
||||
return i;
|
||||
}) ?? [],
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
@@ -41,10 +41,7 @@ export function selectFilter(state: PickupShelfState) {
|
||||
}
|
||||
|
||||
// Wenn queryParams ein leeres Objekt ist, dann wird der Filter gesetzt, aber ohne Werte (leerer Filter)
|
||||
if (isEmpty(queryParams)) {
|
||||
filter.unselectAllFilterOptions();
|
||||
return filter;
|
||||
}
|
||||
filter.unselectAllFilterOptions();
|
||||
|
||||
// Wenn queryParams ein Objekt mit Werten ist, dann wird der Filter gesetzt
|
||||
filter.fromQueryParams(queryParams);
|
||||
|
||||
@@ -113,7 +113,13 @@ export class PickupShelfStore extends ComponentStore<PickupShelfState> implement
|
||||
}
|
||||
|
||||
setQueryParams(queryParams: Record<string, string> | undefined) {
|
||||
this.patchState({ queryParams });
|
||||
// #4533 Wenn ein Abholschein gescannt wird, soll ORD: nicht in der Suchbox stehen und somit auch nicht in der Breadcrumb enthalten sein
|
||||
const isScannedPickUpCode = queryParams?.main_qs?.includes('ORD:');
|
||||
if (isScannedPickUpCode) {
|
||||
this.patchState({ queryParams: { ...queryParams, main_qs: queryParams?.main_qs?.replace('ORD:', '') } });
|
||||
} else {
|
||||
this.patchState({ queryParams });
|
||||
}
|
||||
}
|
||||
|
||||
cancelListRequests() {
|
||||
@@ -164,7 +170,7 @@ export class PickupShelfStore extends ComponentStore<PickupShelfState> implement
|
||||
private beforeFetchQuerySettings = () => {
|
||||
const cachedQuerySettings = this._cacheService.get<QuerySettingsDTO>({
|
||||
name: 'pickup-shelf',
|
||||
providerName: this._pickupShelfIOService.constructor.name,
|
||||
providerName: this._pickupShelfIOService.name(),
|
||||
});
|
||||
|
||||
if (!!cachedQuerySettings) {
|
||||
@@ -179,10 +185,7 @@ export class PickupShelfStore extends ComponentStore<PickupShelfState> implement
|
||||
private fetchQuerySettingsDone = (resp: ResponseArgsOfQuerySettingsDTO) => {
|
||||
this.patchState({ fetchingQuerySettings: false, querySettings: resp.result });
|
||||
|
||||
this._cacheService.set<QuerySettingsDTO>(
|
||||
{ name: 'pickup-shelf', providerName: this._pickupShelfIOService.constructor.name },
|
||||
resp.result
|
||||
);
|
||||
this._cacheService.set<QuerySettingsDTO>({ name: 'pickup-shelf', providerName: this._pickupShelfIOService.name() }, resp.result);
|
||||
};
|
||||
|
||||
private fetchQuerySettingsError = (err: any) => {
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
<button class="filter" (click)="shellFilterOverlay.open()">
|
||||
<ui-icon size="20px" icon="filter_alit"></ui-icon>
|
||||
<span class="label">Filter</span>
|
||||
</button>
|
||||
<div class="flex flex-row items-center h-16 bg-white relative rounded-t font-bold shadow-lg">
|
||||
<h3 class="text-center grow font-bold text-h3">Remission</h3>
|
||||
<button
|
||||
class="filter absolute right-0 top-0 h-14 rounded px-5 text-lg bg-cadet-blue grid grid-flow-col gap-2 items-center"
|
||||
type="button"
|
||||
(click)="shellFilterOverlay.open()"
|
||||
>
|
||||
<shared-icon icon="filter-variant"></shared-icon>
|
||||
<span class="label">Filter</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<shell-filter-overlay #shellFilterOverlay>
|
||||
<page-remission-filter *ngIf="shellFilterOverlay.isOpen" (close)="shellFilterOverlay.close()"></page-remission-filter>
|
||||
@@ -16,7 +23,6 @@
|
||||
(scrolledIndexChange)="scrolledIndexChange($event)"
|
||||
>
|
||||
<div>
|
||||
<h1 class="text-h3 font-bold text-center">Remission</h1>
|
||||
<div class="text-center">
|
||||
<p class="text-xl mt-4">
|
||||
Wählen Sie den Bereich aus dem <br />
|
||||
@@ -25,9 +31,10 @@
|
||||
|
||||
<div class="inline-flex flex-row bg-white rounded-md mt-4">
|
||||
<button
|
||||
class="w-48 py-2 bg-white rounded-md font-bold"
|
||||
class="w-48 py-2 rounded-md font-bold"
|
||||
type="button"
|
||||
*ngFor="let source of sources$ | async"
|
||||
[class.bg-white]="(selectedSource$ | async) !== source"
|
||||
[class.bg-active-branch]="(selectedSource$ | async) === source"
|
||||
[class.text-white]="(selectedSource$ | async) === source"
|
||||
(click)="setSource(source)"
|
||||
@@ -93,7 +100,7 @@
|
||||
<div class="h-px-100"></div>
|
||||
</cdk-virtual-scroll-viewport>
|
||||
|
||||
<div class="actions">
|
||||
<div class="actions flex items-center justify-center bottom-40 sticky gap-7">
|
||||
<ng-container>
|
||||
<a
|
||||
*ngIf="showStartRemissionAction$ | async"
|
||||
|
||||
@@ -15,16 +15,8 @@
|
||||
}
|
||||
|
||||
.filter {
|
||||
@apply absolute font-sans flex items-center font-bold bg-inactive-branch border-0 text-p2 -top-12 right-0 py-px-8 px-px-15 rounded-filter justify-center z-sticky;
|
||||
|
||||
min-width: 106px;
|
||||
|
||||
.label {
|
||||
@apply ml-px-5;
|
||||
}
|
||||
|
||||
&.active {
|
||||
@apply bg-active-branch text-white ml-px-5;
|
||||
@apply bg-active-branch text-white;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,19 +25,13 @@
|
||||
}
|
||||
|
||||
.actions {
|
||||
@apply fixed inline-grid grid-flow-col gap-7;
|
||||
bottom: 7.25rem;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
|
||||
.disabled {
|
||||
@apply cursor-not-allowed bg-inactive-branch;
|
||||
}
|
||||
}
|
||||
|
||||
.cta-scroll {
|
||||
@apply absolute shadow-cta border-none outline-none bg-white w-12 h-12 rounded-full grid items-center justify-center;
|
||||
bottom: 26px;
|
||||
@apply bottom-[5.5rem] absolute shadow-cta border-none outline-none bg-white w-12 h-12 rounded-full grid items-center justify-center;
|
||||
right: 5%;
|
||||
|
||||
ui-icon {
|
||||
|
||||
@@ -10,6 +10,7 @@ import { RouterModule } from '@angular/router';
|
||||
import { RequiredCapacitiesModule } from '../required-capacities/required-capacities.module';
|
||||
import { SharedShippingDocumentDetailsModule } from '../shared/shipping-document-details/shipping-document-details.module';
|
||||
import { RemissionListItemLoadingModule } from './remission-list-item-loading/remission-list-item-loading.module';
|
||||
import { IconModule } from '@shared/components/icon';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@@ -23,6 +24,7 @@ import { RemissionListItemLoadingModule } from './remission-list-item-loading/re
|
||||
SharedShippingDocumentDetailsModule,
|
||||
RemissionListItemLoadingModule,
|
||||
ScrollingModule,
|
||||
IconModule,
|
||||
],
|
||||
exports: [RemissionListComponent],
|
||||
declarations: [RemissionListComponent],
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Clipboard } from '@angular/cdk/clipboard';
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { DomainPrinterService } from '@domain/printer';
|
||||
@@ -8,7 +8,7 @@ import { PrintModalComponent, PrintModalData } from '@modal/printer';
|
||||
import { ArticleDTO, DisplayInfoDTO } from '@swagger/eis';
|
||||
import { UiModalRef, UiModalService } from '@ui/modal';
|
||||
import { first, map } from 'rxjs/operators';
|
||||
|
||||
import { ProductCatalogNavigationService } from '@shared/services';
|
||||
@Component({
|
||||
selector: 'page-article-list-modal',
|
||||
templateUrl: 'article-list-modal.component.html',
|
||||
@@ -16,6 +16,8 @@ import { first, map } from 'rxjs/operators';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ArticleListModalComponent {
|
||||
productCatalogNavigationService = inject(ProductCatalogNavigationService);
|
||||
|
||||
articles$ = this.domainTaskCalendarService.getArticles({ infoId: this.modalRef.data.id }).pipe(map((response) => response.result));
|
||||
expandedArticle: ArticleDTO;
|
||||
|
||||
@@ -64,15 +66,10 @@ export class ArticleListModalComponent {
|
||||
|
||||
this.modalRef.close('closeAll');
|
||||
|
||||
if (!lastActivatedProcessId) {
|
||||
const processId = Date.now();
|
||||
this.router.navigate(['/kunde', processId, 'product', 'search', 'results'], {
|
||||
this.productCatalogNavigationService
|
||||
.getArticleSearchResultsPath(lastActivatedProcessId, {
|
||||
queryParams: { main_qs: taskCalendarSearch },
|
||||
});
|
||||
} else {
|
||||
this.router.navigate(['/kunde', String(lastActivatedProcessId), 'product', 'search', 'results'], {
|
||||
queryParams: { main_qs: taskCalendarSearch },
|
||||
});
|
||||
}
|
||||
})
|
||||
.navigate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
</button>
|
||||
</div>
|
||||
<div class="shared-breadcrumb__crumbs">
|
||||
<ng-container *ngFor="let crumb of breadcrumbs$ | async; let last = last">
|
||||
<a class="shared-breadcrumb__crumb" [routerLink]="crumb.path" [queryParams]="crumb.params">
|
||||
<ng-container *ngFor="let crumb of breadcrumbs$ | async; let idx = index; let last = last">
|
||||
<a class="shared-breadcrumb__crumb" [attr.data-index]="idx" [routerLink]="crumb.path" [queryParams]="crumb.params">
|
||||
<span [class.font-bold]="last">
|
||||
{{ crumb.name }}
|
||||
</span>
|
||||
|
||||
@@ -6,16 +6,4 @@ div {
|
||||
@screen desktop-small {
|
||||
@apply w-desktop-small;
|
||||
}
|
||||
|
||||
@screen desktop {
|
||||
@apply w-desktop;
|
||||
}
|
||||
|
||||
@screen desktop-x-large {
|
||||
@apply w-desktop-x-large;
|
||||
}
|
||||
|
||||
@screen desktop-xx-large {
|
||||
@apply w-desktop-xx-large;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,12 +24,17 @@
|
||||
<span class="error" *ngIf="errors.pattern">Keine gültige E-Mail Adresse</span>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div class="pl-4" *ngIf="channelActionName && notificationChannels.length !== 2">
|
||||
<button type="reset" class="text-black pl-4" *ngIf="!emailDisabled && !!emailControl.value" (click)="clear(emailControl)">
|
||||
<shared-icon icon="close" [size]="24"></shared-icon>
|
||||
</button>
|
||||
<div class="pl-4" *ngIf="showChannelActionNameForEmailControl()">
|
||||
<button
|
||||
data-cta-type="save"
|
||||
data-cta-form="email"
|
||||
class="text-p1 font-bold text-brand outline-none border-none bg-transparent right-0"
|
||||
[disabled]="channelActionLoading || emailControl?.errors?.required || emailControl?.errors?.pattern"
|
||||
[disabled]="emailDisabled"
|
||||
type="button"
|
||||
(click)="channelActionEvent.emit(notificationChannels)"
|
||||
(click)="save()"
|
||||
>
|
||||
{{ channelActionName }}
|
||||
</button>
|
||||
@@ -44,18 +49,17 @@
|
||||
<span class="error" *ngIf="errors.pattern">Keine gültige Mobilnummer</span>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div class="pl-4" *ngIf="channelActionName">
|
||||
<button type="reset" class="text-black pl-4" *ngIf="!mobileDisabled && !!mobileControl.value" (click)="clear(mobileControl)">
|
||||
<shared-icon icon="close" [size]="24"></shared-icon>
|
||||
</button>
|
||||
<div class="pl-4" *ngIf="showChannelActionNameForMobileControl()">
|
||||
<button
|
||||
data-cta-type="save"
|
||||
data-cta-form="mobile"
|
||||
class="text-p1 font-bold text-brand outline-none border-none bg-transparent right-0"
|
||||
[disabled]="
|
||||
channelActionLoading ||
|
||||
mobileControl?.errors?.required ||
|
||||
mobileControl?.errors?.pattern ||
|
||||
emailControl?.errors?.required ||
|
||||
emailControl?.errors?.pattern
|
||||
"
|
||||
[disabled]="mobileDisabled"
|
||||
type="button"
|
||||
(click)="channelActionEvent.emit(notificationChannels)"
|
||||
(click)="save()"
|
||||
>
|
||||
{{ channelActionName }}
|
||||
</button>
|
||||
|
||||
@@ -43,6 +43,10 @@ export class SharedNotificationChannelControlComponent extends ComponentStore<Sh
|
||||
return !!(this.notificationChannelControl.value & 1) && this.emailControl;
|
||||
}
|
||||
|
||||
get emailDisabled() {
|
||||
return this.channelActionLoading || this.emailControl?.errors?.required || this.emailControl?.errors?.pattern;
|
||||
}
|
||||
|
||||
get mobileControl() {
|
||||
return this.notificationGroup.get('mobile') as FormControl;
|
||||
}
|
||||
@@ -51,6 +55,10 @@ export class SharedNotificationChannelControlComponent extends ComponentStore<Sh
|
||||
return !!(this.notificationChannelControl.value & 2) && this.mobileControl;
|
||||
}
|
||||
|
||||
get mobileDisabled() {
|
||||
return this.channelActionLoading || this.mobileControl?.errors?.required || this.mobileControl?.errors?.pattern;
|
||||
}
|
||||
|
||||
get displayToggle() {
|
||||
return this.displayEmail || this.displayMobile;
|
||||
}
|
||||
@@ -91,6 +99,20 @@ export class SharedNotificationChannelControlComponent extends ComponentStore<Sh
|
||||
this.initNotificationChannels$();
|
||||
}
|
||||
|
||||
showChannelActionNameForEmailControl() {
|
||||
return !!this.channelActionName && this.emailControl?.dirty;
|
||||
}
|
||||
|
||||
showChannelActionNameForMobileControl() {
|
||||
return !!this.channelActionName && this.mobileControl?.dirty;
|
||||
}
|
||||
|
||||
clear(control: FormControl) {
|
||||
control.setValue('');
|
||||
control.markAsDirty();
|
||||
this._cdr.markForCheck();
|
||||
}
|
||||
|
||||
initNotificationChannels$() {
|
||||
if (this.notificationGroup) {
|
||||
this.notificationChannels$ = this.notificationChannelControl.valueChanges.pipe(startWith(this.notificationChannelControl.value)).pipe(
|
||||
@@ -136,4 +158,11 @@ export class SharedNotificationChannelControlComponent extends ComponentStore<Sh
|
||||
this.emailControl?.updateValueAndValidity();
|
||||
this.mobileControl?.updateValueAndValidity();
|
||||
}
|
||||
|
||||
save() {
|
||||
this.channelActionEvent.emit(this.notificationChannels);
|
||||
this.emailControl?.markAsPristine();
|
||||
this.mobileControl?.markAsPristine();
|
||||
this._cdr.markForCheck();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
<div class="hidden desktop-large:block side-content" [class.hide-side]="sideOutletNotActivated">
|
||||
<router-outlet #sideOutlet="outlet" name="side"></router-outlet>
|
||||
<div class="shared-splitscreen__side" *ngIf="desktopLarge()" [class.shared-splitscreen__hidden]="!sideActivated()">
|
||||
<router-outlet
|
||||
*ngIf="desktopLarge()"
|
||||
#sideOutlet="outlet"
|
||||
(activate)="onActivate()"
|
||||
(deactivate)="onDeactivate()"
|
||||
name="side"
|
||||
></router-outlet>
|
||||
</div>
|
||||
<div class="col-span-2 desktop-large:col-span-1 main-content" [class.expand-primary]="sideOutletNotActivated">
|
||||
<div class="shared-splitscreen__gap" *ngIf="desktopLarge()" [class.shared-splitscreen__hidden]="!sideActivated()"></div>
|
||||
<div class="shared-splitscreen__primary">
|
||||
<router-outlet #primaryOutlet="outlet"></router-outlet>
|
||||
</div>
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
:host {
|
||||
@apply grid grid-cols-split-screen gap-split-screen h-split-screen-tablet max-h-split-screen-tablet desktop-small:h-split-screen-desktop desktop-small:max-h-split-screen-desktop overflow-scroll;
|
||||
@apply flex flex-row h-split-screen-tablet max-h-split-screen-tablet desktop-small:h-split-screen-desktop desktop-small:max-h-split-screen-desktop overflow-scroll;
|
||||
}
|
||||
|
||||
.hide-side {
|
||||
@apply hidden;
|
||||
.shared-splitscreen__side {
|
||||
@apply w-[31rem] min-w-[31rem] flex-grow-0 flex-shrink;
|
||||
}
|
||||
|
||||
.expand-primary {
|
||||
@apply col-span-2;
|
||||
.shared-splitscreen__gap {
|
||||
@apply w-[1.5rem] min-w-[1.5rem] flex-grow-0 flex-shrink;
|
||||
}
|
||||
|
||||
.shared-splitscreen__primary {
|
||||
@apply flex-grow;
|
||||
}
|
||||
|
||||
.shared-splitscreen__hidden {
|
||||
@apply min-w-0 w-0;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
import { ChangeDetectionStrategy, Component, ViewChild } from '@angular/core';
|
||||
import { RouterOutlet } from '@angular/router';
|
||||
import { AsyncPipe, NgIf } from '@angular/common';
|
||||
import {
|
||||
AfterContentInit,
|
||||
AfterViewInit,
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
DestroyRef,
|
||||
OnInit,
|
||||
QueryList,
|
||||
ViewChild,
|
||||
ViewChildren,
|
||||
computed,
|
||||
inject,
|
||||
signal,
|
||||
} from '@angular/core';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
import { ActivatedRoute, RouterOutlet } from '@angular/router';
|
||||
import { EnvironmentService } from '@core/environment';
|
||||
import { OnInitDirective } from '@shared/directives/element-lifecycle';
|
||||
import { NEVER } from 'rxjs';
|
||||
import { switchMap } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'shared-splitscreen',
|
||||
@@ -8,18 +27,34 @@ import { RouterOutlet } from '@angular/router';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
host: { class: 'shared-splitscreen' },
|
||||
imports: [RouterOutlet],
|
||||
imports: [RouterOutlet, AsyncPipe, NgIf],
|
||||
})
|
||||
export class SharedSplitscreenComponent {
|
||||
@ViewChild('sideOutlet', { static: true, read: RouterOutlet })
|
||||
sideOutlet: RouterOutlet;
|
||||
export class SharedSplitscreenComponent implements AfterViewInit {
|
||||
destroyRef = inject(DestroyRef);
|
||||
|
||||
@ViewChild('primaryOutlet', { static: true, read: RouterOutlet })
|
||||
primaryOutlet: RouterOutlet;
|
||||
environment = inject(EnvironmentService);
|
||||
|
||||
get sideOutletNotActivated() {
|
||||
return !(this.sideOutlet && this.sideOutlet.isActivated);
|
||||
@ViewChildren('sideOutlet', { read: RouterOutlet })
|
||||
side: QueryList<RouterOutlet>;
|
||||
|
||||
desktopLarge = signal(false);
|
||||
|
||||
sideActivated = signal(false);
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.environment.matchDesktopLarge$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((desktopLarge) => {
|
||||
this.desktopLarge.set(desktopLarge);
|
||||
if (!desktopLarge) {
|
||||
this.sideActivated.set(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
constructor() {}
|
||||
onActivate() {
|
||||
this.sideActivated.set(true);
|
||||
}
|
||||
|
||||
onDeactivate() {
|
||||
this.sideActivated.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"$schema": "../../../../node_modules/ng-packagr/ng-package.schema.json",
|
||||
"lib": {
|
||||
"entryFile": "src/public-api.ts"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
import { coerceStringArray } from '@angular/cdk/coercion';
|
||||
import { DestroyRef, Directive, ElementRef, EventEmitter, Input, OnInit, Output, Renderer2, inject } from '@angular/core';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
import { NavigationEnd, Router } from '@angular/router';
|
||||
import { ComponentStore } from '@ngrx/component-store';
|
||||
import { filter, switchMap } from 'rxjs/operators';
|
||||
|
||||
export interface SharedRouterLinkActiveDirectiveState {
|
||||
classList: string[];
|
||||
test: RegExp | undefined;
|
||||
}
|
||||
|
||||
@Directive({ selector: '[sharedRegexRouterLinkActive]', standalone: true })
|
||||
export class RegexRouterLinkActiveDirective extends ComponentStore<SharedRouterLinkActiveDirectiveState> implements OnInit {
|
||||
destroyRef = inject(DestroyRef);
|
||||
|
||||
router = inject(Router);
|
||||
|
||||
elementRef = inject(ElementRef);
|
||||
|
||||
renderer = inject(Renderer2);
|
||||
|
||||
@Input('sharedRegexRouterLinkActive')
|
||||
set classList(value: string[] | string) {
|
||||
this.patchState({ classList: coerceStringArray(value) });
|
||||
}
|
||||
get classList() {
|
||||
return this.get((s) => s.classList);
|
||||
}
|
||||
|
||||
@Input('sharedRegexRouterLinkActiveTest')
|
||||
set test(value: RegExp | string) {
|
||||
const test = typeof value === 'string' ? new RegExp(value) : value;
|
||||
|
||||
this.patchState({ test });
|
||||
}
|
||||
get test() {
|
||||
return this.get((s) => s.test);
|
||||
}
|
||||
|
||||
@Output()
|
||||
isActiveChange = new EventEmitter<boolean>();
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
classList: [],
|
||||
test: undefined,
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.router.events
|
||||
.pipe(
|
||||
takeUntilDestroyed(this.destroyRef),
|
||||
filter((event) => event instanceof NavigationEnd),
|
||||
switchMap(() => this.select((s) => s))
|
||||
)
|
||||
.subscribe(() => this.checkActiveLink());
|
||||
|
||||
this.checkActiveLink();
|
||||
}
|
||||
|
||||
checkActiveLink() {
|
||||
const { classList, test } = this.get((s) => s);
|
||||
|
||||
let isActive = test?.test(this.router.url) ?? false;
|
||||
|
||||
this.isActiveChange.emit(isActive);
|
||||
|
||||
classList.forEach((className) => {
|
||||
if (isActive) {
|
||||
this.renderer.addClass(this.elementRef.nativeElement, className);
|
||||
} else {
|
||||
this.renderer.removeClass(this.elementRef.nativeElement, className);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './lib/regex-router-link-active.directive';
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Directive, EventEmitter, Output } from '@angular/core';
|
||||
import { NumberInput, coerceElement, coerceNumberProperty } from '@angular/cdk/coercion';
|
||||
import { Directive, ElementRef, EventEmitter, HostListener, Input, Output, inject } from '@angular/core';
|
||||
|
||||
@Directive({ selector: '[sharedScrollContainer]', standalone: true })
|
||||
@Directive({ selector: '[sharedScrollContainer]', standalone: true, host: { class: 'shared-scroll-container' } })
|
||||
export class ScrollContainerDirective {
|
||||
@Output()
|
||||
scrollIndexChange = new EventEmitter<{ start: number; end: number }>();
|
||||
elementRef = inject(ElementRef);
|
||||
|
||||
@Output()
|
||||
scrolledToTop = new EventEmitter<void>();
|
||||
@@ -11,17 +11,50 @@ export class ScrollContainerDirective {
|
||||
@Output()
|
||||
scrolledToBottom = new EventEmitter<void>();
|
||||
|
||||
constructor() {}
|
||||
private _delta: number = 0;
|
||||
|
||||
@Input()
|
||||
set delta(value: NumberInput) {
|
||||
this._delta = coerceNumberProperty(value);
|
||||
}
|
||||
|
||||
get scrollPosition() {
|
||||
const element: HTMLElement = coerceElement(this.elementRef);
|
||||
return element.scrollTop;
|
||||
}
|
||||
|
||||
@HostListener('scroll', ['$event'])
|
||||
onScroll(_: Event) {
|
||||
const element: HTMLElement = coerceElement(this.elementRef);
|
||||
const { scrollTop, scrollHeight, clientHeight } = element;
|
||||
|
||||
if (scrollTop === 0) {
|
||||
this.scrolledToTop.emit();
|
||||
} else if (scrollTop + clientHeight + this._delta >= scrollHeight) {
|
||||
this.scrolledToBottom.emit();
|
||||
}
|
||||
}
|
||||
|
||||
scrollToIndex(index: number) {
|
||||
throw new Error('not implemented');
|
||||
const element: HTMLElement = coerceElement(this.elementRef);
|
||||
const { scrollHeight, clientHeight } = element;
|
||||
const itemHeight = scrollHeight / clientHeight;
|
||||
const scrollPosition = index * itemHeight;
|
||||
element.scrollTo({ top: scrollPosition });
|
||||
}
|
||||
|
||||
scrollTo(position: number) {
|
||||
const element: HTMLElement = coerceElement(this.elementRef);
|
||||
element.scrollTo({ top: position });
|
||||
}
|
||||
|
||||
scrollToTop() {
|
||||
throw new Error('not implemented');
|
||||
const element: HTMLElement = coerceElement(this.elementRef);
|
||||
element.scrollTo({ top: 0 });
|
||||
}
|
||||
|
||||
scrollToBottom() {
|
||||
throw new Error('not implemented');
|
||||
const element: HTMLElement = coerceElement(this.elementRef);
|
||||
element.scrollTo({ top: element.scrollHeight });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,16 @@ export function getPurchaseOption(state: PurchaseOptionsState): PurchaseOption {
|
||||
return options[0];
|
||||
}
|
||||
|
||||
return state.purchaseOption;
|
||||
if (state.purchaseOption) {
|
||||
return state.purchaseOption;
|
||||
}
|
||||
|
||||
// #4380 Abholung als Standard wenn möglich und keine Auswahl getroffen wurde
|
||||
if (options.includes('pickup')) {
|
||||
return 'pickup';
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function getSelectedItemIds(state: PurchaseOptionsState): number[] {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { NumberInput, coerceNumberProperty } from '@angular/cdk/coercion';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { CustomerInfoDTO } from '@swagger/crm';
|
||||
import { CustomerDTO, CustomerInfoDTO } from '@swagger/crm';
|
||||
import { encodeFormData, mapCustomerInfoDtoToCustomerCreateFormData } from 'apps/page/customer/src/lib/create-customer';
|
||||
import { NavigationRoute } from './navigation-route';
|
||||
|
||||
@@ -36,7 +36,7 @@ export class CustomerCreateNavigation {
|
||||
return this._router.navigate(route.path, { queryParams: route.queryParams });
|
||||
}
|
||||
|
||||
createCustomerRoute(params: { processId: NumberInput; customerType?: string }): NavigationRoute {
|
||||
createCustomerRoute(params: { processId: NumberInput; customerType?: string; customerInfo?: CustomerInfoDTO }): NavigationRoute {
|
||||
const path = [
|
||||
'/kunde',
|
||||
coerceNumberProperty(params.processId),
|
||||
@@ -49,12 +49,20 @@ export class CustomerCreateNavigation {
|
||||
},
|
||||
];
|
||||
|
||||
const urlTree = this._router.createUrlTree(path, { queryParams: {} });
|
||||
let formData = params?.customerInfo ? encodeFormData(mapCustomerInfoDtoToCustomerCreateFormData(params.customerInfo)) : undefined;
|
||||
|
||||
const urlTree = this._router.createUrlTree(path, {
|
||||
queryParams: {
|
||||
formData,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
path,
|
||||
urlTree,
|
||||
queryParams: {},
|
||||
queryParams: {
|
||||
formData,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { NavigationRoute } from './navigation-route';
|
||||
import { Router } from '@angular/router';
|
||||
import { Config } from '@core/config';
|
||||
import { DBHOrderItemListItemDTO, OrderItemProcessingStatusValue } from '@swagger/oms';
|
||||
import { isBoolean } from 'lodash';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class PickupShelfInNavigationService {
|
||||
@@ -50,8 +51,14 @@ export class PickupShelfInNavigationService {
|
||||
};
|
||||
}
|
||||
|
||||
detailRoute({ item }: { item: DBHOrderItemListItemDTO }): NavigationRoute {
|
||||
detailRoute({ item, side }: { item: DBHOrderItemListItemDTO; side?: boolean }): NavigationRoute {
|
||||
let path: any[];
|
||||
side = isBoolean(side) ? side : true;
|
||||
|
||||
if (!item.orderItemSubsetId) {
|
||||
console.log(item);
|
||||
throw new Error('orderItemSubsetId is missing');
|
||||
}
|
||||
|
||||
if (item.compartmentCode) {
|
||||
path = [
|
||||
@@ -65,14 +72,18 @@ export class PickupShelfInNavigationService {
|
||||
'compartment',
|
||||
item.compartmentCode ? encodeURIComponent(item.compartmentCode) : undefined,
|
||||
item.compartmentInfo ? encodeURIComponent(item.compartmentInfo) : undefined,
|
||||
,
|
||||
item.orderItemSubsetId,
|
||||
].filter((v) => !!v),
|
||||
side: ['list'],
|
||||
side: side ? ['list'] : null,
|
||||
},
|
||||
},
|
||||
].filter((v) => !!v);
|
||||
} else {
|
||||
if (!item.processingStatus) {
|
||||
console.log(item);
|
||||
throw new Error('processingStatus is missing');
|
||||
}
|
||||
|
||||
path = [
|
||||
'/filiale',
|
||||
'pickup-shelf',
|
||||
@@ -87,13 +98,15 @@ export class PickupShelfInNavigationService {
|
||||
item.processingStatus,
|
||||
item.orderItemSubsetId,
|
||||
].filter((v) => !!v),
|
||||
side: ['list'],
|
||||
side: side ? ['list'] : null,
|
||||
},
|
||||
},
|
||||
].filter((v) => !!v);
|
||||
}
|
||||
|
||||
const queryParams = {};
|
||||
const queryParams = {
|
||||
side: String(side),
|
||||
};
|
||||
|
||||
const urlTree = this._router.createUrlTree(path, { queryParams });
|
||||
|
||||
@@ -104,16 +117,24 @@ export class PickupShelfInNavigationService {
|
||||
};
|
||||
}
|
||||
|
||||
editRoute(item: {
|
||||
orderId: number;
|
||||
orderNumber: string;
|
||||
compartmentCode: string;
|
||||
processingStatus: OrderItemProcessingStatusValue;
|
||||
orderItemSubsetId: number;
|
||||
compartmentInfo: string;
|
||||
}): NavigationRoute {
|
||||
editRoute(
|
||||
item: {
|
||||
orderId: number;
|
||||
orderNumber: string;
|
||||
compartmentCode: string;
|
||||
processingStatus: OrderItemProcessingStatusValue;
|
||||
orderItemSubsetId: number;
|
||||
compartmentInfo: string;
|
||||
},
|
||||
{ side }: { side?: boolean } = { side: true }
|
||||
): NavigationRoute {
|
||||
let path: any[];
|
||||
|
||||
if (!item.orderItemSubsetId) {
|
||||
console.log(item);
|
||||
throw new Error('orderItemSubsetId is missing');
|
||||
}
|
||||
|
||||
if (item.compartmentCode) {
|
||||
path = [
|
||||
'/filiale',
|
||||
@@ -126,11 +147,10 @@ export class PickupShelfInNavigationService {
|
||||
'compartment',
|
||||
item.compartmentCode ? encodeURIComponent(item.compartmentCode) : undefined,
|
||||
item.compartmentInfo ? encodeURIComponent(item.compartmentInfo) : undefined,
|
||||
,
|
||||
item.orderItemSubsetId,
|
||||
'edit',
|
||||
].filter((v) => !!v),
|
||||
side: ['list'],
|
||||
side: side ? ['list'] : null,
|
||||
},
|
||||
},
|
||||
].filter((v) => !!v);
|
||||
@@ -150,7 +170,7 @@ export class PickupShelfInNavigationService {
|
||||
item.orderItemSubsetId,
|
||||
'edit',
|
||||
].filter((v) => !!v),
|
||||
side: ['list'],
|
||||
side: side ? ['list'] : null,
|
||||
},
|
||||
},
|
||||
].filter((v) => !!v);
|
||||
@@ -167,16 +187,24 @@ export class PickupShelfInNavigationService {
|
||||
};
|
||||
}
|
||||
|
||||
historyRoute(item: {
|
||||
orderId: number;
|
||||
orderNumber: string;
|
||||
compartmentCode: string;
|
||||
processingStatus: OrderItemProcessingStatusValue;
|
||||
orderItemSubsetId: number;
|
||||
compartmentInfo: string;
|
||||
}): NavigationRoute {
|
||||
historyRoute(
|
||||
item: {
|
||||
orderId: number;
|
||||
orderNumber: string;
|
||||
compartmentCode: string;
|
||||
processingStatus: OrderItemProcessingStatusValue;
|
||||
orderItemSubsetId: number;
|
||||
compartmentInfo: string;
|
||||
},
|
||||
{ side }: { side?: boolean } = { side: true }
|
||||
): NavigationRoute {
|
||||
let path: any[];
|
||||
|
||||
if (!item.orderItemSubsetId) {
|
||||
console.log(item);
|
||||
throw new Error('orderItemSubsetId is missing');
|
||||
}
|
||||
|
||||
if (item.compartmentCode) {
|
||||
path = [
|
||||
'/filiale',
|
||||
@@ -192,7 +220,7 @@ export class PickupShelfInNavigationService {
|
||||
item.orderItemSubsetId,
|
||||
'history',
|
||||
].filter((v) => !!v),
|
||||
side: ['list'],
|
||||
side: side ? ['list'] : null,
|
||||
},
|
||||
},
|
||||
].filter((v) => !!v);
|
||||
@@ -212,7 +240,7 @@ export class PickupShelfInNavigationService {
|
||||
item.orderItemSubsetId,
|
||||
'history',
|
||||
].filter((v) => !!v),
|
||||
side: ['list'],
|
||||
side: side ? ['list'] : null,
|
||||
},
|
||||
},
|
||||
].filter((v) => !!v);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { DOCUMENT } from '@angular/common';
|
||||
import { Inject, Injectable } from '@angular/core';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
@@ -14,6 +15,32 @@ export class ShellService {
|
||||
return this._fontSize$.value;
|
||||
}
|
||||
|
||||
get scale() {
|
||||
if (this._fontSize$.value === 'small') {
|
||||
return 13 / 16;
|
||||
} else if (this._fontSize$.value === 'normal') {
|
||||
return 1;
|
||||
} else if (this._fontSize$.value === 'large') {
|
||||
return 19 / 16;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
scale$ = this._fontSize$.pipe(
|
||||
map((size) => {
|
||||
if (size === 'small') {
|
||||
return 13 / 16;
|
||||
} else if (size === 'normal') {
|
||||
return 1;
|
||||
} else if (size === 'large') {
|
||||
return 19 / 16;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
private _sideMenuOpen$ = new BehaviorSubject<boolean>(false);
|
||||
|
||||
sideMenuOpen$ = this._sideMenuOpen$.asObservable();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user