mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-31 09:37:15 +01:00
Compare commits
4 Commits
hotfix/453
...
performanc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fc949e27b9 | ||
|
|
ed110b443c | ||
|
|
f188ece61e | ||
|
|
14cc3f4895 |
@@ -458,6 +458,10 @@ export class DomainAvailabilityService {
|
||||
return [2, 32, 256, 1024, 2048, 4096].some((code) => availability?.availabilityType === code);
|
||||
}
|
||||
|
||||
private _priceIsEmpty(price: PriceDTO) {
|
||||
return isEmpty(price?.value) || isEmpty(price?.vat);
|
||||
}
|
||||
|
||||
private _mapToTakeAwayAvailability({
|
||||
response,
|
||||
supplier,
|
||||
@@ -478,7 +482,7 @@ export class DomainAvailabilityService {
|
||||
inStock: inStock,
|
||||
supplierSSC: quantity <= inStock ? '999' : '',
|
||||
supplierSSCText: quantity <= inStock ? 'Filialentnahme' : '',
|
||||
price: stockInfo?.retailPrice ?? price, // #4553 Es soll nun immer der retailPrice aus der InStock Abfrage verwendet werden, egal ob "price" empty ist oder nicht
|
||||
price: this._priceIsEmpty(price) ? stockInfo?.retailPrice : price,
|
||||
supplier: { id: supplier?.id },
|
||||
// TODO: Change after API Update
|
||||
// LH: 2021-03-09 preis Property hat nun ein Fallback auf retailPrice
|
||||
|
||||
@@ -2,26 +2,23 @@ import { Injectable } from '@angular/core';
|
||||
import { Logger, LogLevel } from '@core/logger';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { UserStateService } from '@swagger/isa';
|
||||
import { debounceTime, switchMap, takeUntil } from 'rxjs/operators';
|
||||
import { debounceTime, switchMap } from 'rxjs/operators';
|
||||
import { RootState } from './root.state';
|
||||
import packageInfo from 'package';
|
||||
import { environment } from '../../environments/environment';
|
||||
import { Subject } from 'rxjs';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class RootStateService {
|
||||
static LOCAL_STORAGE_KEY = 'ISA_APP_INITIALSTATE';
|
||||
|
||||
private _cancelSave = new Subject<void>();
|
||||
|
||||
constructor(private readonly _userStateService: UserStateService, private _logger: Logger, private _store: Store) {
|
||||
if (!environment.production) {
|
||||
console.log('Die UserState kann in der Konsole mit der Funktion "clearUserState()" geleert werden.');
|
||||
}
|
||||
|
||||
window['clearUserState'] = () => {
|
||||
this.clear();
|
||||
};
|
||||
window['clearUserState'] = () => {
|
||||
this.clear();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async init() {
|
||||
@@ -34,8 +31,7 @@ export class RootStateService {
|
||||
this._store
|
||||
.select((state) => state)
|
||||
.pipe(
|
||||
takeUntil(this._cancelSave),
|
||||
debounceTime(1000),
|
||||
debounceTime(500),
|
||||
switchMap((state) => {
|
||||
const raw = JSON.stringify({ ...state, version: packageInfo.version });
|
||||
RootStateService.SaveToLocalStorageRaw(raw);
|
||||
@@ -68,17 +64,13 @@ export class RootStateService {
|
||||
return false;
|
||||
}
|
||||
|
||||
async clear() {
|
||||
try {
|
||||
this._cancelSave.next();
|
||||
await this._userStateService.UserStateResetUserState().toPromise();
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
RootStateService.RemoveFromLocalStorage();
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
window.location.reload();
|
||||
} catch (error) {
|
||||
this._logger.log(LogLevel.ERROR, error);
|
||||
}
|
||||
clear() {
|
||||
this._userStateService
|
||||
.UserStateResetUserState()
|
||||
.toPromise()
|
||||
.catch((error) => this._logger.log(LogLevel.ERROR, error));
|
||||
RootStateService.RemoveFromLocalStorage();
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
static SaveToLocalStorage(state: RootState) {
|
||||
|
||||
@@ -75,12 +75,6 @@
|
||||
"licence": {
|
||||
"scandit": "Ae7z0WDdRDFqG6oYuAXzesYGJpDLDqt+xWtQHOESiOjaSkB7IEIDJAk534U+cg1zGnk++4hOEK9hXEGR01NLTjh76w1fDL0U63OUjo50EHBXIUvzAVSur3pRY+1ER7SvSEWaT0hDOLYvYrTpdECtt1graN9yMvJzXD38VJKUfssT92p+YENV2Hul3eXIvaVjHqXE/yvupF+MlOMMUMhX0/Km/yTU9H9SjBdsXYihZmYWbt2JotO3Zs1ojXb0+3La10xb01S1q0XdDN6El3XMVilEtdmrP3WoGois8vpQBvOCEvduxCfILFAqjeWXTZvXSut9u+kQKpK8uHW4rVV6iVClpZfPYqKJqTh78AI9gpnfb/zO9GfQEDS3g7wI5WbQKqaNRzhTVowFRri4Ep9R5TRC1bnd00RC4zVaMkbu5kBOA7YoRjgUiYWHKJpi/VokZWyN6u1lsi5mTUbQkm1ZWfX5I/iUVYBgyHZYl+8kfFkwLPXGZNrF4xqubjKiCZRQj0oyNjHOBeHqvAekzhk7scX2g/NN+liRQv4ur413b+uXacSiiYIrLhtGgzrz1KRrtu19uB5odk3LoerDoiYXat7wEg9zUYT/+uBfO2X+uS7L5LW0PMI3hV+joQVpDk5SlA2868Nx0KWtPWmMf7xCuFIhDskfBsXZNRTblqxkk0RzzSqtjx9ihGr+/Tuzm8Pm0s4OQqV7b+++/Zn+Vo4rCqMTwutjOO7dqhah5hbOT1MqY/6VcjCXyDad3BXXr+WYU4GtYTe8Ytjkm/ZTG3fImoDbMchEcqnCw3oxG5e/gkdurE8g/mZlFOtzAN7KkqIsg6qLaC5COjfLPXsi/A=="
|
||||
},
|
||||
"gender": {
|
||||
"0": "Keine Anrede",
|
||||
"1": "Enby",
|
||||
"2": "Herr",
|
||||
"4": "Frau"
|
||||
},
|
||||
"@shared/icon": "/assets/icons.json"
|
||||
|
||||
}
|
||||
@@ -74,11 +74,5 @@
|
||||
"licence": {
|
||||
"scandit": "Ae7z0WDdRDFqG6oYuAXzesYGJpDLDqt+xWtQHOESiOjaSkB7IEIDJAk534U+cg1zGnk++4hOEK9hXEGR01NLTjh76w1fDL0U63OUjo50EHBXIUvzAVSur3pRY+1ER7SvSEWaT0hDOLYvYrTpdECtt1graN9yMvJzXD38VJKUfssT92p+YENV2Hul3eXIvaVjHqXE/yvupF+MlOMMUMhX0/Km/yTU9H9SjBdsXYihZmYWbt2JotO3Zs1ojXb0+3La10xb01S1q0XdDN6El3XMVilEtdmrP3WoGois8vpQBvOCEvduxCfILFAqjeWXTZvXSut9u+kQKpK8uHW4rVV6iVClpZfPYqKJqTh78AI9gpnfb/zO9GfQEDS3g7wI5WbQKqaNRzhTVowFRri4Ep9R5TRC1bnd00RC4zVaMkbu5kBOA7YoRjgUiYWHKJpi/VokZWyN6u1lsi5mTUbQkm1ZWfX5I/iUVYBgyHZYl+8kfFkwLPXGZNrF4xqubjKiCZRQj0oyNjHOBeHqvAekzhk7scX2g/NN+liRQv4ur413b+uXacSiiYIrLhtGgzrz1KRrtu19uB5odk3LoerDoiYXat7wEg9zUYT/+uBfO2X+uS7L5LW0PMI3hV+joQVpDk5SlA2868Nx0KWtPWmMf7xCuFIhDskfBsXZNRTblqxkk0RzzSqtjx9ihGr+/Tuzm8Pm0s4OQqV7b+++/Zn+Vo4rCqMTwutjOO7dqhah5hbOT1MqY/6VcjCXyDad3BXXr+WYU4GtYTe8Ytjkm/ZTG3fImoDbMchEcqnCw3oxG5e/gkdurE8g/mZlFOtzAN7KkqIsg6qLaC5COjfLPXsi/A=="
|
||||
},
|
||||
"gender": {
|
||||
"0": "Keine Anrede",
|
||||
"1": "Enby",
|
||||
"2": "Herr",
|
||||
"4": "Frau"
|
||||
},
|
||||
"@shared/icon": "/assets/icons.json"
|
||||
}
|
||||
@@ -76,11 +76,5 @@
|
||||
"licence": {
|
||||
"scandit": "Ae7z0WDdRDFqG6oYuAXzesYGJpDLDqt+xWtQHOESiOjaSkB7IEIDJAk534U+cg1zGnk++4hOEK9hXEGR01NLTjh76w1fDL0U63OUjo50EHBXIUvzAVSur3pRY+1ER7SvSEWaT0hDOLYvYrTpdECtt1graN9yMvJzXD38VJKUfssT92p+YENV2Hul3eXIvaVjHqXE/yvupF+MlOMMUMhX0/Km/yTU9H9SjBdsXYihZmYWbt2JotO3Zs1ojXb0+3La10xb01S1q0XdDN6El3XMVilEtdmrP3WoGois8vpQBvOCEvduxCfILFAqjeWXTZvXSut9u+kQKpK8uHW4rVV6iVClpZfPYqKJqTh78AI9gpnfb/zO9GfQEDS3g7wI5WbQKqaNRzhTVowFRri4Ep9R5TRC1bnd00RC4zVaMkbu5kBOA7YoRjgUiYWHKJpi/VokZWyN6u1lsi5mTUbQkm1ZWfX5I/iUVYBgyHZYl+8kfFkwLPXGZNrF4xqubjKiCZRQj0oyNjHOBeHqvAekzhk7scX2g/NN+liRQv4ur413b+uXacSiiYIrLhtGgzrz1KRrtu19uB5odk3LoerDoiYXat7wEg9zUYT/+uBfO2X+uS7L5LW0PMI3hV+joQVpDk5SlA2868Nx0KWtPWmMf7xCuFIhDskfBsXZNRTblqxkk0RzzSqtjx9ihGr+/Tuzm8Pm0s4OQqV7b+++/Zn+Vo4rCqMTwutjOO7dqhah5hbOT1MqY/6VcjCXyDad3BXXr+WYU4GtYTe8Ytjkm/ZTG3fImoDbMchEcqnCw3oxG5e/gkdurE8g/mZlFOtzAN7KkqIsg6qLaC5COjfLPXsi/A=="
|
||||
},
|
||||
"gender": {
|
||||
"0": "Keine Anrede",
|
||||
"1": "Enby",
|
||||
"2": "Herr",
|
||||
"4": "Frau"
|
||||
},
|
||||
"@shared/icon": "/assets/icons.json"
|
||||
}
|
||||
@@ -75,11 +75,5 @@
|
||||
"licence": {
|
||||
"scandit": "AVljxT/dG+TAIDDL2jTxm843juR2OtZ6lHLxRpYR7x9uYiSvY2IAHdRx8tjsf9KU7wK0F5cAeb/nLMHF6Vor9ps79wvuBQw6G3N0IW978b78ZUgPOFzxHUAMuD8dbkDZlX8r9y1cOd9sT3UNEwGrQ4siUt2oCkigyTxJAgYs1ijnjQid7q42hHk3tMXywrAYeu5MhF0TV1H77DRDMxPHD/xiR0zhFQRB2Dtnm1+e3LHKCyQjZ/zknEpQB6HS7UbCBoEDj4tohb83E6oqmQFWwt85/Jk9f49gxXakIcNODnQI5H63kSqpEmV9Al1a5L+WGZ6Bq1gwBbnD8FBXlVqxoooiFXW7jzzBa9LNmQiQ5J8yEkIsPeyOHec7F4ERvVONSMYwWyH39ZweSiRsZRM1UsFPhN96bCT5MEwkjPFn4gji6TPGEceJZvV3HwsiCT5Bgjla4bvDsZ2jYvAr9tSij8kIii9dHvsWlrimt+szHJLSz+8uNI6jAvXyr2f3oRxZD/F9osZHVWkgtAc+vVWqkxVJCqmpmoHOXI6TFSqSjYHddhZyU5r2lgQt0+NI6k/bV3iN7Le1RJCP/wuSDCTZjzsU1igB7UnIN2Y70CqCjIeVH9qlxaI1YAC9lwFv1FZvsiueYeJP1n39mmXCSELVtzxgIBEX5yaIHNbbGXd+e8JUgcO8vJ2JA2kJudaU+xfYR5SY//+J1kPsNSbnBnM25LL+LjeRB3QTfqV5sFq8ORWcIMITvkEaRfP3PVcOzb+hO4Ren4ezhJuyADulmvG8a9Kxxk6ymzBbE7a93SGVbxp7OQNEmvTn5+B9wJ7/l1mtvZL2TilrDZBQVMYWrGuUGpA="
|
||||
},
|
||||
"gender": {
|
||||
"0": "Keine Anrede",
|
||||
"1": "Enby",
|
||||
"2": "Herr",
|
||||
"4": "Frau"
|
||||
},
|
||||
"@shared/icon": "/assets/icons.json"
|
||||
}
|
||||
@@ -75,11 +75,5 @@
|
||||
"licence": {
|
||||
"scandit": "AVljxT/dG+TAIDDL2jTxm843juR2OtZ6lHLxRpYR7x9uYiSvY2IAHdRx8tjsf9KU7wK0F5cAeb/nLMHF6Vor9ps79wvuBQw6G3N0IW978b78ZUgPOFzxHUAMuD8dbkDZlX8r9y1cOd9sT3UNEwGrQ4siUt2oCkigyTxJAgYs1ijnjQid7q42hHk3tMXywrAYeu5MhF0TV1H77DRDMxPHD/xiR0zhFQRB2Dtnm1+e3LHKCyQjZ/zknEpQB6HS7UbCBoEDj4tohb83E6oqmQFWwt85/Jk9f49gxXakIcNODnQI5H63kSqpEmV9Al1a5L+WGZ6Bq1gwBbnD8FBXlVqxoooiFXW7jzzBa9LNmQiQ5J8yEkIsPeyOHec7F4ERvVONSMYwWyH39ZweSiRsZRM1UsFPhN96bCT5MEwkjPFn4gji6TPGEceJZvV3HwsiCT5Bgjla4bvDsZ2jYvAr9tSij8kIii9dHvsWlrimt+szHJLSz+8uNI6jAvXyr2f3oRxZD/F9osZHVWkgtAc+vVWqkxVJCqmpmoHOXI6TFSqSjYHddhZyU5r2lgQt0+NI6k/bV3iN7Le1RJCP/wuSDCTZjzsU1igB7UnIN2Y70CqCjIeVH9qlxaI1YAC9lwFv1FZvsiueYeJP1n39mmXCSELVtzxgIBEX5yaIHNbbGXd+e8JUgcO8vJ2JA2kJudaU+xfYR5SY//+J1kPsNSbnBnM25LL+LjeRB3QTfqV5sFq8ORWcIMITvkEaRfP3PVcOzb+hO4Ren4ezhJuyADulmvG8a9Kxxk6ymzBbE7a93SGVbxp7OQNEmvTn5+B9wJ7/l1mtvZL2TilrDZBQVMYWrGuUGpA="
|
||||
},
|
||||
"gender": {
|
||||
"0": "Keine Anrede",
|
||||
"1": "Enby",
|
||||
"2": "Herr",
|
||||
"4": "Frau"
|
||||
},
|
||||
"@shared/icon": "/assets/icons.json"
|
||||
}
|
||||
@@ -76,11 +76,5 @@
|
||||
"licence": {
|
||||
"scandit": "Ae7z0WDdRDFqG6oYuAXzesYGJpDLDqt+xWtQHOESiOjaSkB7IEIDJAk534U+cg1zGnk++4hOEK9hXEGR01NLTjh76w1fDL0U63OUjo50EHBXIUvzAVSur3pRY+1ER7SvSEWaT0hDOLYvYrTpdECtt1graN9yMvJzXD38VJKUfssT92p+YENV2Hul3eXIvaVjHqXE/yvupF+MlOMMUMhX0/Km/yTU9H9SjBdsXYihZmYWbt2JotO3Zs1ojXb0+3La10xb01S1q0XdDN6El3XMVilEtdmrP3WoGois8vpQBvOCEvduxCfILFAqjeWXTZvXSut9u+kQKpK8uHW4rVV6iVClpZfPYqKJqTh78AI9gpnfb/zO9GfQEDS3g7wI5WbQKqaNRzhTVowFRri4Ep9R5TRC1bnd00RC4zVaMkbu5kBOA7YoRjgUiYWHKJpi/VokZWyN6u1lsi5mTUbQkm1ZWfX5I/iUVYBgyHZYl+8kfFkwLPXGZNrF4xqubjKiCZRQj0oyNjHOBeHqvAekzhk7scX2g/NN+liRQv4ur413b+uXacSiiYIrLhtGgzrz1KRrtu19uB5odk3LoerDoiYXat7wEg9zUYT/+uBfO2X+uS7L5LW0PMI3hV+joQVpDk5SlA2868Nx0KWtPWmMf7xCuFIhDskfBsXZNRTblqxkk0RzzSqtjx9ihGr+/Tuzm8Pm0s4OQqV7b+++/Zn+Vo4rCqMTwutjOO7dqhah5hbOT1MqY/6VcjCXyDad3BXXr+WYU4GtYTe8Ytjkm/ZTG3fImoDbMchEcqnCw3oxG5e/gkdurE8g/mZlFOtzAN7KkqIsg6qLaC5COjfLPXsi/A=="
|
||||
},
|
||||
"gender": {
|
||||
"0": "Keine Anrede",
|
||||
"1": "Enby",
|
||||
"2": "Herr",
|
||||
"4": "Frau"
|
||||
},
|
||||
"@shared/icon": "/assets/icons.json"
|
||||
}
|
||||
@@ -10,7 +10,7 @@
|
||||
<div class="page-price-update-item__item-card p-5 h-[212px] bg-white">
|
||||
<div class="page-price-update-item__item-thumbnail text-center mr-4 w-[47px] h-[73px]">
|
||||
<img
|
||||
class="page-price-update-item__item-image w-[47px] max-h-[73px]"
|
||||
class="page-price-update-item__item-image w-[47px] h-[73px]"
|
||||
loading="lazy"
|
||||
*ngIf="item?.product?.ean | productImage; let productImage"
|
||||
[src]="productImage"
|
||||
|
||||
@@ -1,386 +1,3 @@
|
||||
<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">
|
||||
@@ -768,4 +385,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,13 +1,9 @@
|
||||
:host {
|
||||
@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;
|
||||
@apply box-border block h-split-screen-tablet desktop-small:h-split-screen-desktop;
|
||||
}
|
||||
|
||||
.page-article-details__container {
|
||||
@apply overflow-scroll bg-white rounded shadow-card flex flex-col;
|
||||
@apply h-full w-full overflow-y-scroll overflow-hidden bg-white rounded shadow-card flex flex-col;
|
||||
}
|
||||
|
||||
.page-article-details__product-details {
|
||||
|
||||
@@ -338,7 +338,7 @@ export class ArticleDetailsComponent implements OnInit, OnDestroy {
|
||||
const item = await this.store.item$.pipe(first()).toPromise();
|
||||
const modal = this.uiModal.open<BranchDTO>({
|
||||
content: ModalAvailabilitiesComponent,
|
||||
title: 'Bestände in anderen Filialen',
|
||||
title: 'Weitere Verfügbarkeiten',
|
||||
data: {
|
||||
item,
|
||||
},
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<p>Neben dem Titel "{{ item.product?.name }}" gibt es noch andere Artikel, die Sie interessieren könnten.</p>
|
||||
|
||||
<div class="articles">
|
||||
<span class="label mb-2">
|
||||
<span class="label">
|
||||
<ui-icon icon="recommendation" size="20px"></ui-icon>
|
||||
Artikel
|
||||
</span>
|
||||
@@ -28,10 +28,9 @@
|
||||
(click)="close.emit()"
|
||||
>
|
||||
<img [src]="recommendation.product?.ean | productImage: 195:315:true" alt="product-image" />
|
||||
<div class="flex flex-col">
|
||||
<span class="format">{{ recommendation.product?.formatDetail }}</span>
|
||||
<span class="price">{{ recommendation.catalogAvailability?.price?.value?.value | currency: ' ' }} EUR</span>
|
||||
</div>
|
||||
|
||||
<span class="format">{{ recommendation.product?.formatDetail }}</span>
|
||||
<span class="price">{{ recommendation.catalogAvailability?.price?.value?.value | currency: ' ' }} EUR</span>
|
||||
</a>
|
||||
</ui-slider>
|
||||
</ng-container>
|
||||
|
||||
@@ -29,12 +29,12 @@ p {
|
||||
}
|
||||
|
||||
.article {
|
||||
@apply flex flex-col mr-7 mt-4 no-underline text-black h-full min-w-[11rem] justify-between;
|
||||
@apply flex flex-col mr-7 mt-4 no-underline text-black;
|
||||
|
||||
img {
|
||||
@apply rounded-xl;
|
||||
max-height: 19.6875rem;
|
||||
max-width: 11rem;
|
||||
height: 315px;
|
||||
max-width: 195px;
|
||||
box-shadow: 0 0 15px #949393;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,26 @@ 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> = {}) {
|
||||
|
||||
@@ -6,10 +6,12 @@ import { ArticleSearchComponent } from './article-search.component';
|
||||
import { SearchResultsModule } from './search-results/search-results.module';
|
||||
import { SearchMainModule } from './search-main/search-main.module';
|
||||
import { SearchFilterModule } from './search-filter/search-filter.module';
|
||||
import { ArticleSearchService } from './article-search.store';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, RouterModule, UiIconModule, SearchResultsModule, SearchMainModule, SearchFilterModule],
|
||||
exports: [ArticleSearchComponent],
|
||||
declarations: [ArticleSearchComponent],
|
||||
providers: [ArticleSearchService],
|
||||
})
|
||||
export class ArticleSearchModule {}
|
||||
|
||||
@@ -9,7 +9,6 @@ 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',
|
||||
@@ -53,8 +52,7 @@ export class ArticleSearchMainComponent implements OnInit, OnDestroy {
|
||||
private route: ActivatedRoute,
|
||||
private application: ApplicationService,
|
||||
private breadcrumb: BreadcrumbService,
|
||||
private _environment: EnvironmentService,
|
||||
private _navigationService: ProductCatalogNavigationService
|
||||
private _environment: EnvironmentService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
@@ -82,14 +80,6 @@ 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);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
@@ -173,27 +163,6 @@ 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();
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
>
|
||||
<div class="page-search-result-item__item-thumbnail text-center mr-4 w-[3.125rem] h-[4.9375rem]">
|
||||
<img
|
||||
class="page-search-result-item__item-image w-[3.125rem] max-h-[4.9375rem]"
|
||||
class="page-search-result-item__item-image w-[3.125rem] h-[4.9375rem]"
|
||||
loading="lazy"
|
||||
*ngIf="item?.imageId | thumbnailUrl; let thumbnailUrl"
|
||||
[src]="thumbnailUrl"
|
||||
|
||||
@@ -40,77 +40,80 @@
|
||||
|
||||
<div class="page-search-results__order-by mb-[0.125rem]" [class.page-search-results__order-by-primary]="primaryOutletActive$ | async">
|
||||
<shared-order-by-filter
|
||||
*ngIf="filter$ | async; let filter"
|
||||
[orderBy]="filter?.orderBy"
|
||||
(selectedOrderByChange)="search({ filter, clear: true, orderBy: true }); updateBreadcrumbs()"
|
||||
[orderBy]="(filter$ | async)?.orderBy"
|
||||
(selectedOrderByChange)="search({ clear: true, orderBy: true }); updateBreadcrumbs()"
|
||||
>
|
||||
</shared-order-by-filter>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="primaryOutletActive$ | async; else sideOutlet">
|
||||
<cdk-virtual-scroll-viewport class="product-list" [itemSize]="103 * (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
|
||||
[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 class="h-full relative">
|
||||
<cdk-virtual-scroll-viewport class="product-list h-full" itemSize="98" (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
|
||||
[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>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-template #sideOutlet>
|
||||
<cdk-virtual-scroll-viewport class="product-list" [itemSize]="191 * (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 class="h-full relative">
|
||||
<cdk-virtual-scroll-viewport class="product-list h-full" itemSize="181" (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>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
:host {
|
||||
@apply box-border grid h-split-screen-tablet desktop-small:h-split-screen-desktop relative;
|
||||
@apply box-border grid h-split-screen-tablet desktop-small:h-split-screen-desktop;
|
||||
grid-template-rows: auto auto 1fr;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
}
|
||||
|
||||
.page-search-results__result-item {
|
||||
@apply mb-[0.625rem];
|
||||
@apply mb-px-10;
|
||||
}
|
||||
|
||||
.page-search-results__result-item-primary {
|
||||
|
||||
@@ -9,9 +9,8 @@ import {
|
||||
QueryList,
|
||||
TrackByFunction,
|
||||
AfterViewInit,
|
||||
inject,
|
||||
} from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { EnvironmentService } from '@core/environment';
|
||||
@@ -30,7 +29,6 @@ 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',
|
||||
@@ -101,10 +99,6 @@ export class ArticleSearchResultsComponent implements OnInit, OnDestroy, AfterVi
|
||||
|
||||
private readonly SCROLL_INDEX_TOKEN = 'CATALOG_RESULTS_LIST_SCROLL_INDEX';
|
||||
|
||||
shellService = inject(ShellService);
|
||||
|
||||
scale$ = this.shellService.scale$;
|
||||
|
||||
constructor(
|
||||
public searchService: ArticleSearchService,
|
||||
public route: ActivatedRoute,
|
||||
@@ -115,8 +109,7 @@ export class ArticleSearchResultsComponent implements OnInit, OnDestroy, AfterVi
|
||||
private _checkoutService: DomainCheckoutService,
|
||||
private _environment: EnvironmentService,
|
||||
private _navigationService: ProductCatalogNavigationService,
|
||||
private _availability: DomainAvailabilityService,
|
||||
private _router: Router
|
||||
private _availability: DomainAvailabilityService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
@@ -199,9 +192,9 @@ export class ArticleSearchResultsComponent implements OnInit, OnDestroy, AfterVi
|
||||
.subscribe(async ([searchCompleted, processId]) => {
|
||||
const params = searchCompleted.state.filter.getQueryParams();
|
||||
if (searchCompleted.state.searchState === '') {
|
||||
// Ticket 4524 Korrekte Navigation bei orderBy mit aktuellen queryParams
|
||||
// Keine Navigation bei OrderBy
|
||||
if (searchCompleted?.orderBy) {
|
||||
return await this._router.navigate([], { queryParams: params });
|
||||
return;
|
||||
}
|
||||
|
||||
// Navigation auf Details bzw. Results | Details wenn hits 1
|
||||
@@ -290,9 +283,7 @@ export class ArticleSearchResultsComponent implements OnInit, OnDestroy, AfterVi
|
||||
}
|
||||
|
||||
scrolledIndexChange(index: number) {
|
||||
const completeListFetched = this.searchService.items.length === this.searchService.hits;
|
||||
|
||||
if (index && !completeListFetched && this.searchService.items.length <= this.scrollContainer?.getRenderedRange()?.end) {
|
||||
if (index && this.searchService.items.length <= this.scrollContainer?.getRenderedRange()?.end) {
|
||||
this.search({ clear: false });
|
||||
}
|
||||
|
||||
|
||||
@@ -11,14 +11,12 @@ import { combineLatest, fromEvent, Observable, Subject } from 'rxjs';
|
||||
import { first, map, switchMap, takeUntil, withLatestFrom } from 'rxjs/operators';
|
||||
import { ActionsSubject } from '@ngrx/store';
|
||||
import { DomainAvailabilityService } from '@domain/availability';
|
||||
import { provideComponentStore } from '@ngrx/component-store';
|
||||
import { ArticleSearchService } from './article-search/article-search.store';
|
||||
|
||||
@Component({
|
||||
selector: 'page-catalog',
|
||||
templateUrl: 'page-catalog.component.html',
|
||||
styleUrls: ['page-catalog.component.scss'],
|
||||
providers: [provideComponentStore(ArticleSearchService)],
|
||||
providers: [],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class PageCatalogComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { ShoppingCartItemDTO } from '@swagger/checkout';
|
||||
|
||||
export interface CheckoutDummyData extends ShoppingCartItemDTO {
|
||||
changeDataFromCart?: boolean;
|
||||
}
|
||||
export interface CheckoutDummyData extends ShoppingCartItemDTO {}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
|
||||
import { Router } from '@angular/router';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { ItemDTO } from '@swagger/cat';
|
||||
import { DateAdapter } from '@ui/common';
|
||||
@@ -8,8 +9,7 @@ import { Subject } from 'rxjs';
|
||||
import { first, shareReplay, takeUntil } from 'rxjs/operators';
|
||||
import { CheckoutDummyData } from './checkout-dummy-data';
|
||||
import { CheckoutDummyStore } from './checkout-dummy.store';
|
||||
import { CheckoutNavigationService, CustomerSearchNavigation } from '@shared/services';
|
||||
import { Router } from '@angular/router';
|
||||
import { CheckoutNavigationService } from '@shared/services';
|
||||
|
||||
@Component({
|
||||
selector: 'page-checkout-dummy',
|
||||
@@ -38,15 +38,14 @@ export class CheckoutDummyComponent implements OnInit, OnDestroy {
|
||||
_onDestroy$ = new Subject<void>();
|
||||
|
||||
constructor(
|
||||
private _router: Router,
|
||||
private _fb: UntypedFormBuilder,
|
||||
private _dateAdapter: DateAdapter,
|
||||
private _modal: UiModalService,
|
||||
private _store: CheckoutDummyStore,
|
||||
private _ref: UiModalRef<any, CheckoutDummyData>,
|
||||
private readonly _applicationService: ApplicationService,
|
||||
private readonly _checkoutNavigationService: CheckoutNavigationService,
|
||||
private readonly _customerNavigationService: CustomerSearchNavigation,
|
||||
private _router: Router
|
||||
private readonly _checkoutNavigationService: CheckoutNavigationService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
@@ -59,7 +58,7 @@ export class CheckoutDummyComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
});
|
||||
|
||||
if (this.hasShoppingCartItemToUpdate()) {
|
||||
if (!!this._ref?.data && Object.keys(this._ref?.data).length !== 0) {
|
||||
const data = this._ref?.data;
|
||||
this._store.patchState({ shoppingCartItem: data });
|
||||
this.populateFormFromModalData(data);
|
||||
@@ -150,14 +149,6 @@ export class CheckoutDummyComponent implements OnInit, OnDestroy {
|
||||
this.control.markAsUntouched();
|
||||
}
|
||||
|
||||
hasShoppingCartItemToUpdate(): boolean {
|
||||
const hasShoppingCartItem = !!this._ref.data?.id;
|
||||
if (!!this._ref?.data && hasShoppingCartItem) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async nextItem() {
|
||||
if (this.control.invalid || this.control.disabled) {
|
||||
this.control.enable();
|
||||
@@ -167,7 +158,7 @@ export class CheckoutDummyComponent implements OnInit, OnDestroy {
|
||||
|
||||
try {
|
||||
const branch = await this._store.currentBranch$.pipe(first()).toPromise();
|
||||
if (this.hasShoppingCartItemToUpdate()) {
|
||||
if (!!this._ref?.data && Object.keys(this._ref?.data).length !== 0) {
|
||||
await this._store.createAddToCartItem(this.control, branch, true);
|
||||
this._store.updateCart(() => {});
|
||||
} else {
|
||||
@@ -196,17 +187,16 @@ export class CheckoutDummyComponent implements OnInit, OnDestroy {
|
||||
|
||||
try {
|
||||
const branch = await this._store.currentBranch$.pipe(first()).toPromise();
|
||||
if (this.hasShoppingCartItemToUpdate()) {
|
||||
if (!!this._ref?.data && Object.keys(this._ref?.data).length !== 0) {
|
||||
await this._store.createAddToCartItem(this.control, branch, true);
|
||||
this._store.updateCart(async () => {
|
||||
// Set filter for navigation to customer search if customer is not set
|
||||
const customer = await this._store.customer$.pipe(first()).toPromise();
|
||||
const customerFilter = await this._store.customerFilter$.pipe(first()).toPromise();
|
||||
let filter: { [key: string]: string };
|
||||
if (!customer && !this._ref?.data?.changeDataFromCart) {
|
||||
if (!customer) {
|
||||
filter = customerFilter;
|
||||
const path = this._customerNavigationService.defaultRoute({ processId: this._applicationService.activatedProcessId }).path;
|
||||
await this._router.navigate(path, {
|
||||
this._router.navigate(['/kunde', this._applicationService.activatedProcessId, 'customer', 'search'], {
|
||||
queryParams: { customertype: filter.customertype },
|
||||
});
|
||||
} else {
|
||||
@@ -221,10 +211,9 @@ export class CheckoutDummyComponent implements OnInit, OnDestroy {
|
||||
const customer = await this._store.customer$.pipe(first()).toPromise();
|
||||
const customerFilter = await this._store.customerFilter$.pipe(first()).toPromise();
|
||||
let filter: { [key: string]: string };
|
||||
if (!customer && !this._ref?.data?.changeDataFromCart) {
|
||||
if (!customer) {
|
||||
filter = customerFilter;
|
||||
const path = this._customerNavigationService.defaultRoute({ processId: this._applicationService.activatedProcessId }).path;
|
||||
await this._router.navigate(path, {
|
||||
this._router.navigate(['/kunde', this._applicationService.activatedProcessId, 'customer', 'search'], {
|
||||
queryParams: { customertype: filter.customertype },
|
||||
});
|
||||
} else {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
<div class="btn-wrapper">
|
||||
<a class="cta-primary" [routerLink]="productSearchBasePath">Artikel suchen</a>
|
||||
<button class="cta-secondary" (click)="openDummyModal({})">Neuanlage</button>
|
||||
<button class="cta-secondary" (click)="openDummyModal()">Neuanlage</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -54,7 +54,7 @@
|
||||
<button
|
||||
*ngIf="group.orderType === 'Dummy'"
|
||||
class="text-brand border-none font-bold text-p1 outline-none pl-4"
|
||||
(click)="openDummyModal({ changeDataFromCart: true })"
|
||||
(click)="openDummyModal()"
|
||||
>
|
||||
Hinzufügen
|
||||
</button>
|
||||
@@ -81,11 +81,8 @@
|
||||
*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 || 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)"
|
||||
>
|
||||
<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]">
|
||||
<span class="branch-name">{{ targetBranch?.name }} | {{ targetBranch | branchAddress }}</span>
|
||||
</div>
|
||||
<hr />
|
||||
|
||||
@@ -105,10 +105,6 @@ 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, BranchDTO, DestinationDTO, ShoppingCartItemDTO } from '@swagger/checkout';
|
||||
import { AvailabilityDTO, 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,10 +254,6 @@ 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);
|
||||
|
||||
@@ -292,15 +288,15 @@ export class CheckoutReviewComponent implements OnInit, OnDestroy, AfterViewInit
|
||||
this._store.notificationsControl = undefined;
|
||||
}
|
||||
|
||||
openDummyModal({ data, changeDataFromCart = false }: { data?: CheckoutDummyData; changeDataFromCart?: boolean }) {
|
||||
openDummyModal(data?: CheckoutDummyData) {
|
||||
this.uiModal.open({
|
||||
content: CheckoutDummyComponent,
|
||||
data: { ...data, changeDataFromCart },
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
changeDummyItem({ shoppingCartItem }: { shoppingCartItem: ShoppingCartItemDTO }) {
|
||||
this.openDummyModal({ data: shoppingCartItem, changeDataFromCart: true });
|
||||
this.openDummyModal(shoppingCartItem);
|
||||
}
|
||||
|
||||
async changeItem({ shoppingCartItem }: { shoppingCartItem: ShoppingCartItemDTO }) {
|
||||
|
||||
@@ -5,10 +5,14 @@
|
||||
<ng-container *ngIf="buyer$ | async; let buyer">
|
||||
<div *ngIf="!(showAddresses$ | async)" class="flex flex-row items-start justify-between p-5">
|
||||
<div class="flex flex-row flex-wrap pr-4">
|
||||
<ng-container *ngIf="getNameFromBuyer(buyer); let name">
|
||||
<div class="mr-3">{{ name.label }}</div>
|
||||
<div class="font-bold">{{ name.value }}</div>
|
||||
<ng-container *ngIf="!!buyer?.lastName && !!buyer?.firstName; else organisation">
|
||||
<div class="mr-3">Nachname, Vorname</div>
|
||||
<div class="font-bold">{{ buyer?.lastName }}, {{ buyer?.firstName }}</div>
|
||||
</ng-container>
|
||||
<ng-template #organisation>
|
||||
<div class="mr-3">Firmenname</div>
|
||||
<div class="font-bold">{{ buyer?.organisation?.name }}</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
|
||||
<button (click)="changeAddress()" class="text-p1 font-bold text-[#F70400]">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ChangeDetectionStrategy, Component, OnInit, inject } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
||||
import { emailNotificationValidator, mobileNotificationValidator } from '@shared/components/notification-channel-control';
|
||||
import { UntypedFormBuilder, UntypedFormControl, UntypedFormGroup } from '@angular/forms';
|
||||
import { combineLatest } from 'rxjs';
|
||||
@@ -7,8 +7,7 @@ import { first, map, switchMap, takeUntil, withLatestFrom } from 'rxjs/operators
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { DomainCheckoutService } from '@domain/checkout';
|
||||
import { Router } from '@angular/router';
|
||||
import { BuyerDTO, NotificationChannel } from '@swagger/checkout';
|
||||
import { CustomerSearchNavigation } from '@shared/services';
|
||||
import { NotificationChannel } from '@swagger/checkout';
|
||||
|
||||
@Component({
|
||||
selector: 'page-checkout-review-details',
|
||||
@@ -17,8 +16,6 @@ import { CustomerSearchNavigation } from '@shared/services';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class CheckoutReviewDetailsComponent implements OnInit {
|
||||
customerNavigation = inject(CustomerSearchNavigation);
|
||||
|
||||
control: UntypedFormGroup;
|
||||
|
||||
customerFeatures$ = this._store.customerFeatures$;
|
||||
@@ -26,6 +23,20 @@ export class CheckoutReviewDetailsComponent implements OnInit {
|
||||
payer$ = this._store.payer$;
|
||||
buyer$ = this._store.buyer$;
|
||||
|
||||
showAddresses$ = this._store.shoppingCartItems$.pipe(
|
||||
takeUntil(this._store.orderCompleted),
|
||||
withLatestFrom(this.customerFeatures$),
|
||||
map(
|
||||
([items, customerFeatures]) =>
|
||||
items.some(
|
||||
(item) =>
|
||||
item.features?.orderType === 'Versand' ||
|
||||
item.features?.orderType === 'B2B-Versand' ||
|
||||
item.features?.orderType === 'DIG-Versand'
|
||||
) || !!customerFeatures?.b2b
|
||||
)
|
||||
);
|
||||
|
||||
showNotificationChannels$ = combineLatest([this._store.shoppingCartItems$, this.payer$, this.buyer$]).pipe(
|
||||
takeUntil(this._store.orderCompleted),
|
||||
map(
|
||||
@@ -54,22 +65,6 @@ export class CheckoutReviewDetailsComponent implements OnInit {
|
||||
switchMap((processId) => this._domainCheckoutService.getShippingAddress({ processId }))
|
||||
);
|
||||
|
||||
showAddresses$ = this._store.shoppingCartItems$.pipe(
|
||||
takeUntil(this._store.orderCompleted),
|
||||
withLatestFrom(this.customerFeatures$, this.payer$, this.shippingAddress$),
|
||||
map(([items, customerFeatures, payer, shippingAddress]) => {
|
||||
const hasShippingOrBillingAddresses = !!payer?.address || !!shippingAddress;
|
||||
const hasShippingFeature = items.some(
|
||||
(item) =>
|
||||
item.features?.orderType === 'Versand' || item.features?.orderType === 'B2B-Versand' || item.features?.orderType === 'DIG-Versand'
|
||||
);
|
||||
|
||||
const isB2bCustomer = !!customerFeatures?.b2b;
|
||||
|
||||
return hasShippingOrBillingAddresses && (hasShippingFeature || isB2bCustomer);
|
||||
})
|
||||
);
|
||||
|
||||
notificationChannelLoading$ = this._store.notificationChannelLoading$;
|
||||
|
||||
constructor(
|
||||
@@ -123,20 +118,6 @@ export class CheckoutReviewDetailsComponent implements OnInit {
|
||||
this._store.onNotificationChange(notificationChannels);
|
||||
}
|
||||
|
||||
getNameFromBuyer(buyer: BuyerDTO): { value: string; label: string } {
|
||||
if (buyer?.lastName && buyer?.firstName) {
|
||||
return { value: `${buyer?.lastName}, ${buyer?.firstName}`, label: 'Nachname, Vorname' };
|
||||
} else if (buyer?.lastName) {
|
||||
return { value: buyer?.lastName, label: 'Nachname, Vorname' };
|
||||
} else if (buyer?.firstName) {
|
||||
return { value: buyer?.firstName, label: 'Nachname, Vorname' };
|
||||
} else if (buyer?.organisation?.name) {
|
||||
return { value: buyer?.organisation?.name, label: 'Firmenname' };
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
async changeAddress() {
|
||||
const processId = this._application.activatedProcessId;
|
||||
const customer = await this._domainCheckoutService.getBuyer({ processId }).pipe(first()).toPromise();
|
||||
@@ -145,8 +126,7 @@ export class CheckoutReviewDetailsComponent implements OnInit {
|
||||
return;
|
||||
}
|
||||
const customerId = customer.source;
|
||||
await this.customerNavigation.navigateToDetails({ processId, customerId, customer: { customerNumber: customer.buyerNumber } });
|
||||
// this._router.navigate(['/kunde', this._application.activatedProcessId, 'customer', 'search', `${customerId}`]);
|
||||
this._router.navigate(['/kunde', this._application.activatedProcessId, 'customer', 'search', `${customerId}`]);
|
||||
}
|
||||
|
||||
async navigateToCustomerSearch(processId: number) {
|
||||
|
||||
@@ -17,7 +17,7 @@ button {
|
||||
grid-area: item-thumbnail;
|
||||
@apply mr-8 w-[3.75rem] h-[5.9375rem];
|
||||
img {
|
||||
@apply w-[3.75rem] max-h-[5.9375rem] rounded shadow-cta;
|
||||
@apply w-[3.75rem] h-[5.9375rem] rounded shadow-cta;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -55,8 +55,6 @@
|
||||
<span class="w-32">Vorgangs-ID</span>
|
||||
<ng-container *ngIf="customer$ | async; let customer">
|
||||
<a
|
||||
data-which="Vorgangs-ID"
|
||||
data-what="link"
|
||||
*ngIf="customer$ | async; let customer"
|
||||
class="font-bold text-[#0556B4] no-underline"
|
||||
[routerLink]="['/kunde', processId, 'customer', 'search', customer?.id, 'orders', displayOrder.id]"
|
||||
@@ -104,7 +102,7 @@
|
||||
>
|
||||
<div class="page-checkout-summary__items-thumbnail flex flex-row">
|
||||
<a [routerLink]="getProductSearchDetailsPath(order?.product?.ean)" [queryParams]="getProductSearchDetailsQueryParams(order)">
|
||||
<img class="w-[3.125rem] max-h-20 mr-2" [src]="order.product?.ean | productImage: 195:315:true" />
|
||||
<img class="w-[3.125rem] h-20 mr-2" [src]="order.product?.ean | productImage: 195:315:true" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import { DateAdapter } from '@ui/common';
|
||||
import { CheckoutNavigationService, PickUpShelfOutNavigationService, ProductCatalogNavigationService } from '@shared/services';
|
||||
import { EnvironmentService } from '@core/environment';
|
||||
import { SendOrderConfirmationModalService } from '@shared/modals/send-order-confirmation-modal';
|
||||
import { coerceBooleanProperty } from '@angular/cdk/coercion';
|
||||
import { ToasterService } from '@shared/shell';
|
||||
|
||||
@Component({
|
||||
@@ -160,10 +161,6 @@ export class CheckoutSummaryComponent implements OnInit, OnDestroy {
|
||||
return this._environmentService.matchDesktopLarge$;
|
||||
}
|
||||
|
||||
get isTablet() {
|
||||
return this._environmentService.matchTablet();
|
||||
}
|
||||
|
||||
expanded: boolean[] = [];
|
||||
|
||||
constructor(
|
||||
@@ -328,7 +325,6 @@ export class CheckoutSummaryComponent implements OnInit, OnDestroy {
|
||||
content: PrintModalComponent,
|
||||
data: {
|
||||
printerType: 'Label',
|
||||
printImmediately: !this.isTablet,
|
||||
print: async (printer) => {
|
||||
try {
|
||||
const result = await this.domainPrinterService.printOrder({ orderIds: orders.map((o) => o.id), printer }).toPromise();
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import { Directive, HostListener, Input, Output, EventEmitter, OnInit } from '@angular/core';
|
||||
import { BranchDTO } from '@swagger/checkout';
|
||||
import { PurchasingOptionsModalStore } from '../../modals/purchasing-options-modal/purchasing-options-modal.store';
|
||||
|
||||
/* tslint:disable: directive-selector */
|
||||
@Directive({ selector: '[keyNavigation]' })
|
||||
export class KeyNavigationDirective implements OnInit {
|
||||
@Input() element: any;
|
||||
@Input('keyNavigation') data: BranchDTO[];
|
||||
@Output() closeDropdown = new EventEmitter<void>();
|
||||
@Output() preselectBranch = new EventEmitter<BranchDTO>();
|
||||
selectedData: BranchDTO;
|
||||
position = 0;
|
||||
posMarker = 0;
|
||||
|
||||
@HostListener('window:keyup', ['$event'])
|
||||
keyEvent(event: KeyboardEvent) {
|
||||
if (event.key === 'ArrowUp') {
|
||||
if (this.position > 0) {
|
||||
this.position--;
|
||||
}
|
||||
|
||||
if (this.position <= this.posMarker - 4) {
|
||||
this.element.scrollTop -= 44;
|
||||
}
|
||||
|
||||
this.selectedData = this.data[this.position];
|
||||
this.preselectBranch.emit(this.selectedData);
|
||||
}
|
||||
|
||||
if (event.key === 'ArrowDown') {
|
||||
if (this.position < this.data.length - 1) {
|
||||
this.position++;
|
||||
}
|
||||
|
||||
if (this.position >= 4) {
|
||||
this.posMarker = this.position;
|
||||
this.element.scrollTop += 44;
|
||||
}
|
||||
|
||||
this.selectedData = this.data[this.position];
|
||||
this.preselectBranch.emit(this.selectedData);
|
||||
}
|
||||
|
||||
if (event.key === 'Enter') {
|
||||
this.purchasingOptionsModalStore.setBranch(this.selectedData);
|
||||
this.position = 0;
|
||||
this.closeDropdown.emit();
|
||||
}
|
||||
}
|
||||
|
||||
constructor(private purchasingOptionsModalStore: PurchasingOptionsModalStore) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.selectedData = this.data[this.position];
|
||||
this.preselectBranch.emit(this.selectedData);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
import { KeyNavigationDirective } from './key-navigation.directive';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule],
|
||||
exports: [KeyNavigationDirective],
|
||||
declarations: [KeyNavigationDirective],
|
||||
providers: [],
|
||||
})
|
||||
export class KeyNavigationModule {}
|
||||
@@ -22,7 +22,7 @@ button {
|
||||
|
||||
.page-customer-order-details-item__thumbnail {
|
||||
img {
|
||||
@apply rounded shadow-cta w-[3.625rem] max-h-[5.9375rem];
|
||||
@apply rounded shadow-cta w-[3.625rem] h-[5.9375rem];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import { CustomerOrderSearchComponent } from './customer-order-search.component'
|
||||
import { CustomerOrderSearchFilterComponent, OrderBranchIdInputComponent } from './customer-order-search-filter';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { UiSpinnerModule } from '@ui/spinner';
|
||||
import { CustomerOrderSearchStore } from './customer-order-search.store';
|
||||
import { IconComponent, IconModule } from '@shared/components/icon';
|
||||
import { FilterModule } from '@shared/components/filter';
|
||||
import { CustomerOrderSearchMainModule } from './search-main';
|
||||
@@ -21,6 +22,7 @@ import { CustomerOrderSearchMainModule } from './search-main';
|
||||
CustomerOrderSearchMainModule,
|
||||
],
|
||||
exports: [CustomerOrderSearchComponent],
|
||||
providers: [CustomerOrderSearchStore],
|
||||
declarations: [CustomerOrderSearchComponent, CustomerOrderSearchFilterComponent],
|
||||
})
|
||||
export class CustomerOrderSearchModule {}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Filter } from '@shared/components/filter';
|
||||
import { BranchDTO, ListResponseArgsOfOrderItemListItemDTO, OrderItemListItemDTO, QuerySettingsDTO } from '@swagger/oms';
|
||||
import { isResponseArgs } from '@utils/object';
|
||||
import { Observable, Subject } from 'rxjs';
|
||||
import { switchMap, takeUntil, tap, withLatestFrom } from 'rxjs/operators';
|
||||
import { switchMap, tap, withLatestFrom } from 'rxjs/operators';
|
||||
|
||||
export interface CustomerOrderSearchState {
|
||||
defaultSettings?: QuerySettingsDTO;
|
||||
@@ -125,8 +125,6 @@ export class CustomerOrderSearchStore extends ComponentStore<CustomerOrderSearch
|
||||
|
||||
searchStarted = new Subject<{ clear?: boolean; silentReload?: boolean }>();
|
||||
|
||||
cancelSearch$ = new Subject<void>();
|
||||
|
||||
constructor(private _domainGoodsInService: DomainCustomerOrderService, private _cache: CacheService) {
|
||||
super({
|
||||
fetching: false,
|
||||
@@ -194,11 +192,6 @@ export class CustomerOrderSearchStore extends ComponentStore<CustomerOrderSearch
|
||||
});
|
||||
}
|
||||
|
||||
cancelSearchRequest() {
|
||||
this.cancelSearch$.next();
|
||||
this.patchState({ fetching: false, silentFetching: false });
|
||||
}
|
||||
|
||||
search = this.effect((options$: Observable<{ clear?: boolean; siletReload?: boolean }>) =>
|
||||
options$.pipe(
|
||||
tap((_) => {
|
||||
@@ -245,7 +238,6 @@ export class CustomerOrderSearchStore extends ComponentStore<CustomerOrderSearch
|
||||
}
|
||||
|
||||
return this._domainGoodsInService.search(queryToken).pipe(
|
||||
takeUntil(this.cancelSearch$),
|
||||
tapResponse(
|
||||
(res) => {
|
||||
let _results: OrderItemListItemDTO[] = [];
|
||||
|
||||
@@ -17,7 +17,10 @@ import { ApplicationService } from '@core/application';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class CustomerOrderSearchMainComponent implements OnInit, OnDestroy {
|
||||
filter$ = this._customerOrderSearchStore.filter$.pipe(filter((f) => !!f));
|
||||
filter$ = this._customerOrderSearchStore.filter$.pipe(
|
||||
filter((f) => !!f),
|
||||
take(1)
|
||||
);
|
||||
|
||||
loading$ = this._customerOrderSearchStore.fetching$;
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
>
|
||||
<div class="page-customer-order-item__item-thumbnail text-center mr-4 w-[3.125rem] h-[4.9375rem]">
|
||||
<img
|
||||
class="page-customer-order-item__item-image w-[3.125rem] max-h-[4.9375rem]"
|
||||
class="page-customer-order-item__item-image w-[3.125rem] h-[4.9375rem]"
|
||||
loading="lazy"
|
||||
*ngIf="item?.product?.ean | productImage; let productImage"
|
||||
[src]="productImage"
|
||||
|
||||
@@ -170,9 +170,10 @@ export class CustomerOrderSearchResultsComponent extends ComponentStore<Customer
|
||||
);
|
||||
|
||||
this._searchResultSubscription.add(
|
||||
combineLatest([this.processId$, this._activatedRoute.queryParams])
|
||||
this.processId$
|
||||
.pipe(
|
||||
debounceTime(150),
|
||||
withLatestFrom(this._activatedRoute.queryParams),
|
||||
switchMap(([processId, params]) =>
|
||||
this._application.getSelectedBranch$(processId).pipe(map((selectedBranch) => ({ processId, params, selectedBranch })))
|
||||
)
|
||||
@@ -184,6 +185,16 @@ export class CustomerOrderSearchResultsComponent extends ComponentStore<Customer
|
||||
|
||||
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 });
|
||||
@@ -201,7 +212,6 @@ export class CustomerOrderSearchResultsComponent extends ComponentStore<Customer
|
||||
|
||||
if (!isEqual(cleanQueryParams, this.cleanupQueryParams(this._customerOrderSearchStore.filter.getQueryParams()))) {
|
||||
this._customerOrderSearchStore.setQueryParams(params);
|
||||
|
||||
const queryToken = {
|
||||
...this._customerOrderSearchStore.filter.getQueryParams(),
|
||||
processId,
|
||||
@@ -236,8 +246,8 @@ export class CustomerOrderSearchResultsComponent extends ComponentStore<Customer
|
||||
|
||||
const process = await this._application.getProcessById$(processId).pipe(first()).toPromise();
|
||||
if (!!process) {
|
||||
await this.createBreadcrumb(processId, params);
|
||||
await this.updateBreadcrumb(processId, params);
|
||||
await this.createBreadcrumb(processId, params);
|
||||
}
|
||||
|
||||
if (this._activatedRoute?.outlet === 'primary') {
|
||||
@@ -278,13 +288,13 @@ export class CustomerOrderSearchResultsComponent extends ComponentStore<Customer
|
||||
...this.cleanupQueryParams(this._customerOrderSearchStore?.filter?.getQueryParams()),
|
||||
main_qs: this.sharedFilterInputGroupMain?.uiInput?.value,
|
||||
};
|
||||
|
||||
this._customerOrderSearchStore?.setQueryParams(queryParams);
|
||||
})
|
||||
);
|
||||
|
||||
this._router.events.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((event) => {
|
||||
if (event instanceof NavigationStart) {
|
||||
this.cacheResults();
|
||||
this._addScrollPositionToCache();
|
||||
}
|
||||
});
|
||||
@@ -325,6 +335,18 @@ export class CustomerOrderSearchResultsComponent extends ComponentStore<Customer
|
||||
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());
|
||||
}
|
||||
|
||||
@@ -342,6 +364,25 @@ export class CustomerOrderSearchResultsComponent extends ComponentStore<Customer
|
||||
return clean;
|
||||
}
|
||||
|
||||
async removeBreadcrumbs(processId: number) {
|
||||
const editCrumbs = await this._breadcrumb.getBreadcrumbsByKeyAndTags$(processId, ['customer-order', 'edit']).pipe(first()).toPromise();
|
||||
|
||||
const historyCrumbs = await this._breadcrumb
|
||||
.getBreadcrumbsByKeyAndTags$(processId, ['customer-order', 'history'])
|
||||
.pipe(first())
|
||||
.toPromise();
|
||||
|
||||
editCrumbs.forEach((crumb) => {
|
||||
this._breadcrumb.removeBreadcrumb(crumb.id, true);
|
||||
});
|
||||
|
||||
historyCrumbs.forEach((crumb) => {
|
||||
this._breadcrumb.removeBreadcrumb(crumb.id, true);
|
||||
});
|
||||
|
||||
await this.removeDetailsBreadcrumb(processId);
|
||||
}
|
||||
|
||||
async removeDetailsBreadcrumb(processId: number) {
|
||||
const detailsCrumbs = await this._breadcrumb
|
||||
.getBreadcrumbsByKeyAndTags$(processId, ['customer-order', 'details'])
|
||||
@@ -353,19 +394,7 @@ export class CustomerOrderSearchResultsComponent extends ComponentStore<Customer
|
||||
});
|
||||
}
|
||||
|
||||
async createMainBreadcrumb(processId: number, params: Record<string, string>) {
|
||||
await this._breadcrumb.addBreadcrumbIfNotExists({
|
||||
key: processId,
|
||||
name: 'Kundenbestellung',
|
||||
path: this._navigationService.getCustomerOrdersBasePath(processId).path,
|
||||
params,
|
||||
tags: ['customer-order', 'main', 'filter'],
|
||||
section: 'customer',
|
||||
});
|
||||
}
|
||||
|
||||
async createBreadcrumb(processId: number, params: Record<string, string>) {
|
||||
await this.createMainBreadcrumb(processId, params);
|
||||
await this._breadcrumb.addOrUpdateBreadcrumbIfNotExists({
|
||||
key: processId,
|
||||
name: this.getBreadcrumbName(params),
|
||||
@@ -394,20 +423,6 @@ export class CustomerOrderSearchResultsComponent extends ComponentStore<Customer
|
||||
}
|
||||
}
|
||||
|
||||
cacheResults() {
|
||||
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,
|
||||
});
|
||||
}
|
||||
|
||||
getBreadcrumbName(params: Record<string, string>) {
|
||||
const input = params?.main_qs;
|
||||
|
||||
@@ -427,8 +442,8 @@ export class CustomerOrderSearchResultsComponent extends ComponentStore<Customer
|
||||
search({ filter, clear = false }: { filter?: Filter; clear?: boolean }) {
|
||||
if (!!filter) {
|
||||
this.sharedFilterInputGroupMain.cancelAutocomplete();
|
||||
this._customerOrderSearchStore.setQueryParams(filter?.getQueryParams());
|
||||
}
|
||||
|
||||
this._customerOrderSearchStore.search({ clear });
|
||||
}
|
||||
|
||||
|
||||
@@ -3,21 +3,18 @@ import { ActivatedRoute } from '@angular/router';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { AuthService } from '@core/auth';
|
||||
import { EnvironmentService } from '@core/environment';
|
||||
import { provideComponentStore } from '@ngrx/component-store';
|
||||
import { BranchSelectorComponent } from '@shared/components/branch-selector';
|
||||
import { BreadcrumbComponent } from '@shared/components/breadcrumb';
|
||||
import { BranchDTO } from '@swagger/checkout';
|
||||
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
|
||||
import { Observable, Subject, fromEvent } from 'rxjs';
|
||||
import { first, map, switchMap, takeUntil, withLatestFrom } from 'rxjs/operators';
|
||||
import { CustomerOrderSearchStore } from './customer-order-search';
|
||||
|
||||
@Component({
|
||||
selector: 'page-customer-order',
|
||||
templateUrl: 'customer-order.component.html',
|
||||
styleUrls: ['customer-order.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
providers: [provideComponentStore(CustomerOrderSearchStore)],
|
||||
})
|
||||
export class CustomerOrderComponent implements OnInit {
|
||||
@ViewChild(BreadcrumbComponent, { read: ElementRef }) breadcrumbRef: ElementRef<HTMLElement>;
|
||||
@@ -42,24 +39,13 @@ export class CustomerOrderComponent implements OnInit {
|
||||
private _uiModal: UiModalService,
|
||||
private _renderer: Renderer2,
|
||||
private _environmentService: EnvironmentService,
|
||||
public auth: AuthService,
|
||||
private _store: CustomerOrderSearchStore
|
||||
public auth: AuthService
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.selectedBranch$ = this.application.activatedProcessId$.pipe(
|
||||
switchMap((processId) => this.application.getSelectedBranch$(Number(processId)))
|
||||
);
|
||||
|
||||
/* Ticket #4544 - Suchrequest abbrechen bei Prozesswechsel
|
||||
/ um zu verhindern, dass die Suche in einen anderen Kundenbestellungen Prozess übernommen wird
|
||||
/ bei Prozesswechsel zwischen 2 Kundenbestellungen Prozessen
|
||||
*/
|
||||
this.processId$.pipe(takeUntil(this._onDestroy$)).subscribe((processId) => {
|
||||
if (Number(processId) !== this._store.processId) {
|
||||
this._store.cancelSearchRequest();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
[tabindex]="tabIndexStart"
|
||||
[autofocus]="focusAfterInit"
|
||||
>
|
||||
<shared-select-option *ngFor="let gender of genderSettings.genders" [value]="gender.value">{{ gender.label }}</shared-select-option>
|
||||
<shared-select-option [value]="2">Herr</shared-select-option>
|
||||
<shared-select-option [value]="4">Frau</shared-select-option>
|
||||
</shared-select>
|
||||
</shared-form-control>
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Component, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { UntypedFormControl, UntypedFormGroup } from '@angular/forms';
|
||||
import { FormBlockGroup } from '../form-block';
|
||||
import { NameFormBlockData } from './name-form-block-data';
|
||||
import { GenderSettingsService } from '@shared/services';
|
||||
import { Gender } from '@swagger/crm';
|
||||
|
||||
@Component({
|
||||
selector: 'app-name-form-block',
|
||||
@@ -15,7 +15,18 @@ export class NameFormBlockComponent extends FormBlockGroup<NameFormBlockData> {
|
||||
return this.tabIndexStart + 3;
|
||||
}
|
||||
|
||||
constructor(public genderSettings: GenderSettingsService) {
|
||||
displayGenderNameFn = (gender: Gender) => {
|
||||
if (gender == 2) {
|
||||
return 'Herr';
|
||||
}
|
||||
if (gender == 4) {
|
||||
return 'Frau';
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import { CrmCustomerService } from '@domain/crm';
|
||||
import { AddressDTO, CustomerDTO, CustomerInfoDTO, PayerDTO, ShippingAddressDTO } from '@swagger/crm';
|
||||
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
|
||||
import { UiValidators } from '@ui/validators';
|
||||
import { isNull, merge } from 'lodash';
|
||||
import { isNull } from 'lodash';
|
||||
import { BehaviorSubject, Observable, of, Subject } from 'rxjs';
|
||||
import {
|
||||
first,
|
||||
@@ -229,33 +229,7 @@ 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) => {
|
||||
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;
|
||||
return !response?.error && (response as any)?.result === 1 ? null : { invalid: 'Kundenkartencode ist ungültig' };
|
||||
}),
|
||||
catchError((error) => {
|
||||
if (error instanceof HttpErrorResponse) {
|
||||
@@ -268,13 +242,31 @@ export abstract class AbstractCreateCustomer implements OnInit, OnDestroy {
|
||||
})
|
||||
);
|
||||
}),
|
||||
tap(() => {
|
||||
tap(async (result) => {
|
||||
control.markAsTouched();
|
||||
this.cdr.markForCheck();
|
||||
|
||||
if (result === null) {
|
||||
const customerInfoDto = await this.getAnonymousCustomerForCode(control.value);
|
||||
if (customerInfoDto) {
|
||||
const data = mapCustomerInfoDtoToCustomerCreateFormData(customerInfoDto);
|
||||
this._formData.next(data);
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
async getAnonymousCustomerForCode(code: string): Promise<CustomerInfoDTO | undefined> {
|
||||
try {
|
||||
const res = await this.customerService.getCustomers(code).toPromise();
|
||||
|
||||
if (res.result.length > 0 && res.result[0].id < 0) {
|
||||
return res.result[0];
|
||||
}
|
||||
} catch (error) {}
|
||||
}
|
||||
|
||||
async navigateToCustomerDetails(customer: CustomerDTO) {
|
||||
const processId = await this.processId$.pipe(first()).toPromise();
|
||||
const route = this.customerSearchNavigation.detailsRoute({ processId, customerId: customer.id, customer });
|
||||
|
||||
@@ -41,7 +41,7 @@ export class CreateB2BCustomerComponent extends AbstractCreateCustomer {
|
||||
deviatingNameRequiredMarks: (keyof NameFormBlockData)[] = ['gender', 'firstName', 'lastName'];
|
||||
|
||||
deviatingNameValidationFns: Record<string, ValidatorFn[]> = {
|
||||
gender: [Validators.required],
|
||||
gender: [Validators.required, Validators.min(1)],
|
||||
firstName: [Validators.required],
|
||||
lastName: [Validators.required],
|
||||
};
|
||||
|
||||
@@ -25,7 +25,7 @@ export class CreateGuestCustomerComponent extends AbstractCreateCustomer {
|
||||
nameValidationFns: Record<keyof NameFormBlockData, ValidatorFn[]> = {
|
||||
firstName: [Validators.required],
|
||||
lastName: [Validators.required],
|
||||
gender: [Validators.required],
|
||||
gender: [Validators.required, Validators.min(1)],
|
||||
title: [],
|
||||
};
|
||||
|
||||
@@ -44,7 +44,7 @@ export class CreateGuestCustomerComponent extends AbstractCreateCustomer {
|
||||
deviatingNameRequiredMarks: (keyof NameFormBlockData)[] = ['gender', 'firstName', 'lastName'];
|
||||
|
||||
deviatingNameValidationFns: Record<string, ValidatorFn[]> = {
|
||||
gender: [Validators.required],
|
||||
gender: [Validators.required, Validators.min(1)],
|
||||
firstName: [Validators.required],
|
||||
lastName: [Validators.required],
|
||||
};
|
||||
|
||||
@@ -64,12 +64,6 @@
|
||||
>
|
||||
</app-name-form-block>
|
||||
|
||||
<p class="info" *ngIf="customerType === 'webshop-p4m'">
|
||||
Wir werden Ihnen Werbung zu ähnlichen Produkten oder Dienstleistungen aus unserem Sortiment per E-Mail zusenden. Sie können der
|
||||
Verwendung Ihrer Daten jederzeit z.B. mittels der in den E-Mails enthaltenen Abmeldelinks widersprechen, ohne dass hierfür andere als
|
||||
die Übermittlungskosten nach den Basistarifen entstehen.
|
||||
</p>
|
||||
|
||||
<app-email-form-block
|
||||
class="flex-grow"
|
||||
#email
|
||||
|
||||
@@ -36,7 +36,7 @@ export class CreateP4MCustomerComponent extends AbstractCreateCustomer implement
|
||||
nameValidationFns: Record<keyof NameFormBlockData, ValidatorFn[]> = {
|
||||
firstName: [Validators.required],
|
||||
lastName: [Validators.required],
|
||||
gender: [Validators.required],
|
||||
gender: [Validators.required, Validators.min(1)],
|
||||
title: [],
|
||||
};
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ export class CreateStoreCustomerComponent extends AbstractCreateCustomer {
|
||||
|
||||
nameValidationFns: Record<string, ValidatorFn[]> = {
|
||||
title: [],
|
||||
gender: [Validators.required],
|
||||
gender: [Validators.required, Validators.min(1)],
|
||||
firstName: [Validators.required],
|
||||
lastName: [Validators.required],
|
||||
};
|
||||
|
||||
@@ -25,7 +25,7 @@ export class CreateWebshopCustomerComponent extends AbstractCreateCustomer {
|
||||
nameValidationFns: Record<keyof NameFormBlockData, ValidatorFn[]> = {
|
||||
firstName: [Validators.required],
|
||||
lastName: [Validators.required],
|
||||
gender: [Validators.required],
|
||||
gender: [Validators.required, Validators.min(1)],
|
||||
title: [],
|
||||
};
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ export class UpdateP4MWebshopCustomerComponent extends AbstractCreateCustomer im
|
||||
nameValidationFns: Record<keyof NameFormBlockData, ValidatorFn[]> = {
|
||||
firstName: [Validators.required],
|
||||
lastName: [Validators.required],
|
||||
gender: [Validators.required],
|
||||
gender: [Validators.required, Validators.min(1)],
|
||||
title: [],
|
||||
};
|
||||
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
<form [formGroup]="formGroup" (ngSubmit)="save()">
|
||||
<shared-form-control label="Anrede">
|
||||
<shared-select formControlName="gender" placeholder="Anrede" tabindex="1" [autofocus]="true">
|
||||
<shared-select-option *ngFor="let gender of genderSettings.genders" [value]="gender.value">{{ gender.label }}</shared-select-option>
|
||||
<shared-select-option [value]="2">Herr</shared-select-option>
|
||||
<shared-select-option [value]="4">Frau</shared-select-option>
|
||||
</shared-select>
|
||||
</shared-form-control>
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import { map } from 'rxjs/operators';
|
||||
import { AsyncPipe, NgForOf, NgIf } from '@angular/common';
|
||||
import { AddressSelectionModalService } from '@shared/modals/address-selection-modal';
|
||||
import { CustomerSearchStore } from '../store';
|
||||
import { CustomerSearchNavigation, GenderSettingsService } from '@shared/services';
|
||||
import { CustomerSearchNavigation } from '@shared/services';
|
||||
import { IconComponent } from '@shared/components/icon';
|
||||
import { combineLatest } from 'rxjs';
|
||||
import { RouterLink } from '@angular/router';
|
||||
@@ -35,7 +35,7 @@ import { RouterLink } from '@angular/router';
|
||||
})
|
||||
export class AddBillingAddressMainViewComponent {
|
||||
formGroup = new FormGroup({
|
||||
gender: new FormControl<Gender>(undefined, [Validators.required]),
|
||||
gender: new FormControl<Gender>(0, [Validators.required, Validators.min(1)]),
|
||||
title: new FormControl<string>(undefined),
|
||||
firstName: new FormControl<string>(undefined, [Validators.required]),
|
||||
lastName: new FormControl<string>(undefined, [Validators.required]),
|
||||
@@ -59,8 +59,7 @@ export class AddBillingAddressMainViewComponent {
|
||||
private _customerService: CrmCustomerService,
|
||||
private _addressSelection: AddressSelectionModalService,
|
||||
private _store: CustomerSearchStore,
|
||||
private _navigation: CustomerSearchNavigation,
|
||||
public genderSettings: GenderSettingsService
|
||||
private _navigation: CustomerSearchNavigation
|
||||
) {}
|
||||
|
||||
async save() {
|
||||
|
||||
@@ -27,7 +27,8 @@
|
||||
|
||||
<shared-form-control label="Anrede">
|
||||
<shared-select formControlName="gender" placeholder="Anrede" tabindex="4" [autofocus]="true">
|
||||
<shared-select-option *ngFor="let gender of genderSettings.genders" [value]="gender.value">{{ gender.label }}</shared-select-option>
|
||||
<shared-select-option [value]="2">Herr</shared-select-option>
|
||||
<shared-select-option [value]="4">Frau</shared-select-option>
|
||||
</shared-select>
|
||||
</shared-form-control>
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import { map, takeUntil } from 'rxjs/operators';
|
||||
import { AsyncPipe, NgForOf, NgIf } from '@angular/common';
|
||||
import { AddressSelectionModalService } from '@shared/modals/address-selection-modal';
|
||||
import { CustomerSearchStore } from '../store';
|
||||
import { CustomerSearchNavigation, GenderSettingsService } from '@shared/services';
|
||||
import { CustomerSearchNavigation } from '@shared/services';
|
||||
import { Subject, combineLatest } from 'rxjs';
|
||||
import { RouterLink } from '@angular/router';
|
||||
import { IconComponent } from '@shared/components/icon';
|
||||
@@ -41,7 +41,7 @@ export class AddShippingAddressMainViewComponent implements OnInit, OnDestroy {
|
||||
);
|
||||
|
||||
formGroup = new FormGroup({
|
||||
gender: new FormControl<Gender>(undefined, [Validators.required]),
|
||||
gender: new FormControl<Gender>(0, [Validators.required, Validators.min(1)]),
|
||||
title: new FormControl<string>(undefined),
|
||||
firstName: new FormControl<string>(undefined, [Validators.required]),
|
||||
lastName: new FormControl<string>(undefined, [Validators.required]),
|
||||
@@ -65,8 +65,7 @@ export class AddShippingAddressMainViewComponent implements OnInit, OnDestroy {
|
||||
private _customerService: CrmCustomerService,
|
||||
private _addressSelection: AddressSelectionModalService,
|
||||
private _store: CustomerSearchStore,
|
||||
private _navigation: CustomerSearchNavigation,
|
||||
public genderSettings: GenderSettingsService
|
||||
private _navigation: CustomerSearchNavigation
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
|
||||
@@ -39,9 +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 *ngIf="created$ | async; let created" class="data-value">
|
||||
{{ created | date: 'dd.MM.yyyy' }} | {{ created | 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>
|
||||
@@ -113,7 +111,7 @@
|
||||
</div>
|
||||
<div class="customer-details-customer-main-row">
|
||||
<div class="data-label">Land</div>
|
||||
<div *ngIf="country$ | async; let country" class="data-value">{{ country | country }}</div>
|
||||
<div class="data-value">{{ country$ | async | country }}</div>
|
||||
</div>
|
||||
<div class="customer-details-customer-main-row">
|
||||
<div class="data-label">Festnetznr.</div>
|
||||
@@ -125,16 +123,6 @@
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="!(isBusinessKonto$ | async)">
|
||||
<div class="customer-details-customer-main-row">
|
||||
<div class="data-label">Geburtstag</div>
|
||||
<div class="data-value">{{ dateOfBirth$ | async | date: 'dd.MM.yyyy' }}</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="!(isBusinessKonto$ | async) && (organisationName$ | async)">
|
||||
<div class="customer-details-customer-main-row">
|
||||
<div class="data-label">Firmenname</div>
|
||||
<div class="data-value">{{ organisationName$ | async }}</div>
|
||||
</div>
|
||||
<div class="customer-details-customer-main-row">
|
||||
<div class="data-label">Abteilung</div>
|
||||
<div class="data-value">{{ department$ | async }}</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, inject } from '@angular/core';
|
||||
import { Subject, combineLatest } from 'rxjs';
|
||||
import { first, map, switchMap, takeUntil } from 'rxjs/operators';
|
||||
import { CustomerSearchNavigation, GenderSettingsService } from '@shared/services';
|
||||
import { CustomerSearchNavigation } from '@shared/services';
|
||||
import { CustomerSearchStore } from '../store';
|
||||
import { ShippingAddressDTO, NotificationChannel, ShoppingCartDTO, PayerDTO, BuyerDTO } from '@swagger/checkout';
|
||||
import { DomainCheckoutService } from '@domain/checkout';
|
||||
@@ -10,11 +10,16 @@ import { UiModalService } from '@ui/modal';
|
||||
import { ComponentStore } from '@ngrx/component-store';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { CheckoutNavigationService, ProductCatalogNavigationService } from '@shared/services';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
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',
|
||||
4: 'Frau',
|
||||
};
|
||||
|
||||
export interface CustomerDetailsViewMainState {
|
||||
isBusy: boolean;
|
||||
shoppingCart: ShoppingCartDTO;
|
||||
@@ -33,9 +38,7 @@ export class CustomerDetailsViewMainComponent extends ComponentStore<CustomerDet
|
||||
|
||||
customerService = inject(CrmCustomerService);
|
||||
|
||||
fetching$ = combineLatest([this._store.fetchingCustomer$, this._store.fetchingCustomerList$]).pipe(
|
||||
map(([fetchingCustomer, fetchingList]) => fetchingCustomer || fetchingList)
|
||||
);
|
||||
fetching$ = this._store.fetchingCustomer$;
|
||||
|
||||
isBusy$ = this.select((s) => s.isBusy);
|
||||
|
||||
@@ -83,7 +86,7 @@ export class CustomerDetailsViewMainComponent extends ComponentStore<CustomerDet
|
||||
|
||||
customerNumberBeeline$ = this._store.select((s) => s.customer?.linkedRecords?.find((r) => r.repository === 'beeline')?.number);
|
||||
|
||||
gender$ = this._store.select((s) => this._genderSettings.getGenderByValue(s.customer?.gender));
|
||||
gender$ = this._store.select((s) => GENDER_MAP[s.customer?.gender]);
|
||||
|
||||
title$ = this._store.select((s) => s.customer?.title);
|
||||
|
||||
@@ -121,8 +124,6 @@ export class CustomerDetailsViewMainComponent extends ComponentStore<CustomerDet
|
||||
|
||||
shoppingCartHasNoItems$ = this.shoppingCartHasItems$.pipe(map((hasItems) => !hasItems));
|
||||
|
||||
dateOfBirth$ = this._store.select((s) => s.customer?.dateOfBirth);
|
||||
|
||||
get isBusy() {
|
||||
return this.get((s) => s.isBusy);
|
||||
}
|
||||
@@ -180,10 +181,6 @@ export class CustomerDetailsViewMainComponent extends ComponentStore<CustomerDet
|
||||
return this.get((s) => s.payer);
|
||||
}
|
||||
|
||||
get snapshot() {
|
||||
return this._activatedRoute.snapshot;
|
||||
}
|
||||
|
||||
constructor(
|
||||
private _store: CustomerSearchStore,
|
||||
private _navigation: CustomerSearchNavigation,
|
||||
@@ -192,9 +189,7 @@ export class CustomerDetailsViewMainComponent extends ComponentStore<CustomerDet
|
||||
private _application: ApplicationService,
|
||||
private _catalogNavigation: ProductCatalogNavigationService,
|
||||
private _checkoutNavigation: CheckoutNavigationService,
|
||||
private _router: Router,
|
||||
private _activatedRoute: ActivatedRoute,
|
||||
private _genderSettings: GenderSettingsService
|
||||
private _router: Router
|
||||
) {
|
||||
super({ isBusy: false, shoppingCart: undefined, shippingAddress: undefined, payer: undefined });
|
||||
}
|
||||
@@ -227,21 +222,6 @@ export class CustomerDetailsViewMainComponent extends ComponentStore<CustomerDet
|
||||
.subscribe((shoppingCart) => {
|
||||
this.patchState({ shoppingCart });
|
||||
});
|
||||
|
||||
// #4564 Fix - Da der Kunde bei einer erneuten Suche auf undefined gesetzt wird, muss man diesen erneut nach erfolgreicher Suche setzen.
|
||||
// Dies geschieht bereits in der customer-search.component.ts immer wenn eine Navigation ausgeführt wird (sprich für Fälle in denen ein neuer Kunde gesetzt wird).
|
||||
// Falls aber nach exakt dem gleichen Kunden gesucht wird, muss man diesen hier manuell erneut setzen, ansonsten bleibt dieser im Store auf undefined.
|
||||
// Da dies nur für die Detailansicht relevant ist, wird hier auch auf hits 1 überprüft, ansonsten befindet man sich sowieso in der Trefferliste.
|
||||
this._store.customerListResponse$.pipe(takeUntil(this._onDestroy$)).subscribe(([result]) => {
|
||||
if (result.hits === 1) {
|
||||
const customerId = result?.result[0].id;
|
||||
const selectedCustomerId = +this.snapshot.params.customerId;
|
||||
|
||||
if (customerId === selectedCustomerId) {
|
||||
this._store.selectCustomer({ customerId });
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
@@ -429,12 +409,12 @@ export class CustomerDetailsViewMainComponent extends ComponentStore<CustomerDet
|
||||
|
||||
@log
|
||||
_patchProcessName() {
|
||||
let name = `${this.customer.firstName ?? ''} ${this.customer.lastName ?? ''}`;
|
||||
let name = `${this.customer.firstName} ${this.customer.lastName}`;
|
||||
|
||||
// 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 ?? ''}`;
|
||||
name = `${this.customer.organisation?.name}`;
|
||||
}
|
||||
|
||||
this._application.patchProcess(this.processId, {
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
<form [formGroup]="formGroup" (ngSubmit)="save()">
|
||||
<shared-form-control label="Anrede">
|
||||
<shared-select formControlName="gender" placeholder="Anrede" tabindex="1" [autofocus]="true">
|
||||
<shared-select-option *ngFor="let gender of genderSettings.genders" [value]="gender.value">{{ gender.label }}</shared-select-option>
|
||||
<shared-select-option [value]="2">Herr</shared-select-option>
|
||||
<shared-select-option [value]="4">Frau</shared-select-option>
|
||||
</shared-select>
|
||||
</shared-form-control>
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import { map, switchMap, takeUntil } from 'rxjs/operators';
|
||||
import { AsyncPipe, NgForOf } from '@angular/common';
|
||||
import { AddressSelectionModalService } from '@shared/modals/address-selection-modal';
|
||||
import { CustomerSearchStore } from '../store';
|
||||
import { CustomerSearchNavigation, GenderSettingsService } from '@shared/services';
|
||||
import { CustomerSearchNavigation } from '@shared/services';
|
||||
import { ComponentStore } from '@ngrx/component-store';
|
||||
import { Subject, combineLatest } from 'rxjs';
|
||||
import { ActivatedRoute, RouterLink } from '@angular/router';
|
||||
@@ -39,7 +39,7 @@ export class EditBillingAddressMainViewComponent extends ComponentStore<EditBill
|
||||
private _cdr = inject(ChangeDetectorRef);
|
||||
|
||||
formGroup = new FormGroup({
|
||||
gender: new FormControl<Gender>(undefined, [Validators.required]),
|
||||
gender: new FormControl<Gender>(0, [Validators.required, Validators.min(1)]),
|
||||
title: new FormControl<string>(undefined),
|
||||
firstName: new FormControl<string>(undefined, [Validators.required]),
|
||||
lastName: new FormControl<string>(undefined, [Validators.required]),
|
||||
@@ -74,8 +74,7 @@ export class EditBillingAddressMainViewComponent extends ComponentStore<EditBill
|
||||
private _store: CustomerSearchStore,
|
||||
private _navigation: CustomerSearchNavigation,
|
||||
private _activatedRoute: ActivatedRoute,
|
||||
private _modal: UiModalService,
|
||||
public genderSettings: GenderSettingsService
|
||||
private _modal: UiModalService
|
||||
) {
|
||||
super({ payer: undefined });
|
||||
}
|
||||
|
||||
@@ -28,7 +28,8 @@
|
||||
|
||||
<ui-form-control [clearable]="true" label="Anrede" variant="inline">
|
||||
<ui-select formControlName="gender" tabindex="4">
|
||||
<ui-select-option *ngFor="let gender of genderSettings.genders" [value]="gender.value" [label]="gender.label"></ui-select-option>
|
||||
<ui-select-option [value]="2" label="Herr"></ui-select-option>
|
||||
<ui-select-option [value]="4" label="Frau"></ui-select-option>
|
||||
</ui-select>
|
||||
</ui-form-control>
|
||||
<ui-form-control [clearable]="true" label="Titel" variant="inline">
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
<form *ngIf="control" [formGroup]="control" (ngSubmit)="submit()">
|
||||
<ui-form-control [clearable]="true" label="Anrede" variant="inline">
|
||||
<ui-select formControlName="gender" tabindex="1">
|
||||
<ui-select-option *ngFor="let gender of genderSettings.genders" [value]="gender.value" [label]="gender.label"></ui-select-option>
|
||||
<ui-select-option [value]="2" label="Herr"></ui-select-option>
|
||||
<ui-select-option [value]="4" label="Frau"></ui-select-option>
|
||||
</ui-select>
|
||||
</ui-form-control>
|
||||
<ui-form-control [clearable]="true" label="Titel" variant="inline">
|
||||
|
||||
@@ -12,7 +12,7 @@ import { validateEmail } from '../../validators/email-validator';
|
||||
import { genderLastNameValidator } from '../../validators/gender-b2b-validator';
|
||||
import { camelCase } from 'lodash';
|
||||
import { CustomerSearchStore } from '../store';
|
||||
import { CustomerSearchNavigation, GenderSettingsService, NavigationRoute } from '@shared/services';
|
||||
import { CustomerSearchNavigation, NavigationRoute } from '@shared/services';
|
||||
|
||||
@Component({ template: '' })
|
||||
export abstract class CustomerDataEditComponent implements OnInit {
|
||||
@@ -41,8 +41,7 @@ export abstract class CustomerDataEditComponent implements OnInit {
|
||||
private cdr: ChangeDetectorRef,
|
||||
private location: Location,
|
||||
private _store: CustomerSearchStore,
|
||||
private _navigation: CustomerSearchNavigation,
|
||||
public genderSettings: GenderSettingsService
|
||||
private _navigation: CustomerSearchNavigation
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
@@ -91,7 +90,7 @@ export abstract class CustomerDataEditComponent implements OnInit {
|
||||
|
||||
this.control = fb.group(
|
||||
{
|
||||
gender: fb.control(customerDTO?.gender, [isBranch ? Validators.required : () => null]),
|
||||
gender: fb.control(customerDTO?.gender, [isBranch ? Validators.min(1) : () => null, isBranch ? Validators.required : () => null]),
|
||||
title: fb.control(customerDTO?.title),
|
||||
lastName: fb.control(customerDTO?.lastName, [Validators.required]),
|
||||
firstName: fb.control(customerDTO?.firstName, [Validators.required]),
|
||||
|
||||
@@ -27,7 +27,8 @@
|
||||
|
||||
<shared-form-control label="Anrede">
|
||||
<shared-select formControlName="gender" placeholder="Anrede" tabindex="4" [autofocus]="true">
|
||||
<shared-select-option *ngFor="let gender of genderSettings.genders" [value]="gender.value">{{ gender.label }}</shared-select-option>
|
||||
<shared-select-option [value]="2">Herr</shared-select-option>
|
||||
<shared-select-option [value]="4">Frau</shared-select-option>
|
||||
</shared-select>
|
||||
</shared-form-control>
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import { map, switchMap, takeUntil } from 'rxjs/operators';
|
||||
import { AsyncPipe, NgForOf, NgIf } from '@angular/common';
|
||||
import { AddressSelectionModalService } from '@shared/modals/address-selection-modal';
|
||||
import { CustomerSearchStore } from '../store';
|
||||
import { CustomerSearchNavigation, GenderSettingsService } from '@shared/services';
|
||||
import { CustomerSearchNavigation } from '@shared/services';
|
||||
import { Subject, combineLatest } from 'rxjs';
|
||||
import { IconComponent } from '@shared/components/icon';
|
||||
import { ActivatedRoute, RouterLink } from '@angular/router';
|
||||
@@ -50,7 +50,7 @@ export class EditShippingAddressMainViewComponent extends ComponentStore<EditShi
|
||||
);
|
||||
|
||||
formGroup = new FormGroup({
|
||||
gender: new FormControl<Gender>(undefined, [Validators.required]),
|
||||
gender: new FormControl<Gender>(0, [Validators.required, Validators.min(1)]),
|
||||
title: new FormControl<string>(undefined),
|
||||
firstName: new FormControl<string>(undefined, [Validators.required]),
|
||||
lastName: new FormControl<string>(undefined, [Validators.required]),
|
||||
@@ -83,8 +83,7 @@ export class EditShippingAddressMainViewComponent extends ComponentStore<EditShi
|
||||
private _customerService: CrmCustomerService,
|
||||
private _addressSelection: AddressSelectionModalService,
|
||||
private _store: CustomerSearchStore,
|
||||
private _navigation: CustomerSearchNavigation,
|
||||
public genderSettings: GenderSettingsService
|
||||
private _navigation: CustomerSearchNavigation
|
||||
) {
|
||||
super({ shippingAddress: undefined });
|
||||
}
|
||||
|
||||
@@ -8,10 +8,10 @@ import { CrmCustomerService } from '@domain/crm';
|
||||
import { Result } from '@domain/defs';
|
||||
import { CustomerDTO, ListResponseArgsOfCustomerInfoDTO, QuerySettingsDTO } from '@swagger/crm';
|
||||
import { Filter } from '@shared/components/filter';
|
||||
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 +163,7 @@ export class CustomerSearchStore extends ComponentStore<CustomerSearchState> imp
|
||||
|
||||
selectedOrderItem$ = this.select(S.selectSelectedOrderItem);
|
||||
|
||||
constructor(private _customerService: CrmCustomerService, private _omsService: DomainOmsService, private _modal: UiModalService) {
|
||||
constructor(private _customerService: CrmCustomerService, private _omsService: DomainOmsService) {
|
||||
super({ customerListCount: 0 });
|
||||
}
|
||||
|
||||
@@ -205,8 +205,7 @@ export class CustomerSearchStore extends ComponentStore<CustomerSearchState> imp
|
||||
};
|
||||
|
||||
handleSelectCustomerError = (err: any) => {
|
||||
this._modal.error('Fehler beim Auswählen des Kundens', err);
|
||||
this.patchState({ fetchingCustomer: false });
|
||||
console.error(err);
|
||||
};
|
||||
|
||||
handleSelectCustomerComplete = () => {
|
||||
@@ -231,8 +230,7 @@ export class CustomerSearchStore extends ComponentStore<CustomerSearchState> imp
|
||||
};
|
||||
|
||||
handleSelectOrderError = (err: any) => {
|
||||
this._modal.error('Fehler beim Auswählen der Bestellung', err);
|
||||
this.patchState({ fetchingOrder: false });
|
||||
console.error(err);
|
||||
};
|
||||
|
||||
handleSelectOrderComplete = () => {
|
||||
@@ -261,8 +259,7 @@ export class CustomerSearchStore extends ComponentStore<CustomerSearchState> imp
|
||||
};
|
||||
|
||||
handleFetchCustomerOrdersError = (err: any) => {
|
||||
this._modal.error('Fehler beim Laden der Kundenbestellungen', err);
|
||||
this.patchState({ fetchingCustomerOrders: false });
|
||||
console.error(err);
|
||||
};
|
||||
|
||||
handleFetchCustomerOrdersComplete = () => {
|
||||
@@ -285,8 +282,7 @@ export class CustomerSearchStore extends ComponentStore<CustomerSearchState> imp
|
||||
};
|
||||
|
||||
handleFetchFilterError = (err: any) => {
|
||||
this._modal.error('Fehler beim Laden der Filter', err);
|
||||
this.patchState({ fetchingFilter: false });
|
||||
console.error(err);
|
||||
};
|
||||
|
||||
handleFetchFilterComplete = () => {
|
||||
@@ -303,10 +299,7 @@ export class CustomerSearchStore extends ComponentStore<CustomerSearchState> imp
|
||||
),
|
||||
withLatestFrom(this.filter$, this.processId$),
|
||||
map(([a1, a2, a3]) => {
|
||||
// #4564 Setze "customer" undefined immer wenn neu gesucht wird,
|
||||
// da sonst Änderungen einer zweiten ISA am Kunden, selbst nach erneuter Suche, nicht geupdated werden,
|
||||
// da noch der alte Kundendatensatz im Store gespeichert ist
|
||||
this.patchState({ fetchingCustomerList: true, customerList: [], customer: undefined, customerListCount: 0, message: '' });
|
||||
this.patchState({ fetchingCustomerList: true, customerList: [], customerListCount: 0, message: '' });
|
||||
|
||||
let retored = false;
|
||||
if (a1.ignoreRestore) {
|
||||
@@ -348,8 +341,7 @@ export class CustomerSearchStore extends ComponentStore<CustomerSearchState> imp
|
||||
};
|
||||
|
||||
handleSearchError = (err: any) => {
|
||||
this._modal.error('Fehler beim Laden der Liste', err);
|
||||
this.patchState({ fetchingCustomerList: false });
|
||||
console.error(err);
|
||||
};
|
||||
|
||||
handleSearchComplete = () => {
|
||||
|
||||
@@ -5,8 +5,8 @@ export function genderLastNameValidator(isB2b: boolean): ValidatorFn | null {
|
||||
return (control: UntypedFormGroup): ValidationErrors | null => {
|
||||
const gender = control.get('gender').value;
|
||||
const lastName = control.get('lastName').value;
|
||||
if (!!lastName) {
|
||||
control.get('gender').setValidators([Validators.required]);
|
||||
if (!!lastName && gender === 0) {
|
||||
control.get('gender').setValidators([Validators.required, Validators.min(1)]);
|
||||
return { genderNotSet: true };
|
||||
} else {
|
||||
control.get('gender').setValidators(null);
|
||||
|
||||
@@ -16,7 +16,7 @@ export function organisationB2bDeliveryValidator(): ValidatorFn | null {
|
||||
lastName = control.get('shippingAddress').get('lastName');
|
||||
organisation = control.get('shippingAddress').get('organisation').get('name');
|
||||
isOrganisationSet = !!organisation.value;
|
||||
isNameSet = !!firstName.value && !!lastName.value;
|
||||
isNameSet = gender.value !== 0 && !!firstName.value && !!lastName.value;
|
||||
|
||||
if (control.get('differentShippingAddress').value === true) {
|
||||
return validate(gender, firstName, lastName, organisation, isOrganisationSet, isNameSet);
|
||||
@@ -28,14 +28,14 @@ export function organisationB2bDeliveryValidator(): ValidatorFn | null {
|
||||
lastName = control.get('lastName');
|
||||
organisation = control.get('organisation').get('name');
|
||||
isOrganisationSet = !!organisation.value;
|
||||
isNameSet = !!firstName.value && !!lastName.value;
|
||||
isNameSet = gender.value !== 0 && !!firstName.value && !!lastName.value;
|
||||
return validate(gender, firstName, lastName, organisation, isOrganisationSet, isNameSet);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function setNameValidation(gender: AbstractControl, firstName: AbstractControl, lastName: AbstractControl) {
|
||||
gender.setValidators([Validators.required]);
|
||||
gender.setValidators([Validators.required, Validators.min(1)]);
|
||||
firstName.setValidators([Validators.required]);
|
||||
lastName.setValidators([Validators.required]);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
ui-slider {
|
||||
img {
|
||||
@apply rounded cursor-pointer;
|
||||
max-height: 160px;
|
||||
height: 160px;
|
||||
|
||||
// max-width: aut;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import { debounceTime, first, map, shareReplay, takeUntil, tap } from 'rxjs/oper
|
||||
import { GoodsInListItemComponent } from './goods-in-list-item/goods-in-list-item.component';
|
||||
import { GoodsInListStore } from './goods-in-list.store';
|
||||
import { PickupShelfInNavigationService } from '@shared/services';
|
||||
import { CacheService } from '@core/cache';
|
||||
|
||||
@Component({
|
||||
selector: 'page-goods-in-list',
|
||||
@@ -61,21 +60,20 @@ export class GoodsInListComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
|
||||
private _onDestroy$ = new Subject();
|
||||
|
||||
private readonly SCROLL_POSITION_TOKEN = 'GOODS_IN_LIST_SCROLL_POSITION';
|
||||
|
||||
constructor(
|
||||
private _breadcrumb: BreadcrumbService,
|
||||
private _domainOmsService: DomainOmsService,
|
||||
public store: GoodsInListStore,
|
||||
private _router: Router,
|
||||
private _route: ActivatedRoute,
|
||||
private readonly _config: Config,
|
||||
private _cache: CacheService
|
||||
private readonly _config: Config
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.store.setTake(Number(this._route.snapshot.queryParams.take ?? 25));
|
||||
this._route.queryParams.pipe(takeUntil(this._onDestroy$), debounceTime(0)).subscribe(async (params) => {
|
||||
const scrollPos = Number(params.scroll_position ?? 0);
|
||||
|
||||
// Initial Search - Always Search If No Params Are Set
|
||||
if (
|
||||
(Object.keys(params).length === 0 || this.store.results.length === 0) &&
|
||||
@@ -84,8 +82,7 @@ export class GoodsInListComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
this.store.search({
|
||||
cb: () => {
|
||||
setTimeout(() => {
|
||||
this.scrollContainer?.scrollTo(this._getScrollPositionFromCache());
|
||||
this._removeScrollPositionFromCache();
|
||||
this.scrollContainer?.scrollTo(scrollPos);
|
||||
}, 0);
|
||||
},
|
||||
});
|
||||
@@ -104,8 +101,7 @@ export class GoodsInListComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
this.store.search({
|
||||
cb: () => {
|
||||
setTimeout(() => {
|
||||
this.scrollContainer?.scrollTo(this._getScrollPositionFromCache());
|
||||
this._removeScrollPositionFromCache();
|
||||
this.scrollContainer?.scrollTo(scrollPos);
|
||||
}, 0);
|
||||
},
|
||||
});
|
||||
@@ -121,13 +117,12 @@ export class GoodsInListComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
this._onDestroy$.next();
|
||||
this._onDestroy$.complete();
|
||||
|
||||
this._addScrollPositionToCache();
|
||||
|
||||
this.updateBreadcrumb(this.store.filter.getQueryParams());
|
||||
}
|
||||
|
||||
cleanupQueryParams(params: Record<string, string> = {}) {
|
||||
const clean = { ...params };
|
||||
delete clean['scroll_position'];
|
||||
delete clean['take'];
|
||||
delete clean['view'];
|
||||
|
||||
@@ -148,21 +143,6 @@ export class GoodsInListComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
this.listItems.changes.pipe(takeUntil(this._onDestroy$)).subscribe(() => this.registerEditSscDisabled());
|
||||
}
|
||||
|
||||
private _removeScrollPositionFromCache(): void {
|
||||
this._cache.delete({ processId: this._config.get('process.ids.goodsIn'), token: this.SCROLL_POSITION_TOKEN });
|
||||
}
|
||||
|
||||
private _addScrollPositionToCache(): void {
|
||||
this._cache.set<number>(
|
||||
{ processId: this._config.get('process.ids.goodsIn'), token: this.SCROLL_POSITION_TOKEN },
|
||||
this.scrollContainer?.scrollPos
|
||||
);
|
||||
}
|
||||
|
||||
private _getScrollPositionFromCache(): number {
|
||||
return this._cache.get<number>({ processId: this._config.get('process.ids.goodsIn'), token: this.SCROLL_POSITION_TOKEN });
|
||||
}
|
||||
|
||||
navigateToDetails(orderItem: OrderItemListItemDTO) {
|
||||
if (this.editSsc) {
|
||||
return;
|
||||
@@ -204,6 +184,7 @@ export class GoodsInListComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
}
|
||||
|
||||
async updateBreadcrumb(queryParams: Record<string, string> | Params = this.store.filter?.getQueryParams()) {
|
||||
const scroll_position = this.scrollContainer?.scrollPos;
|
||||
const take = this._route?.snapshot?.queryParams?.take;
|
||||
|
||||
if (queryParams) {
|
||||
@@ -211,7 +192,7 @@ export class GoodsInListComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
.getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), ['goods-in', 'list'])
|
||||
.pipe(first())
|
||||
.toPromise();
|
||||
const params = { ...queryParams, take };
|
||||
const params = { ...queryParams, scroll_position, take };
|
||||
|
||||
for (const crumb of crumbs) {
|
||||
this._breadcrumb.patchBreadcrumb(crumb.id, {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ChangeDetectionStrategy, Component, OnDestroy, OnInit, ViewChild, inject } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { KeyValueDTOOfStringAndString, OrderItemListItemDTO } from '@swagger/oms';
|
||||
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
|
||||
@@ -10,7 +10,6 @@ import { GoodsInRemissionPreviewStore } from './goods-in-remission-preview.store
|
||||
import { Config } from '@core/config';
|
||||
import { ToasterService } from '@shared/shell';
|
||||
import { PickupShelfInNavigationService } from '@shared/services';
|
||||
import { CacheService } from '@core/cache';
|
||||
|
||||
@Component({
|
||||
selector: 'page-goods-in-remission-preview',
|
||||
@@ -23,6 +22,8 @@ export class GoodsInRemissionPreviewComponent implements OnInit, OnDestroy {
|
||||
private _pickupShelfInNavigationService = inject(PickupShelfInNavigationService);
|
||||
@ViewChild(UiScrollContainerComponent) scrollContainer: UiScrollContainerComponent;
|
||||
|
||||
private _scrollPosition: number;
|
||||
|
||||
items$ = this._store.results$;
|
||||
|
||||
itemLength$ = this.items$.pipe(map((items) => items?.length));
|
||||
@@ -51,16 +52,14 @@ export class GoodsInRemissionPreviewComponent implements OnInit, OnDestroy {
|
||||
byCompartmentCodeFn = (item: OrderItemListItemDTO) =>
|
||||
!!item.compartmentInfo ? `${item.compartmentCode}_${item.compartmentInfo}` : item.compartmentCode;
|
||||
|
||||
private readonly SCROLL_POSITION_TOKEN = 'REMISSION_PREVIEW_SCROLL_POSITION';
|
||||
|
||||
constructor(
|
||||
private _breadcrumb: BreadcrumbService,
|
||||
private _store: GoodsInRemissionPreviewStore,
|
||||
private _route: ActivatedRoute,
|
||||
private _router: Router,
|
||||
private _modal: UiModalService,
|
||||
private _config: Config,
|
||||
private _toast: ToasterService,
|
||||
private _cache: CacheService
|
||||
private _toast: ToasterService
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
@@ -72,25 +71,9 @@ export class GoodsInRemissionPreviewComponent implements OnInit, OnDestroy {
|
||||
ngOnDestroy(): void {
|
||||
this._onDestroy$.next();
|
||||
this._onDestroy$.complete();
|
||||
this._addScrollPositionToCache();
|
||||
this.updateBreadcrumb();
|
||||
}
|
||||
|
||||
private _removeScrollPositionFromCache(): void {
|
||||
this._cache.delete({ processId: this._config.get('process.ids.goodsIn'), token: this.SCROLL_POSITION_TOKEN });
|
||||
}
|
||||
|
||||
private _addScrollPositionToCache(): void {
|
||||
this._cache.set<number>(
|
||||
{ processId: this._config.get('process.ids.goodsIn'), token: this.SCROLL_POSITION_TOKEN },
|
||||
this.scrollContainer?.scrollPos
|
||||
);
|
||||
}
|
||||
|
||||
private _getScrollPositionFromCache(): number {
|
||||
return this._cache.get<number>({ processId: this._config.get('process.ids.goodsIn'), token: this.SCROLL_POSITION_TOKEN });
|
||||
}
|
||||
|
||||
async createBreadcrumb() {
|
||||
await this._breadcrumb.addOrUpdateBreadcrumbIfNotExists({
|
||||
key: this._config.get('process.ids.goodsIn'),
|
||||
@@ -110,6 +93,7 @@ export class GoodsInRemissionPreviewComponent implements OnInit, OnDestroy {
|
||||
for (const crumb of crumbs) {
|
||||
this._breadcrumb.patchBreadcrumb(crumb.id, {
|
||||
name: crumb.name,
|
||||
params: { scroll_position: this.scrollContainer?.scrollPos },
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -149,11 +133,14 @@ export class GoodsInRemissionPreviewComponent implements OnInit, OnDestroy {
|
||||
this._store.searchResult$.pipe(takeUntil(this._onDestroy$)).subscribe(async (result) => {
|
||||
await this.createBreadcrumb();
|
||||
|
||||
this.scrollContainer?.scrollTo(this._getScrollPositionFromCache() ?? 0);
|
||||
this._removeScrollPositionFromCache();
|
||||
if (this._scrollPosition) {
|
||||
this.scrollContainer?.scrollTo(Number(this._scrollPosition ?? 0));
|
||||
this._scrollPosition = undefined;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this._scrollPosition = this._route.snapshot.queryParams?.scroll_position;
|
||||
this._store.search();
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
}
|
||||
|
||||
.package-details-list-item__product-image {
|
||||
@apply w-12 shadow max-h-full;
|
||||
@apply w-12 h-auto shadow;
|
||||
}
|
||||
|
||||
@screen desktop {
|
||||
@@ -20,7 +20,7 @@
|
||||
}
|
||||
|
||||
.package-details-list-item__product-image {
|
||||
@apply w-auto max-h-full shadow mx-auto;
|
||||
@apply w-auto h-full shadow mx-auto;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -75,10 +75,7 @@
|
||||
>Zur erneuten Prüfung 7 Tage nach Avisierung.</ui-tooltip
|
||||
>
|
||||
</ng-container>
|
||||
<div
|
||||
class="isa-label text-white font-bold page-package-details__arrival-status"
|
||||
[class]="packageDetails.package.arrivalStatus | arrivalStatusColorClass"
|
||||
>
|
||||
<div class="isa-label text-white font-bold" [class]="packageDetails.package.arrivalStatus | arrivalStatusColorClass">
|
||||
{{ packageDetails.package.arrivalStatus | arrivalStatus }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -97,7 +94,7 @@
|
||||
</ng-container>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<span class="font-bold">{{ packageDetails?.package?.items ?? '-' }}</span> Exemplare
|
||||
<span class="font-bold">{{ packageDetails.package.items }}</span> Exemplare
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -12,7 +12,6 @@ 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;
|
||||
@@ -65,11 +64,6 @@ 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();
|
||||
@@ -125,11 +119,11 @@ export abstract class PickupShelfBaseComponent implements OnInit {
|
||||
const filterQueryParams = this.listStore.filter.getQueryParams();
|
||||
|
||||
// Only Update QueryParams if the user is already on the details, edit or history page
|
||||
// const view: string = this.activatedRoute.snapshot.data.view;
|
||||
// if (['filter', 'details', 'edit', 'history'].includes(view)) {
|
||||
// await this.router.navigate([], { queryParams: { ...queryParams, ...filterQueryParams }, skipLocationChange: true });
|
||||
// return;
|
||||
// }
|
||||
const view: string = this.activatedRoute.snapshot.data.view;
|
||||
if (['details', 'edit', 'history'].includes(view)) {
|
||||
await this.router.navigate([], { queryParams: { ...queryParams, ...filterQueryParams }, skipLocationChange: true });
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.hits === 1) {
|
||||
const detailsPath = await this.getPathForDetail(response.result[0]).pipe(take(1)).toPromise();
|
||||
|
||||
@@ -38,10 +38,6 @@ export abstract class PickupShelfDetailsBaseComponent {
|
||||
|
||||
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,
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
:host {
|
||||
@apply box-border overflow-y-scroll h-split-screen-tablet desktop-small:h-split-screen-desktop;
|
||||
}
|
||||
|
||||
.page-pickup-shelf-in-details__container {
|
||||
@apply box-border grid overflow-y-scroll h-split-screen-tablet desktop-small:h-split-screen-desktop;
|
||||
grid-template-rows: auto 1fr;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,75 +1,56 @@
|
||||
<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>
|
||||
|
||||
<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>
|
||||
|
||||
<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>
|
||||
|
||||
<ng-container *ngIf="selectedItem$ | async; let item">
|
||||
<page-pickup-shelf-details-items-group
|
||||
[orderType]="selectedItemOrderType$ | async"
|
||||
[groupedItems]="[item]"
|
||||
></page-pickup-shelf-details-items-group>
|
||||
<page-pickup-shelf-details-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>
|
||||
</ng-container>
|
||||
|
||||
<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 *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="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>
|
||||
|
||||
@@ -16,7 +16,6 @@ import { DBHOrderItemListItemDTO, KeyValueDTOOfStringAndString } from '@swagger/
|
||||
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { RunCheckTrigger } from '../../trigger';
|
||||
import { PickUpShelfDetailsItemsGroupComponent } from '../../shared/pickup-shelf-details-items-group/pickup-shelf-details-items-group.component';
|
||||
|
||||
@Component({
|
||||
selector: 'page-pickup-shelf-in-details',
|
||||
@@ -33,7 +32,6 @@ import { PickUpShelfDetailsItemsGroupComponent } from '../../shared/pickup-shelf
|
||||
PickUpShelfDetailsItemComponent,
|
||||
PickUpShelfDetailsTagsComponent,
|
||||
PickUpShelfDetailsCoversComponent,
|
||||
PickUpShelfDetailsItemsGroupComponent,
|
||||
PickupShelfAddToPreviousCompartmentCodeLabelPipe,
|
||||
UiSpinnerModule,
|
||||
OnInitDirective,
|
||||
@@ -54,12 +52,6 @@ export class PickupShelfInDetailsComponent extends PickupShelfDetailsBaseCompone
|
||||
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;
|
||||
|
||||
@@ -76,8 +68,6 @@ export class PickupShelfInDetailsComponent extends PickupShelfDetailsBaseCompone
|
||||
|
||||
selectedItem$ = this.store.selectedOrderItem$;
|
||||
|
||||
selectedItemOrderType$ = this.selectedItem$.pipe(map((item) => item?.features?.orderType));
|
||||
|
||||
coverOrderItems$ = this.store.coverOrderItems$;
|
||||
|
||||
displayedCompartmentInfo$ = this.store.compartmentInfo$;
|
||||
|
||||
@@ -34,3 +34,14 @@ 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;
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
die bearbeitet werden können.
|
||||
</div>
|
||||
<div
|
||||
class="page-pickup-shelf-in-list__items-list mb-[0.625rem]"
|
||||
class="page-pickup-shelf-in-list__items-list"
|
||||
*ngFor="let bueryNumberGroup of list$ | async | groupBy: byBuyerNumberFn; trackBy: trackByGroupFn"
|
||||
>
|
||||
<ng-container *ngIf="bueryNumberGroup.items[0]; let firstItem">
|
||||
@@ -80,3 +80,42 @@
|
||||
</div>
|
||||
<page-pickup-shelf-list-item-loader *ngIf="fetching$ | async"></page-pickup-shelf-list-item-loader>
|
||||
</div>
|
||||
|
||||
<!-- <div class="h-full relative overflow-hidden overflow-y-scroll" sharedScrollContainer>
|
||||
<div *ngIf="!(listEmpty$ | async); else emptyMessage" class="page-pickup-shelf-in-list__scroll-container m-0 p-0" (reachEnd)="loadMore()">
|
||||
<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>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
@@ -18,7 +18,7 @@ import { PickUpShelfListItemComponent } from '../../shared/pickup-shelf-list-ite
|
||||
import { Group, GroupByPipe } from '@ui/common';
|
||||
import { UiSpinnerModule } from '@ui/spinner';
|
||||
import { PickupShelfInNavigationService } from '@shared/services';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { debounceTime, map } from 'rxjs/operators';
|
||||
import { DBHOrderItemListItemDTO } from '@swagger/oms';
|
||||
import { Observable, combineLatest, of } from 'rxjs';
|
||||
import { PickupShelfDetailsStore, PickupShelfStore } from '../../store';
|
||||
@@ -79,7 +79,7 @@ export class PickUpShelfInListComponent implements OnInit, AfterViewInit {
|
||||
})
|
||||
);
|
||||
|
||||
fetching$: Observable<boolean> = this.store.fetchingList$;
|
||||
fetching$: Observable<boolean> = this.store.fetching$;
|
||||
|
||||
searchboxHint$ = this.store.searchboxHint$;
|
||||
|
||||
@@ -121,22 +121,19 @@ export class PickUpShelfInListComponent implements OnInit, AfterViewInit {
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
combineLatest([this.store.processId$, this._activatedRoute.queryParams])
|
||||
.pipe(takeUntilDestroyed(this.destroyRef))
|
||||
.subscribe(([_, queryParams]) => {
|
||||
if (!this.store.list.length || !isEqual(queryParams, this.cleanupQueryParams(this.store.filter.getQueryParams()))) {
|
||||
this.store.setQueryParams(queryParams);
|
||||
this.store.fetchList({ emitFetchListResponse: false });
|
||||
}
|
||||
this.store.processId$.pipe(takeUntilDestroyed(this.destroyRef), debounceTime(150)).subscribe((_) => {
|
||||
if (!this.store.list.length) {
|
||||
this.store.fetchList();
|
||||
}
|
||||
|
||||
const scrollPos = this._getScrollPositionFromCache();
|
||||
if (!!scrollPos && this._activatedRoute.outlet === 'primary') {
|
||||
setTimeout(() => {
|
||||
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) {
|
||||
@@ -149,20 +146,6 @@ export class PickUpShelfInListComponent implements OnInit, AfterViewInit {
|
||||
this.scrollItemIntoView();
|
||||
}
|
||||
|
||||
cleanupQueryParams(params: Record<string, string> = {}) {
|
||||
const clean = { ...params };
|
||||
|
||||
for (const key in clean) {
|
||||
if (Object.prototype.hasOwnProperty.call(clean, key)) {
|
||||
if (clean[key] == undefined) {
|
||||
delete clean[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return clean;
|
||||
}
|
||||
|
||||
private _removeScrollPositionFromCache(): void {
|
||||
this._cache.delete({ processId: this.store.processId, token: this.SCROLL_POSITION_TOKEN });
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
<div class="flex flex-row px-12 justify-center desktop-large:px-0">
|
||||
<shared-filter-input-group-main
|
||||
class="block w-full mr-3 desktop-large:mx-auto"
|
||||
*ngIf="filter$ | async; let filter"
|
||||
[inputGroup]="filter?.input | group: 'main'"
|
||||
[hint]="searchboxHint$ | async"
|
||||
[loading]="fetching$ | async"
|
||||
|
||||
@@ -38,7 +38,7 @@ export class PickUpShelfInMainSideViewComponent implements OnInit {
|
||||
})
|
||||
);
|
||||
|
||||
fetching$: Observable<boolean> = this.store.fetchingList$;
|
||||
fetching$: Observable<boolean> = this.store.fetching$;
|
||||
|
||||
searchboxHint$ = this.store.searchboxHint$;
|
||||
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
:host {
|
||||
@apply box-border overflow-y-scroll h-split-screen-tablet desktop-small:h-split-screen-desktop;
|
||||
}
|
||||
|
||||
.page-pickup-shelf-out-details__container {
|
||||
@apply box-border grid overflow-y-scroll h-split-screen-tablet desktop-small:h-split-screen-desktop;
|
||||
grid-template-rows: auto 1fr;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,55 +1,36 @@
|
||||
<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>
|
||||
|
||||
<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 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="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>
|
||||
|
||||
@@ -3,9 +3,9 @@ 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 } from '@swagger/oms';
|
||||
import { DBHOrderItemListItemDTO, KeyValueDTOOfStringAndString, OrderItemProcessingStatusValue } from '@swagger/oms';
|
||||
import { PickUpShelfOutNavigationService } from '@shared/services';
|
||||
import { BehaviorSubject, Observable, asapScheduler, combineLatest } from 'rxjs';
|
||||
import { BehaviorSubject, asapScheduler } 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';
|
||||
@@ -13,7 +13,6 @@ 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',
|
||||
@@ -29,7 +28,6 @@ import { PickUpShelfDetailsItemsGroupComponent } from '../../shared/pickup-shelf
|
||||
PickUpShelfDetailsHeaderComponent,
|
||||
PickUpShelfDetailsItemComponent,
|
||||
PickUpShelfDetailsTagsComponent,
|
||||
PickUpShelfDetailsItemsGroupComponent,
|
||||
UiSpinnerModule,
|
||||
OnInitDirective,
|
||||
FormsModule,
|
||||
@@ -46,30 +44,9 @@ export class PickupShelfOutDetailsComponent extends PickupShelfDetailsBaseCompon
|
||||
|
||||
order$ = this.store.order$;
|
||||
|
||||
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;
|
||||
})
|
||||
);
|
||||
orderItems$ = this.store.orderItems$;
|
||||
|
||||
fetching$ = this.store.fetchingOrder$;
|
||||
fetchingItems$ = this.store.fetchingOrderItems$;
|
||||
|
||||
viewFetching$ = combineLatest([this.fetching$, this.fetchingItems$]).pipe(map(([fetching, fetchingItems]) => fetching || fetchingItems));
|
||||
|
||||
selectedCompartmentInfo = this.store.selectedCompartmentInfo;
|
||||
|
||||
@@ -86,8 +63,6 @@ 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() {
|
||||
|
||||
@@ -34,3 +34,14 @@ 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;
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
die bearbeitet werden können.
|
||||
</div>
|
||||
<div
|
||||
class="page-pickup-shelf-out-list__items-list w-full mb-[0.625rem]"
|
||||
class="page-pickup-shelf-out-list__items-list w-full"
|
||||
*ngFor="let bueryNumberGroup of list$ | async | groupBy: byBuyerNumberFn; trackBy: trackByGroupFn"
|
||||
>
|
||||
<ng-container *ngIf="bueryNumberGroup.items[0]; let firstItem">
|
||||
@@ -94,3 +94,76 @@
|
||||
</button>
|
||||
</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)"
|
||||
>
|
||||
<ng-container *ngIf="processId$ | async; let processId">
|
||||
<div
|
||||
class="page-pickup-shelf-out-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-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>
|
||||
</div>
|
||||
</ng-container>
|
||||
<page-pickup-shelf-list-item-loader *ngIf="fetching$ | async"></page-pickup-shelf-list-item-loader>
|
||||
</ui-scroll-container>
|
||||
<div class="actions z-sticky h-0 gap-4" *ngIf="actions$ | async; let actions">
|
||||
<button
|
||||
[disabled]="(loadingFetchedActionButton$ | async) || (fetching$ | async)"
|
||||
class="cta-action"
|
||||
*ngFor="let action of actions"
|
||||
[class.cta-action-primary]="action.selected"
|
||||
[class.cta-action-secondary]="!action.selected"
|
||||
(click)="handleAction(action)"
|
||||
>
|
||||
<ui-spinner [show]="(loadingFetchedActionButton$ | async) || (fetching$ | async)">{{ action.label }}</ui-spinner>
|
||||
</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> -->
|
||||
|
||||
@@ -83,7 +83,7 @@ export class PickupShelfOutListComponent implements OnInit, AfterViewInit {
|
||||
})
|
||||
);
|
||||
|
||||
fetching$: Observable<boolean> = this.store.fetchingList$;
|
||||
fetching$: Observable<boolean> = this.store.fetching$;
|
||||
|
||||
searchboxHint$ = this.store.searchboxHint$;
|
||||
|
||||
@@ -245,7 +245,7 @@ export class PickupShelfOutListComponent implements OnInit, AfterViewInit {
|
||||
action,
|
||||
items: selectedItems,
|
||||
});
|
||||
this.store.fetchList({ emitFetchListResponse: false });
|
||||
this.store.fetchList();
|
||||
this.store.resetSelectedListItems();
|
||||
this.loadingFetchedActionButton$.next(false);
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
<div class="flex flex-row px-12 justify-center desktop-large:px-0">
|
||||
<shared-filter-input-group-main
|
||||
class="block w-full mr-3 desktop-large:mx-auto"
|
||||
*ngIf="filter$ | async; let filter"
|
||||
[inputGroup]="filter?.input | group: 'main'"
|
||||
[hint]="searchboxHint$ | async"
|
||||
[loading]="fetching$ | async"
|
||||
|
||||
@@ -38,7 +38,7 @@ export class PickupShelfOutMainSideViewComponent implements OnInit {
|
||||
})
|
||||
);
|
||||
|
||||
fetching$: Observable<boolean> = this.store.fetchingList$;
|
||||
fetching$: Observable<boolean> = this.store.fetching$;
|
||||
|
||||
searchboxHint$ = this.store.searchboxHint$;
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ ui-slider {
|
||||
@apply mr-5;
|
||||
|
||||
.image {
|
||||
@apply relative border-none outline-none bg-transparent flex;
|
||||
@apply relative border-none outline-none bg-transparent;
|
||||
width: 53px;
|
||||
height: 85px;
|
||||
}
|
||||
@@ -27,7 +27,7 @@ ui-slider {
|
||||
|
||||
.thumbnail {
|
||||
@apply rounded-md shadow-lg;
|
||||
max-height: 5.3125rem;
|
||||
height: 85px;
|
||||
}
|
||||
|
||||
.faded {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<button
|
||||
type="button"
|
||||
[disabled]="!(customer$ | async)"
|
||||
class="page-pickup-shelf-details-header-nav-menu__nav-button px-2 py-3 bg-[#C6CBD0] rounded flex flex-row items-center open:bg-[#596470] open:text-white z-dropdown"
|
||||
class="px-2 py-3 bg-[#C6CBD0] rounded flex flex-row items-center open:bg-[#596470] open:text-white z-dropdown"
|
||||
[cdkMenuTriggerFor]="navMenu"
|
||||
#menuTrigger="cdkMenuTriggerFor"
|
||||
[class.open]="menuTrigger.isOpen()"
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
:host {
|
||||
@apply inline-block;
|
||||
}
|
||||
|
||||
.page-pickup-shelf-details-header-nav-menu__nav-button:disabled {
|
||||
@apply cursor-not-allowed text-white;
|
||||
}
|
||||
|
||||
@@ -7,12 +7,11 @@ import { ComponentStore } from '@ngrx/component-store';
|
||||
import { IconComponent } from '@shared/components/icon';
|
||||
import { SharedMenuModule } from '@shared/components/menu';
|
||||
import { CustomerSearchNavigation } from '@shared/services';
|
||||
import { CustomerInfoDTO } from '@swagger/crm';
|
||||
import { combineLatest } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
export interface PickUpShelfDetailsHeaderNavMenuComponentState {
|
||||
customer?: CustomerInfoDTO;
|
||||
customerId?: number;
|
||||
showCustomerDetails: boolean;
|
||||
}
|
||||
|
||||
@@ -26,26 +25,26 @@ export interface PickUpShelfDetailsHeaderNavMenuComponentState {
|
||||
imports: [CdkMenuModule, SharedMenuModule, IconComponent, RouterLink, NgIf, AsyncPipe],
|
||||
})
|
||||
export class PickUpShelfDetailsHeaderNavMenuComponent extends ComponentStore<PickUpShelfDetailsHeaderNavMenuComponentState> {
|
||||
@Input() set customer(value: CustomerInfoDTO) {
|
||||
this.patchState({ customer: value });
|
||||
@Input() set customerId(value: NumberInput) {
|
||||
this.patchState({ customerId: coerceNumberProperty(value) });
|
||||
}
|
||||
|
||||
@Input() set showCustomerDetails(value: BooleanInput) {
|
||||
this.patchState({ showCustomerDetails: coerceBooleanProperty(value) });
|
||||
}
|
||||
|
||||
readonly customer$ = this.select((state) => state.customer);
|
||||
readonly customerId$ = this.select((state) => state.customerId);
|
||||
|
||||
readonly showCustomerDetails$ = this.select((state) => state.showCustomerDetails);
|
||||
|
||||
ordersRoute$ = this.customer$.pipe(
|
||||
map((customer) => !!customer && this._navigation.ordersRoute({ processId: Date.now(), customerId: customer?.id, customer }))
|
||||
ordersRoute$ = this.customerId$.pipe(
|
||||
map((customerId) => customerId && this._navigation.ordersRoute({ processId: Date.now(), customerId }))
|
||||
);
|
||||
|
||||
customerDetailsRoute$ = combineLatest([this.showCustomerDetails$, this.customer$]).pipe(
|
||||
customerDetailsRoute$ = combineLatest([this.showCustomerDetails$, this.customerId$]).pipe(
|
||||
map(
|
||||
([showCustomerDetails, customer]) =>
|
||||
showCustomerDetails && !!customer && this._navigation.detailsRoute({ processId: Date.now(), customerId: customer?.id, customer })
|
||||
([showCustomerDetails, customerId]) =>
|
||||
showCustomerDetails && customerId && this._navigation.detailsRoute({ processId: Date.now(), customerId })
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
@@ -20,7 +20,10 @@
|
||||
|
||||
<div class="page-pickup-shelf-details-header__details bg-white px-4 pt-4 pb-5">
|
||||
<div class="flex flex-row items-center" [class.mb-8]="!orderItem?.features?.paid && !isKulturpass">
|
||||
<page-pickup-shelf-details-header-nav-menu class="mr-2" [customer]="customer$ | async"></page-pickup-shelf-details-header-nav-menu>
|
||||
<page-pickup-shelf-details-header-nav-menu
|
||||
class="mr-2"
|
||||
[customerId]="customerId$ | async"
|
||||
></page-pickup-shelf-details-header-nav-menu>
|
||||
<h2 class="page-pickup-shelf-details-header__details-header items-center">
|
||||
<div class="text-h2">
|
||||
{{ orderItem?.organisation }}
|
||||
@@ -142,6 +145,96 @@
|
||||
</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,6 +81,51 @@
|
||||
}
|
||||
}
|
||||
|
||||
.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,6 +92,10 @@ 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);
|
||||
@@ -101,9 +105,9 @@ export class PickUpShelfDetailsHeaderComponent {
|
||||
|
||||
changeDateDisabled$ = this.changeStatusDisabled$;
|
||||
|
||||
customer$ = this._store.customer$;
|
||||
customerId$ = this._store.customer$.pipe(map((customer) => customer?.id));
|
||||
|
||||
features$ = this.customer$.pipe(
|
||||
features$ = this._store.customer$.pipe(
|
||||
map((customer) => customer?.features || []),
|
||||
map((features) => features.filter((f) => f.enabled && !!f.description)),
|
||||
shareReplay()
|
||||
@@ -111,6 +115,14 @@ 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(
|
||||
@@ -121,6 +133,20 @@ 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) {
|
||||
@@ -130,6 +156,10 @@ export class PickUpShelfDetailsHeaderComponent {
|
||||
this.cdr.markForCheck();
|
||||
}
|
||||
|
||||
selectAll() {
|
||||
this._store.selectAllOrderItemIds();
|
||||
}
|
||||
|
||||
updatePickupDeadline(date: Date) {
|
||||
this.updateDate.emit({ date, type: 'pickup' });
|
||||
}
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
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,10 +11,8 @@
|
||||
<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 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 *ngFor="let notification of emailNotificationDates$ | async">
|
||||
{{ notification | date: 'dd.MM.yyyy | HH:mm' }} Uhr<br />
|
||||
</ng-container>
|
||||
</ui-tooltip>
|
||||
</ng-container>
|
||||
@@ -22,10 +20,8 @@
|
||||
<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 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 *ngFor="let notification of smsNotificationDates$ | async">
|
||||
{{ notification | date: 'dd.MM.yyyy | HH:mm' }} Uhr<br />
|
||||
</ng-container>
|
||||
</ui-tooltip>
|
||||
</ng-container>
|
||||
|
||||
@@ -22,7 +22,7 @@ button {
|
||||
|
||||
.page-pickup-shelf-details-item__thumbnail {
|
||||
img {
|
||||
@apply rounded shadow-cta w-[3.625rem] max-h-[5.9375rem];
|
||||
@apply rounded shadow-cta w-[3.625rem] h-[5.9375rem];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user