Compare commits

...

24 Commits

Author SHA1 Message Date
Nino
2e3b3064f2 #4516 Fix Also Show Details Items Group in Pick Up Shelf In 2023-12-27 15:46:24 +01:00
Nino
382f6470d3 Merge branch 'develop' into fix/4516-Pickup-Shelf-Details-Gruppierung 2023-12-27 15:36:23 +01:00
Nino Righi
d5254cc150 Merged PR 1703: #4541 Added Errorhandling to Customer Search Store
#4541 Added Errorhandling to Customer Search Store
2023-12-27 08:09:29 +00:00
Nino Righi
adc5a5a280 Merged PR 1702: #4516 WA Implementation of orderType Groups
#4516 WA Implementation of orderType Groups
2023-12-22 16:13:52 +00:00
Nino
f171be1af2 #4516 Implementation of groupedItems and finished bugfix
Co-authored-by: nasti <nastiche@users.noreply.github.com>
2023-12-22 17:01:37 +01:00
nastiche
dcff09ec7f #4516 - grouppierung von items 2023-12-22 14:41:16 +01:00
Lorenz Hilpert
f0b653fd0f Merged PR 1699: #4533 Breadcrumb wird falsch dargestellt
#4533 Breadcrumb wird falsch dargestellt
2023-12-20 16:17:39 +00:00
Lorenz Hilpert
14eba6e5ea Merged PR 1698: #4485 Kundendaten werden übernommen wenn welche vorhanden sind
#4485 Kundendaten werden übernommen wenn welche vorhanden sind
2023-12-20 14:55:04 +00:00
Nino
0a88a92b45 #4516 Init group structure for items and orderType
Co-authored-by: nasti <nastiche@users.noreply.github.com>
2023-12-19 18:03:17 +01:00
Nino Righi
5073693fc2 Merged PR 1697: #4530 Notification Batch Messages for each type
#4530 Notification Batch Messages for each type
2023-12-19 13:34:22 +00:00
Nino
736b8f7ec7 Kommentare eingefügt 2023-12-18 17:56:05 +01:00
Nino Righi
f3cb6236a5 Merged PR 1695: #4523 AHF, WA Added other notification types to Email Notification Badge
#4523 AHF, WA Added other notification types to Email Notification Badge
2023-12-15 16:03:34 +00:00
Lorenz Hilpert
4ab9890313 #4485 #4500 Kundenkartenkonto anlage Verhalten and Suche angeglichen 2023-12-15 14:34:24 +01:00
Nino Righi
a8cd6ce844 Merged PR 1693: #4522 Pickup Shelf List Added margin-bottom as gap between item groups, remov...
#4522 Pickup Shelf List Added margin-bottom as gap between item groups, removed unused code
2023-12-12 14:18:07 +00:00
Lorenz Hilpert
3bba23cc76 Merged PR 1692: #4380 Abholung als Standard wenn möglich und keine Auswahl getroffen wurde
#4380 Abholung als Standard wenn möglich und keine Auswahl getroffen wurde
2023-12-12 12:43:13 +00:00
Nino Righi
e25f176a7b Merged PR 1691: #4521 Fix Check Page Catalog List if List got already fetched completely
#4521 Fix Check Page Catalog List if List got already fetched completely
2023-12-12 09:39:22 +00:00
Nino Righi
a169d2a4e9 Merged PR 1690: #4518 Fix Pickup Shelf In List additionally Fetch Items after queryParams change
#4518 Fix Pickup Shelf In List additionally Fetch Items after queryParams change
2023-12-11 15:55:32 +00:00
Nino Righi
6a9caa432e Merged PR 1689: #4519 #4520 Page Catalog Search Breadcrumb and Navigation fixes
#4519 #4520 Page Catalog Search Breadcrumb and Navigation fixes
2023-12-11 09:23:35 +00:00
Nino Righi
389948c077 Merged PR 1688: #4510 Fix Time Format in Customer Details
#4510 Fix Time Format in Customer Details
2023-12-08 09:30:29 +00:00
Nino Righi
e7724ed8b9 Merged PR 1687: #4509 Implemented Loading Spinner on Pickup Shelf Details Pages
#4509 Implemented Loading Spinner on Pickup Shelf Details Pages
2023-12-07 10:03:39 +00:00
Nino Righi
c7f1b27fdf Merged PR 1686: #4508 Pickup Shelf In Clear Cover Items after Routing to new Details page
#4508 Pickup Shelf In Clear Cover Items after Routing to new Details page
2023-12-06 15:18:55 +00:00
Lorenz Hilpert
135f0255b8 IPad2 PDP Styling 2023-12-06 16:18:06 +01:00
Nino Righi
65a7aa569d Merged PR 1685: Scroll Position Fix After Process Change
Scroll Position Fix After Process Change
2023-12-05 18:14:32 +00:00
Nino Righi
211eaa6175 Merged PR 1684: Page Catalog Result List - Removed maxBufferSize, Updated ScrollPosition Handling
Page Catalog Result List - Removed maxBufferSize, Updated ScrollPosition Handling
2023-12-05 16:59:23 +00:00
52 changed files with 1160 additions and 599 deletions

View File

@@ -1,3 +1,386 @@
<div class="page-article-details__wrapper">
<div #detailsContainer class="page-article-details__container px-5" *ngIf="store.item$ | async; let item">
<div class="page-article-details__product-details mb-3">
<div class="page-article-details__product-bookmark justify-self-end">
<div *ngIf="showArchivBadge$ | async" class="archiv-badge">
<button [uiOverlayTrigger]="archivTooltip" class="p-0 m-0 outline-none border-none bg-transparent relative -top-[0.3125rem]">
<img src="/assets/images/bookmark_benachrichtigung_archiv.svg" alt="Archiv Badge" />
<ui-tooltip yPosition="above" xPosition="after" [yOffset]="-11" [xOffset]="-8" #archivTooltip [closeable]="true">
<ng-container *ngIf="isAvailable$ | async; else notAvailable">
Archivtitel. Wird nicht mehr gedruckt. Artikel ist bestellbar, weil lieferbar.
</ng-container>
<ng-template #notAvailable>
Archivtitel. Wird nicht mehr gedruckt. Nicht bestellbar.
</ng-template>
</ui-tooltip>
</button>
</div>
<div *ngIf="showSubscriptionBadge$ | async">
<button
[uiOverlayTrigger]="subscribtionTooltip"
class="p-0 m-0 outline-none border-none bg-transparent relative -top-[0.3125rem]"
>
<img src="/assets/images/bookmark_subscription.svg" alt="Fortsetzungsartikel Badge" />
</button>
<ui-tooltip yPosition="above" xPosition="after" [yOffset]="-11" [xOffset]="-8" #subscribtionTooltip [closeable]="true"
>Artikel ist ein Fortsetzungsartikel,<br />
Artikel muss über eine Aboabteilung<br />
bestellt werden.
</ui-tooltip>
</div>
<div *ngIf="showPromotionBadge$ | async" class="promotion-badge">
<button [uiOverlayTrigger]="promotionTooltip" class="p-0 m-0 outline-none border-none bg-transparent relative -top-[0.3125rem]">
<ui-icon-badge icon="gift" alt="Prämienkatalog Badge"></ui-icon-badge>
<ui-tooltip yPosition="above" xPosition="after" [yOffset]="-11" [xOffset]="-8" #promotionTooltip [closeable]="true">
Dieser Artikel befindet sich im Prämienkatalog.
</ui-tooltip>
</button>
</div>
</div>
<div class="page-article-details__product-image-recessions flex flex-col items-center">
<div class="page-article-details__product-image">
<button class="border-none outline-none bg-transparent relative" (click)="showImages()">
<img
class="max-h-[19.6875rem] max-w-[12.1875rem] rounded"
(load)="loadImage()"
[src]="item.imageId | productImage: 195:315:true"
alt="product image"
/>
<ui-icon
class="absolute text-[#A7B9CB] inline-block bottom-[0.875rem] right-[1.125rem]"
*ngIf="imageLoaded$ | async"
icon="search_add"
size="25px"
></ui-icon>
</button>
</div>
<button
(click)="showReviews()"
class="page-article-details__product-recessions flex flex-col mt-2 items-center bg-transparent border-none outline-none"
*ngIf="item.reviews?.length > 0"
>
<ui-stars [rating]="store.reviewRating$ | async"></ui-stars>
<div class="text-p2 text-[#0556B4] font-bold">{{ item.reviews.length }} Rezensionen</div>
</button>
</div>
<div class="page-article-details__product-contributors">
<a
*ngFor="let contributor of contributors$ | async; let last = last"
class="text-[#0556B4] font-semibold no-underline text-p2"
[routerLink]="resultsPath"
[queryParams]="{ main_qs: contributor, main_author: 'author' }"
>
{{ contributor }}{{ last ? '' : ';' }}
</a>
</div>
<div class="page-article-details__product-print justify-self-end" [class.mt-4]="isBadgeVisible$ | async">
<button class="bg-transparent text-brand font-bold text-lg outline-none border-none p-0" (click)="print()">Drucken</button>
</div>
<div class="page-article-details__product-title text-h3 font-bold mb-6">
{{ item.product?.name }}
</div>
<div class="page-article-details__product-misc flex flex-col mb-4">
<div
class="page-article-details__product-format flex items-center font-bold text-p3"
*ngIf="item?.product?.format && item?.product?.formatDetail"
>
<img
*ngIf="item?.product?.format !== '--'"
class="flex mr-2 h-[1.125rem]"
[src]="'/assets/images/Icon_' + item.product?.format + '.svg'"
[alt]="item.product?.formatDetail"
/>
{{ item.product?.formatDetail }}
</div>
<div class="page-article-details__product-volume" *ngIf="item?.product?.volume">Band/Reihe {{ item?.product?.volume }}</div>
<div class="page-article-details__product-publication">{{ publicationDate$ | async }}</div>
</div>
<div class="page-article-details__product-price-info flex flex-col mb-4 flex-nowrap self-end">
<div class="page-article-details__product-price font-bold text-xl self-end" *ngIf="price$ | async; let price">
{{ price?.value?.value | currency: price?.value?.currency:'code' }}
</div>
<div *ngIf="price$ | async; let price" class="page-article-details__product-price-bound self-end">
{{ price?.vat?.vatType | vat: (priceMaintained$ | async) }}
</div>
<div class="page-article-details__product-points self-end" *ngIf="store.promotionPoints$ | async; let promotionPoints">
{{ promotionPoints }} Lesepunkte
</div>
</div>
<div class="page-article-details__product-origin-infos flex flex-col mb-4">
<div class="page-article-details__product-manufacturer" data-name="product-manufacturer">{{ item.product?.manufacturer }}</div>
<div class="page-article-details__product-language" *ngIf="item?.product?.locale" data-name="product-language">
{{ item?.product?.locale }}
</div>
</div>
<div class="page-article-details__product-stock flex justify-end items-center">
<div class="h-5 w-16 bg-[#e6eff9] animate-[load_0.75s_linear_infinite]" *ngIf="store.fetchingTakeAwayAvailability$ | async"></div>
<button
class="flex flex-row py-4 pl-4"
type="button"
[uiOverlayTrigger]="tooltip"
[overlayTriggerDisabled]="!(stockTooltipText$ | async)"
(click)="showTooltip()"
*ngIf="!(store.fetchingTakeAwayAvailability$ | async)"
>
<ng-container *ngIf="store.takeAwayAvailability$ | async; let takeAwayAvailability">
<ui-icon class="mr-2 mb-1" icon="home" size="15px"></ui-icon>
<span class="font-bold text-p3">{{ takeAwayAvailability.inStock || 0 }}x</span>
</ng-container>
</button>
<ui-tooltip #tooltip yPosition="above" xPosition="after" [yOffset]="-12" [closeable]="true">
{{ stockTooltipText$ | async }}
</ui-tooltip>
</div>
<div class="page-article-details__product-ean-specs flex flex-col">
<div class="page-article-details__product-ean" data-name="product-ean">{{ item.product?.ean }}</div>
<div class="page-article-details__product-specs">
<ng-container *ngIf="item?.specs?.length > 0">
{{ (item?.specs)[0]?.value }}
</ng-container>
</div>
</div>
<div class="page-article-details__product-availabilities flex flex-row items-center justify-end mt-4">
<div
class="h-5 w-6 bg-[#e6eff9] animate-[load_0.75s_linear_infinite]"
*ngIf="store.fetchingTakeAwayAvailability$ | async; else showAvailabilityTakeAwayIcon"
></div>
<ng-template #showAvailabilityTakeAwayIcon>
<div
*ngIf="store.isTakeAwayAvailabilityAvailable$ | async"
class="w-[2.25rem] h-[2.25rem] bg-[#D8DFE5] rounded-[5px_5px_0px_5px] flex items-center justify-center"
>
<ui-icon class="mx-1" icon="shopping_bag" size="18px"> </ui-icon>
</div>
</ng-template>
<div
class="h-5 w-6 bg-[#e6eff9] animate-[load_0.75s_linear_infinite]"
*ngIf="store.fetchingPickUpAvailability$ | async; else showAvailabilityPickUpIcon"
></div>
<ng-template #showAvailabilityPickUpIcon>
<div
#uiOverlayTrigger="uiOverlayTrigger"
[uiOverlayTrigger]="orderDeadlineTooltip"
*ngIf="store.isPickUpAvailabilityAvailable$ | async"
class="page-article-details__product-pick-up-availability w-[2.25rem] h-[2.25rem] cursor-pointer bg-[#D8DFE5] rounded-[5px_5px_0px_5px] flex items-center justify-center ml-3"
[class.tooltip-active]="uiOverlayTrigger.opened"
>
<shared-icon icon="isa-box-out" [size]="24"></shared-icon>
</div>
<ui-tooltip [warning]="true" yPosition="above" xPosition="after" [yOffset]="-12" #orderDeadlineTooltip [closeable]="true">
<b>{{ (store.pickUpAvailability$ | async)?.orderDeadline | orderDeadline }}</b>
</ui-tooltip>
</ng-template>
<div
class="h-5 w-6 bg-[#e6eff9] animate-[load_0.75s_linear_infinite]"
*ngIf="store.fetchingDeliveryAvailability$ | async; else showAvailabilityDeliveryIcon"
></div>
<ng-template #showAvailabilityDeliveryIcon>
<div
*ngIf="showDeliveryTruck$ | async"
class="w-[2.25rem] h-[2.25rem] bg-[#D8DFE5] rounded-[5px_5px_0px_5px] flex items-center justify-center ml-3"
>
<ui-icon class="-mb-[0.3125rem] -mt-[0.3125rem] mx-1" icon="truck" size="30px"></ui-icon>
</div>
</ng-template>
<div
class="h-5 w-6 bg-[#e6eff9] animate-[load_0.75s_linear_infinite]"
*ngIf="store.fetchingDeliveryB2BAvailability$ | async; else showAvailabilityDeliveryB2BIcon"
></div>
<ng-template #showAvailabilityDeliveryB2BIcon>
<div
*ngIf="showDeliveryB2BTruck$ | async"
class="w-[2.25rem] h-[2.25rem] bg-[#D8DFE5] rounded-[5px_5px_0px_5px] flex items-center justify-center ml-3"
>
<ui-icon class="-mb-[0.625rem] -mt-[0.625rem] mx-1" icon="truck_b2b" size="30px"> </ui-icon>
</div>
</ng-template>
<span *ngIf="store.isDownload$ | async" class="flex flex-row items-center">
<div class="w-[2.25rem] h-[2.25rem] bg-[#D8DFE5] rounded-[5px_5px_0px_5px] flex items-center justify-center ml-3">
<ui-icon class="mx-1" icon="download" size="18px"></ui-icon>
</div>
</span>
</div>
<div class="page-article-details__shelf-ssc">
<div class="page-article-details__ssc flex justify-end my-2 font-bold text-lg">
<div class="w-52 h-5 bg-[#e6eff9] animate-[load_0.75s_linear_infinite]" *ngIf="fetchingAvailabilities$ | async"></div>
<ng-container *ngIf="!(fetchingAvailabilities$ | async)">
<div class="text-right" *ngIf="store.sscText$ | async; let sscText">
{{ sscText }}
</div>
</ng-container>
</div>
<div class="page-article-details__shelfinfo text-right" *ngIf="store.isDownload$ | async">
<ng-container
*ngIf="
item?.stockInfos && item?.shelfInfos && (item?.stockInfos)[0]?.compartment && (item?.shelfInfos)[0]?.label;
else stockInfos
"
>
<span data-name="compartment">
{{ (item?.stockInfos)[0]?.compartment }}
</span>
/
<br />
<span data-name="shelf-info-label">
{{ (item?.shelfInfos)[0]?.label }}
</span>
</ng-container>
<ng-template #stockInfos>
<ng-container *ngIf="item?.stockInfos && (item?.stockInfos)[0]?.compartment; else shelfInfos">
{{ (item?.stockInfos)[0]?.compartment }}
</ng-container>
</ng-template>
<ng-template #shelfInfos>
<ng-container *ngIf="item?.shelfInfos && (item?.shelfInfos)[0]?.label">
<span data-name="shelf-info-label">{{ (item?.shelfInfos)[0]?.label }}</span>
</ng-container>
</ng-template>
</div>
<div class="page-article-details__shelfinfo text-right" *ngIf="!(store.isDownload$ | async)">
<ng-container
*ngIf="
item?.stockInfos && item?.shelfInfos && (item?.stockInfos)[0]?.compartment && (item?.shelfInfos)[0]?.label;
else stockInfos2
"
>
<span data-name="compartment">{{ (item?.stockInfos)[0]?.compartment }}</span>
/
<br />
<span data-name="shelf-info-label">{{ (item?.shelfInfos)[0]?.label }}</span>
</ng-container>
<ng-template #stockInfos2>
<ng-container *ngIf="item?.stockInfos && (item?.stockInfos)[0]?.compartment; else shelfInfos2">
{{ (item?.stockInfos)[0]?.compartment }}
</ng-container>
</ng-template>
<ng-template #shelfInfos2>
<ng-container *ngIf="item?.shelfInfos && (item?.shelfInfos)[0]?.label">
{{ (item?.shelfInfos)[0]?.label }}
</ng-container>
</ng-template>
</div>
</div>
</div>
<div class="page-article-details__product-formats-container mt-3" *ngIf="item.family?.length > 0">
<hr class="bg-[#E6EFF9] border-t-2" />
<div class="pt-3">
<div class="page-article-details__product-formats">
<span class="mr-2">Auch verfügbar als</span>
<ui-slider [scrollDistance]="250">
<a
class="mr-4 text-[#0556B4] font-bold no-underline px-2"
*ngFor="let format of item.family"
[routerLink]="getDetailsPath(format.product.ean)"
queryParamsHandling="preserve"
>
<span class="flex items-center">
<img
class="mr-2"
*ngIf="!!format.product?.format"
[src]="'/assets/images/OF_Icon_' + format.product?.format + '.svg'"
alt="format icon"
/>
{{ format.product?.formatDetail }}
<span class="ml-1">{{ format.catalogAvailability?.price?.value?.value | currency: '€' }}</span>
</span>
</a>
</ui-slider>
</div>
</div>
</div>
<hr class="bg-[#E6EFF9] border-t-2 my-3" />
<div #description class="page-article-details__product-description flex flex-col flex-grow mb-6" *ngIf="item.texts?.length > 0">
<page-article-details-text class="block box-border" [text]="item.texts[0]"> </page-article-details-text>
<div class="box-border">
<button
class="font-bold flex flex-row text-[#0556B4] items-center mt-2"
*ngIf="!showMore && item?.texts?.length > 1"
(click)="showMore = !showMore"
>
Mehr <ui-icon class="ml-2" size="15px" icon="arrow"></ui-icon>
</button>
</div>
<div *ngIf="showMore" class="page-article-details__product-description-text flex flex-col whitespace-pre-line break-words box-border">
<span *ngFor="let text of item.texts | slice: 1">
<h3 class="my-4 text-p2 font-bold">{{ text.label }}</h3>
{{ text.value }}
</span>
<button class="font-bold flex flex-row text-[#0556B4] items-center mt-2" (click)="showMore = !showMore">
<ui-icon class="transform ml-0 mr-2 rotate-180" size="15px" icon="arrow"></ui-icon> Weniger
</button>
<button class="page-article-details__scroll-top-cta" (click)="scrollTop(description)">
<ui-icon class="text-[#0556B4]" icon="arrow" size="20px"></ui-icon>
</button>
</div>
<div class="h-28 box-border"></div>
</div>
</div>
<div class="page-article-details__product-recommendations relative">
<button
*ngIf="store.item$ | async; let item"
class="shadow-[#dce2e9_0px_-2px_18px_0px] mb-5 border-none outline-none flex items-center px-5 h-14 min-h-[3.5rem] bg-white w-full"
(click)="showRecommendations = true"
>
<span class="uppercase text-[#0556B4] font-bold text-p3">Empfehlungen</span>
<img class="absolute right-5 -top-[0.125rem] h-12" src="assets/images/recommendation_tag.png" alt="recommendation icon" />
</button>
</div>
</div>
<div
class="page-article-details__actions absolute bottom-32 left-1/2 -translate-x-1/2 whitespace-nowrap"
*ngIf="store.item$ | async; let item"
>
<button
*ngIf="!(store.isDownload$ | async)"
class="text-brand border-2 border-brand bg-white font-bold text-lg px-[1.375rem] py-4 rounded-full mr-[1.875rem]"
(click)="showAvailabilities()"
>
Bestände in anderen Filialen
</button>
<button
class="text-white bg-brand border-brand font-bold text-lg px-[1.375rem] py-4 rounded-full border-none no-underline"
(click)="showPurchasingModal()"
[disabled]="!(isAvailable$ | async) || (fetchingAvailabilities$ | async) || (item?.features && (item?.features)[0]?.key === 'PFO')"
>
In den Warenkorb
</button>
</div>
<div class="page-article-details__recommendations-overlay absolute top-0 inset-x-0" @slideYAnimation *ngIf="showRecommendations">
<page-article-recommendations (close)="showRecommendations = false"></page-article-recommendations>
</div>
<!--
<ng-container *ngIf="!showRecommendations">
<div #detailsContainer class="page-article-details__container px-5 relative">
<ng-container *ngIf="store.item$ | async; let item">
@@ -385,4 +768,4 @@
<div class="page-article-details__recommendations-overlay absolute rounded-t" @slideYAnimation *ngIf="showRecommendations">
<page-article-recommendations (close)="showRecommendations = false"></page-article-recommendations>
</div>
</div> -->

View File

@@ -1,9 +1,13 @@
:host {
@apply box-border block h-split-screen-tablet desktop-small:h-split-screen-desktop;
@apply box-border block relative;
}
.page-article-details__wrapper {
@apply grid grid-rows-[1fr_auto] h-split-screen-tablet max-h-split-screen-tablet desktop-small:h-split-screen-desktop desktop-small:max-h-split-screen-desktop;
}
.page-article-details__container {
@apply h-full w-full overflow-y-scroll overflow-hidden bg-white rounded shadow-card flex flex-col;
@apply overflow-scroll bg-white rounded shadow-card flex flex-col;
}
.page-article-details__product-details {

View File

@@ -43,26 +43,6 @@ export class ArticleSearchComponent implements OnInit, OnDestroy {
this.removeBreadcrumbs(processId);
this.addOrUpdateBreadcrumbs(processId, queryParams);
});
this._articleSearch.searchCompleted
.pipe(takeUntil(this._onDestroy$), withLatestFrom(this._processId$))
.subscribe(async ([searchCompleted, processId]) => {
if (searchCompleted.state.searchState === '') {
const params = searchCompleted.state.filter.getQueryParams();
if (searchCompleted.state.hits === 1) {
const item = searchCompleted.state.items.find((f) => f);
await this._navigationService
.getArticleDetailsPath({
processId,
itemId: item.id,
extras: { queryParams: params },
})
.navigate();
} else {
await this._navigationService.getArticleSearchResultsPath(processId, { queryParams: params }).navigate();
}
}
});
}
cleanupQueryParams(params: Record<string, string> = {}) {

View File

@@ -16,7 +16,6 @@ export interface ArticleSearchState {
hits: number;
selectedBranch: BranchDTO;
selectedItemIds: number[];
scrollPosition: number;
defaultSettings?: UISettingsDTO;
}
@@ -44,12 +43,6 @@ export class ArticleSearchService extends ComponentStore<ArticleSearchState> {
return this.get((s) => s.items);
}
scrollPosition$ = this.select((s) => s.scrollPosition);
get scrollPosition() {
return this.get((s) => s.scrollPosition);
}
selectedBranch$ = this.select((s) => s.selectedBranch);
get selectedBranch() {
@@ -92,7 +85,6 @@ export class ArticleSearchService extends ComponentStore<ArticleSearchState> {
searchState: '',
selectedItemIds: [],
selectedBranch: undefined,
scrollPosition: 0,
});
this.setDefaultFilter();
}
@@ -113,10 +105,6 @@ export class ArticleSearchService extends ComponentStore<ArticleSearchState> {
this.patchState({ selectedBranch });
}
setScrollPosition(scrollPosition: number) {
this.patchState({ scrollPosition });
}
async setDefaultFilter(defaultQueryParams?: Record<string, string>) {
const defaultSettings = await this.catalog.getSettings().toPromise();

View File

@@ -9,6 +9,7 @@ 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',
@@ -52,7 +53,8 @@ export class ArticleSearchMainComponent implements OnInit, OnDestroy {
private route: ActivatedRoute,
private application: ApplicationService,
private breadcrumb: BreadcrumbService,
private _environment: EnvironmentService
private _environment: EnvironmentService,
private _navigationService: ProductCatalogNavigationService
) {}
ngOnInit() {
@@ -80,6 +82,14 @@ export class ArticleSearchMainComponent implements OnInit, OnDestroy {
}
this.removeResultsAndDetailsBreadcrumbs(processId);
// #4519 und #4520 Durch den Performance Umbau, wird auf allen größen kleiner als der Splitscreen die Article-Search Komponente nicht geladen
// Stattdessen werden die Komponenten search-main und search-filter geladen. Einige Funktionen von Article-Search müssen trotzdem aufgerufen werden
if (!this._environment.matchDesktopLarge()) {
this.resetFilter(queryParams);
this.removeCheckoutBreadcrumb(processId);
this.addOrUpdateBreadcrumbs(processId, queryParams);
}
})
);
@@ -163,6 +173,27 @@ export class ArticleSearchMainComponent implements OnInit, OnDestroy {
});
}
resetFilter(queryParams: Record<string, string>) {
if (Object.keys(queryParams).length === 0) {
this.searchService.resetFilter();
}
}
async removeCheckoutBreadcrumb(processId: number) {
this.breadcrumb.removeBreadcrumbsByKeyAndTags(processId, ['checkout']);
}
async addOrUpdateBreadcrumbs(processId: number, queryParams: Record<string, string>) {
await this.breadcrumb.addBreadcrumbIfNotExists({
key: processId,
name: 'Artikelsuche',
path: this._navigationService.getArticleSearchBasePath(processId).path,
params: queryParams,
tags: ['catalog', 'main'],
section: 'customer',
});
}
async removeResultsAndDetailsBreadcrumbs(processId: number) {
const resultsCrumbs = await this.breadcrumb.getBreadcrumbsByKeyAndTags$(processId, ['catalog', 'results']).pipe(first()).toPromise();
const detailCrumbs = await this.breadcrumb.getBreadcrumbsByKeyAndTags$(processId, ['catalog', 'details']).pipe(first()).toPromise();

View File

@@ -1,10 +1,7 @@
<a
<div
class="page-search-result-item__item-card hover p-5 desktop-small:px-4 desktop-small:py-[0.625rem] h-[13.25rem] desktop-small:h-[11.3125rem] bg-white border border-solid border-transparent rounded"
[class.page-search-result-item__item-card-primary]="primaryOutletActive"
[routerLink]="detailsPath"
[routerLinkActive]="!isTablet && !primaryOutletActive ? 'active' : ''"
queryParamsHandling="preserve"
(click)="isDesktopLarge ? scrollIntoView() : ''"
[class.active]="isActive"
>
<div class="page-search-result-item__item-thumbnail text-center mr-4 w-[3.125rem] h-[4.9375rem]">
<img
@@ -122,4 +119,4 @@
</ng-container>
</div>
</div>
</a>
</div>

View File

@@ -1,5 +1,5 @@
import { DatePipe } from '@angular/common';
import { Component, ChangeDetectionStrategy, Input, EventEmitter, Output, HostBinding, ElementRef } from '@angular/core';
import { Component, ChangeDetectionStrategy, Input, EventEmitter, Output, HostBinding } from '@angular/core';
import { ApplicationService } from '@core/application';
import { EnvironmentService } from '@core/environment';
import { DomainAvailabilityService, DomainInStockService } from '@domain/availability';
@@ -54,6 +54,8 @@ export class SearchResultItemComponent extends ComponentStore<SearchResultItemCo
@Input()
primaryOutletActive?: boolean = false;
@Input() isActive: boolean;
@Output()
selectedChange = new EventEmitter<ItemDTO>();
@@ -82,11 +84,6 @@ export class SearchResultItemComponent extends ComponentStore<SearchResultItemCo
return this._environment.matchDesktopLarge();
}
get detailsPath() {
return this._navigationService.getArticleDetailsPath({ processId: this.applicationService.activatedProcessId, itemId: this.item?.id })
.path;
}
get resultsPath() {
return this._navigationService.getArticleSearchResultsPath(this.applicationService.activatedProcessId).path;
}
@@ -141,7 +138,6 @@ export class SearchResultItemComponent extends ComponentStore<SearchResultItemCo
private _availability: DomainAvailabilityService,
private _environment: EnvironmentService,
private _navigationService: ProductCatalogNavigationService,
private _elRef: ElementRef<HTMLElement>,
private _store: Store
) {
super({
@@ -150,10 +146,6 @@ export class SearchResultItemComponent extends ComponentStore<SearchResultItemCo
});
}
scrollIntoView() {
this._elRef.nativeElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
setSelected() {
const isSelected = this._articleSearchService.selectedItemIds.includes(this.item?.id);
this._articleSearchService.setSelected({ selected: !isSelected, itemId: this.item?.id });

View File

@@ -46,28 +46,27 @@
</shared-order-by-filter>
</div>
<div class="h-full relative">
<cdk-virtual-scroll-viewport
#scrollContainer
class="product-list h-full"
[itemSize]="(primaryOutletActive$ | async) ? 98 : 181"
[maxBufferPx]="maxBufferCdkScrollContainer$ | async"
(scrolledIndexChange)="scrolledIndexChange($event)"
>
<search-result-item
class="page-search-results__result-item"
[class.page-search-results__result-item-primary]="primaryOutletActive$ | async"
*cdkVirtualFor="let item of results$ | async; trackBy: trackByItemId"
(selectedChange)="addToCart($event)"
[selected]="isSelected(item)"
[selectable]="isSelectable(item)"
[item]="item"
[primaryOutletActive]="primaryOutletActive$ | async"
></search-result-item>
<page-search-result-item-loading
[primaryOutletActive]="primaryOutletActive$ | async"
*ngIf="fetching$ | async"
></page-search-result-item-loading>
<ng-container *ngIf="primaryOutletActive$ | async; else sideOutlet">
<cdk-virtual-scroll-viewport class="product-list" [itemSize]="106 * (scale$ | async)" (scrolledIndexChange)="scrolledIndexChange($event)">
<a
*cdkVirtualFor="let item of results$ | async; let i = index; trackBy: trackByItemId"
[routerLink]="getDetailsPath(item.id)"
routerLinkActive
#rla="routerLinkActive"
queryParamsHandling="preserve"
(click)="scrollToItem(i)"
>
<search-result-item
class="page-search-results__result-item page-search-results__result-item-primary"
(selectedChange)="addToCart($event)"
[selected]="isSelected(item)"
[selectable]="isSelectable(item)"
[item]="item"
[primaryOutletActive]="true"
[isActive]="rla.isActive"
></search-result-item>
</a>
<page-search-result-item-loading [primaryOutletActive]="true" *ngIf="fetching$ | async"></page-search-result-item-loading>
</cdk-virtual-scroll-viewport>
<div class="actions z-sticky h-0">
<button
@@ -79,4 +78,38 @@
<ui-spinner [show]="loading$ | async">In den Warenkorb legen</ui-spinner>
</button>
</div>
</div>
</ng-container>
<ng-template #sideOutlet>
<cdk-virtual-scroll-viewport class="product-list" [itemSize]="222 * (scale$ | async)" (scrolledIndexChange)="scrolledIndexChange($event)">
<a
*cdkVirtualFor="let item of results$ | async; let i = index; trackBy: trackByItemId"
[routerLink]="getDetailsPath(item.id)"
routerLinkActive
#rla="routerLinkActive"
queryParamsHandling="preserve"
(click)="scrollToItem(i)"
>
<search-result-item
class="page-search-results__result-item"
(selectedChange)="addToCart($event)"
[selected]="isSelected(item)"
[selectable]="isSelectable(item)"
[item]="item"
[primaryOutletActive]="false"
[isActive]="rla.isActive"
></search-result-item>
</a>
<page-search-result-item-loading [primaryOutletActive]="false" *ngIf="fetching$ | async"></page-search-result-item-loading>
</cdk-virtual-scroll-viewport>
<div class="actions z-sticky h-0">
<button
[disabled]="loading$ | async"
*ngIf="(selectedItemIds$ | async)?.length > 0"
class="cta-cart cta-action-primary"
(click)="addToCart()"
>
<ui-spinner [show]="loading$ | async">In den Warenkorb legen</ui-spinner>
</button>
</div>
</ng-template>

View File

@@ -1,5 +1,5 @@
:host {
@apply box-border grid h-split-screen-tablet desktop-small:h-split-screen-desktop;
@apply box-border grid h-split-screen-tablet desktop-small:h-split-screen-desktop relative;
grid-template-rows: auto auto 1fr;
}
@@ -8,7 +8,7 @@
}
.page-search-results__result-item {
@apply mb-px-10;
@apply mb-[0.625rem];
}
.page-search-results__result-item-primary {

View File

@@ -9,6 +9,7 @@ import {
QueryList,
TrackByFunction,
AfterViewInit,
inject,
} from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { ApplicationService } from '@core/application';
@@ -19,7 +20,7 @@ import { ItemDTO } from '@swagger/cat';
import { AddToShoppingCartDTO } from '@swagger/checkout';
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
import { CacheService } from 'apps/core/cache/src/public-api';
import { isEqual } from 'lodash';
import { debounce, isEqual } from 'lodash';
import { BehaviorSubject, combineLatest, Subscription } from 'rxjs';
import { debounceTime, first, map, switchMap, withLatestFrom } from 'rxjs/operators';
import { ArticleSearchService } from '../article-search.store';
@@ -28,6 +29,8 @@ import { SearchResultItemComponent } from './search-result-item.component';
import { ProductCatalogNavigationService } from '@shared/services';
import { Filter, FilterInputGroupMainComponent } from 'apps/shared/components/filter/src/lib';
import { DomainAvailabilityService, ItemData } from '@domain/availability';
import { asapScheduler } from 'rxjs';
import { ShellService } from '@shared/shell';
@Component({
selector: 'page-search-results',
@@ -37,7 +40,7 @@ import { DomainAvailabilityService, ItemData } from '@domain/availability';
})
export class ArticleSearchResultsComponent implements OnInit, OnDestroy, AfterViewInit {
@ViewChildren(SearchResultItemComponent) listItems: QueryList<SearchResultItemComponent>;
@ViewChild('scrollContainer', { static: true })
@ViewChild(CdkVirtualScrollViewport, { static: false })
scrollContainer: CdkVirtualScrollViewport;
@ViewChild(FilterInputGroupMainComponent, { static: false })
@@ -59,6 +62,10 @@ export class ArticleSearchResultsComponent implements OnInit, OnDestroy, AfterVi
})
);
getProcessId(): number {
return this.application.activatedProcessId;
}
loading$ = new BehaviorSubject<boolean>(false);
private subscriptions = new Subscription();
@@ -92,21 +99,11 @@ export class ArticleSearchResultsComponent implements OnInit, OnDestroy, AfterVi
return this._environment.matchDesktop$.pipe(map((matches) => matches && this.route.outlet === 'primary'));
}
// Ticket #4169 Splitscreen
// Render genug Artikel um bei Navigation auf Trefferliste | PDP zum angewählten Artikel zu Scrollen
maxBufferCdkScrollContainer$ = this.results$.pipe(
map((results) => {
if (this._environment.matchDesktop() && this.route.outlet === 'side' && results?.length > 0) {
// Splitscreen mode: Items Length * Item Pixel Height
const maxBufferSize = results.length * 181;
return maxBufferSize >= 1200 ? maxBufferSize : 1200;
} else if (this._environment.matchTablet()) {
return 500;
} else {
return 1200;
}
})
);
private readonly SCROLL_INDEX_TOKEN = 'CATALOG_RESULTS_LIST_SCROLL_INDEX';
shellService = inject(ShellService);
scale$ = this.shellService.scale$;
constructor(
public searchService: ArticleSearchService,
@@ -157,9 +154,8 @@ export class ArticleSearchResultsComponent implements OnInit, OnDestroy, AfterVi
const cleanQueryParams = this.cleanupQueryParams(queryParams);
// Scroll to scroll_position in great result list
if (!!queryParams?.scroll_position && this.route.outlet === 'primary') {
this.scrollTop(Number(queryParams.scroll_position ?? 0));
if (this.route.outlet === 'primary' && processChanged) {
this.scrollToItem(this._getScrollIndexFromCache());
}
if (!isEqual(cleanQueryParams, this.cleanupQueryParams(this.searchService.filter.getQueryParams()))) {
@@ -175,11 +171,6 @@ export class ArticleSearchResultsComponent implements OnInit, OnDestroy, AfterVi
) {
this.search({ clear: true });
} else {
if (!this.isDesktopLarge || this.route.outlet === 'primary') {
this.scrollTop(Number(queryParams.scroll_position ?? 0));
} else {
this.scrollItemIntoView();
}
const selectedItemIds: Array<string> = queryParams?.selected_item_ids?.split(',') ?? [];
for (const id of selectedItemIds) {
if (id) {
@@ -272,7 +263,41 @@ export class ArticleSearchResultsComponent implements OnInit, OnDestroy, AfterVi
}
ngAfterViewInit(): void {
this.scrollItemIntoView();
this.scrollToItem(this._getScrollIndexFromCache());
}
private _addScrollIndexToCache(index: number): void {
this.cache.set<number>({ processId: this.getProcessId(), token: this.SCROLL_INDEX_TOKEN }, index);
}
private _getScrollIndexFromCache(): number {
return this.cache.get<number>({ processId: this.getProcessId(), token: this.SCROLL_INDEX_TOKEN });
}
scrollToItem(i?: number) {
let index = i;
if (!index) {
index = this._getScrollIndexFromCache();
} else {
this._addScrollIndexToCache(index);
}
asapScheduler.schedule(() => {
this.scrollContainer.scrollToIndex(index, 'smooth');
}, 150);
}
scrolledIndexChange(index: number) {
const completeListFetched = this.searchService.items.length === this.searchService.hits;
if (index && !completeListFetched && this.searchService.items.length <= this.scrollContainer?.getRenderedRange()?.end) {
this.search({ clear: false });
}
if (this.getProcessId() === this.searchService.processId) {
this._addScrollIndexToCache(index);
}
}
async ngOnDestroy() {
@@ -306,40 +331,14 @@ export class ArticleSearchResultsComponent implements OnInit, OnDestroy, AfterVi
this.searchService.search({ clear, orderBy, doNotTrack: true });
}
scrollTop(scrollPos: number) {
setTimeout(() => this.scrollContainer.scrollTo({ top: scrollPos }), 0);
}
scrollItemIntoView() {
setTimeout(() => {
const item = this.listItems?.find((item) => item.item.id === Number(this.route?.snapshot?.params?.id));
item?.scrollIntoView();
}, 0);
}
async scrolledIndexChange(index: number) {
alert('scrolledIndexChange');
const results = await this.results$.pipe(first()).toPromise();
const hits = await this.hits$.pipe(first()).toPromise();
if (results.length >= hits) {
return;
}
if (this.route.outlet === 'primary') {
this.searchService.setScrollPosition(this.scrollContainer.measureScrollOffset('top'));
}
if (index >= results.length - 20 && results.length - 20 > 0) {
this.search({ clear: false });
}
getDetailsPath(itemId: number) {
return this._navigationService.getArticleDetailsPath({ processId: this.application.activatedProcessId, itemId }).path;
}
async updateBreadcrumbs(
processId: number = this.searchService.processId,
queryParams: Record<string, string> = this.searchService.filter?.getQueryParams()
) {
const scroll_position = this.searchService.scrollPosition;
const selected_item_ids = this.searchService?.selectedItemIds?.toString();
if (queryParams) {
@@ -349,7 +348,7 @@ export class ArticleSearchResultsComponent implements OnInit, OnDestroy, AfterVi
.toPromise();
const name = queryParams.main_qs ? queryParams.main_qs : 'Alle Artikel';
const params = { ...queryParams, scroll_position, selected_item_ids };
const params = { ...queryParams, selected_item_ids };
for (const crumb of crumbs) {
this.breadcrumb.patchBreadcrumb(crumb.id, {
@@ -402,7 +401,6 @@ export class ArticleSearchResultsComponent implements OnInit, OnDestroy, AfterVi
cleanupQueryParams(params: Record<string, string> = {}) {
const clean = { ...params };
delete clean['scroll_position'];
delete clean['selected_item_ids'];
for (const key in clean) {

View File

@@ -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 } from 'lodash';
import { isNull, merge } from 'lodash';
import { BehaviorSubject, Observable, of, Subject } from 'rxjs';
import {
first,
@@ -229,7 +229,33 @@ export abstract class AbstractCreateCustomer implements OnInit, OnDestroy {
const customerId = this.formData?._meta?.customerDto?.id ?? this.formData?._meta?.customerInfoDto?.id;
return this.customerService.checkLoyaltyCard({ loyaltyCardNumber: value, customerId }).pipe(
map((response) => {
return !response?.error && (response as any)?.result === 1 ? null : { invalid: 'Kundenkartencode ist ungültig' };
if (response.error) {
throw response.message;
}
/**
* #4485 Kubi // Verhalten mit angelegte aber nicht verknüpfte Kundenkartencode in Kundensuche und Kundendaten erfassen ist nicht gleich
* Fall1: Kundenkarte hat Daten in point4more:
* Sobald Kundenkartencode in Feld "Kundenkartencode" reingegeben wird- werden die Daten von point4more in Formular "Kundendaten Erfassen" eingefügt und ersetzen (im Ganzen, nicht inkremental) die Daten in Felder, falls welche schon reingetippt werden.
* Fall2: Kundenkarte hat keine Daten in point4more:
* Sobald Kundenkartencode in Feld "Kundenkartencode" reingegeben wird- bleiben die Daten in Formular "Kundendaten Erfassen" in Felder, falls welche schon reingetippt werden.
*/
if (response.result && response.result.customer) {
const customer = response.result.customer;
const data = mapCustomerInfoDtoToCustomerCreateFormData(customer);
if (data.name.firstName && data.name.lastName) {
// Fall1
this._formData.next(data);
} else {
// Fall2 Hier müssen die Metadaten gesetzt werden um eine verknüfung zur kundenkarte zu ermöglichen.
const current = this.formData;
current._meta = data._meta;
current.p4m = data.p4m;
}
}
return null;
}),
catchError((error) => {
if (error instanceof HttpErrorResponse) {
@@ -242,31 +268,13 @@ export abstract class AbstractCreateCustomer implements OnInit, OnDestroy {
})
);
}),
tap(async (result) => {
tap(() => {
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 });

View File

@@ -39,7 +39,7 @@
<div class="customer-details-customer-main-data px-5 py-3 grid grid-flow-row gap-3">
<div class="flex flex-row">
<div class="data-label">Erstellungsdatum</div>
<div class="data-value">{{ created$ | async | date: 'dd.MM.yyyy' }} | {{ created$ | async | date: 'hh:mm' }} Uhr</div>
<div class="data-value">{{ created$ | async | date: 'dd.MM.yyyy' }} | {{ created$ | async | date: 'HH:mm' }} Uhr</div>
</div>
<div class="flex flex-row">
<div class="data-label">Kundennummer</div>

View File

@@ -12,6 +12,7 @@ import { isEmpty } from 'lodash';
import { DomainOmsService } from '@domain/oms';
import { OrderDTO, OrderListItemDTO } from '@swagger/oms';
import { hash } from '@utils/common';
import { UiModalService } from '@ui/modal';
@Injectable()
export class CustomerSearchStore extends ComponentStore<CustomerSearchState> implements OnStoreInit, OnDestroy {
@@ -163,7 +164,7 @@ export class CustomerSearchStore extends ComponentStore<CustomerSearchState> imp
selectedOrderItem$ = this.select(S.selectSelectedOrderItem);
constructor(private _customerService: CrmCustomerService, private _omsService: DomainOmsService) {
constructor(private _customerService: CrmCustomerService, private _omsService: DomainOmsService, private _modal: UiModalService) {
super({ customerListCount: 0 });
}
@@ -205,7 +206,8 @@ export class CustomerSearchStore extends ComponentStore<CustomerSearchState> imp
};
handleSelectCustomerError = (err: any) => {
console.error(err);
this._modal.error('Fehler beim Auswählen des Kundens', err);
this.patchState({ fetchingCustomer: false });
};
handleSelectCustomerComplete = () => {
@@ -230,7 +232,8 @@ export class CustomerSearchStore extends ComponentStore<CustomerSearchState> imp
};
handleSelectOrderError = (err: any) => {
console.error(err);
this._modal.error('Fehler beim Auswählen der Bestellung', err);
this.patchState({ fetchingOrder: false });
};
handleSelectOrderComplete = () => {
@@ -259,7 +262,8 @@ export class CustomerSearchStore extends ComponentStore<CustomerSearchState> imp
};
handleFetchCustomerOrdersError = (err: any) => {
console.error(err);
this._modal.error('Fehler beim Laden der Kundenbestellungen', err);
this.patchState({ fetchingCustomerOrders: false });
};
handleFetchCustomerOrdersComplete = () => {
@@ -282,7 +286,8 @@ export class CustomerSearchStore extends ComponentStore<CustomerSearchState> imp
};
handleFetchFilterError = (err: any) => {
console.error(err);
this._modal.error('Fehler beim Laden der Filter', err);
this.patchState({ fetchingFilter: false });
};
handleFetchFilterComplete = () => {
@@ -341,7 +346,8 @@ export class CustomerSearchStore extends ComponentStore<CustomerSearchState> imp
};
handleSearchError = (err: any) => {
console.error(err);
this._modal.error('Fehler beim Laden der Liste', err);
this.patchState({ fetchingCustomerList: false });
};
handleSearchComplete = () => {

View File

@@ -12,6 +12,7 @@ import { RunCheckTrigger } from './trigger';
import { OrderItemsContext } from '@domain/oms';
import { ActionHandlerService } from './services/action-handler.service';
import { Config } from '@core/config';
import { debounce } from '@utils/common';
export type GetNameForBreadcrumbData = {
processId: number;
@@ -64,6 +65,9 @@ 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)
private _runChecks() {
const processId = this._checkAndUpdateProcessId();
const queryParams = this._checkAndUpdateQueryParams();

View File

@@ -38,6 +38,10 @@ 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,

View File

@@ -1,5 +1,8 @@
:host {
@apply box-border grid overflow-y-scroll h-split-screen-tablet desktop-small:h-split-screen-desktop;
@apply box-border overflow-y-scroll h-split-screen-tablet desktop-small:h-split-screen-desktop;
}
.page-pickup-shelf-in-details__container {
grid-template-rows: auto 1fr;
}

View File

@@ -1,56 +1,75 @@
<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="grid overflow-y-scroll h-full" [class.page-pickup-shelf-in-details__container]="!(viewFetching$ | async)">
<div *ngIf="noOrderItemsFound$ | async" class="text-center text-2xl font-bold bg-white rounded px-4 py-8">
<h4>Posten wurde nicht gefunden</h4>
</div>
<div class="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 *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>

View File

@@ -16,6 +16,7 @@ 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',
@@ -32,6 +33,7 @@ import { RunCheckTrigger } from '../../trigger';
PickUpShelfDetailsItemComponent,
PickUpShelfDetailsTagsComponent,
PickUpShelfDetailsCoversComponent,
PickUpShelfDetailsItemsGroupComponent,
PickupShelfAddToPreviousCompartmentCodeLabelPipe,
UiSpinnerModule,
OnInitDirective,
@@ -52,6 +54,12 @@ 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;
@@ -68,6 +76,8 @@ 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$;

View File

@@ -34,14 +34,3 @@ page-pickup-shelf-list-item {
.cta-action-secondary {
@apply bg-white text-brand;
}
::ng-deep page-pickup-shelf-in-list ui-scroll-container {
height: 100% !important;
overflow: hidden !important;
}
::ng-deep page-pickup-shelf-in-list ui-scroll-container .scroll-container {
height: 100% !important;
max-height: 100% !important;
gap: 0.625rem !important;
}

View File

@@ -46,7 +46,7 @@
die bearbeitet werden können.
</div>
<div
class="page-pickup-shelf-in-list__items-list"
class="page-pickup-shelf-in-list__items-list mb-[0.625rem]"
*ngFor="let bueryNumberGroup of list$ | async | groupBy: byBuyerNumberFn; trackBy: trackByGroupFn"
>
<ng-container *ngIf="bueryNumberGroup.items[0]; let firstItem">
@@ -80,42 +80,3 @@
</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> -->

View File

@@ -121,19 +121,21 @@ export class PickUpShelfInListComponent implements OnInit, AfterViewInit {
) {}
ngOnInit() {
this.store.processId$.pipe(takeUntilDestroyed(this.destroyRef), debounceTime(150)).subscribe((_) => {
if (!this.store.list.length) {
this.store.fetchList();
}
combineLatest([this.store.processId$, this._activatedRoute.queryParams])
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(([_, queryParams]) => {
if (!this.store.list.length || !isEqual(queryParams, this.store.filter.getQueryParams())) {
this.store.fetchList();
}
const scrollPos = this._getScrollPositionFromCache();
if (!!scrollPos && 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) {

View File

@@ -1,5 +1,8 @@
:host {
@apply box-border grid overflow-y-scroll h-split-screen-tablet desktop-small:h-split-screen-desktop;
@apply box-border overflow-y-scroll h-split-screen-tablet desktop-small:h-split-screen-desktop;
}
.page-pickup-shelf-out-details__container {
grid-template-rows: auto 1fr;
}

View File

@@ -1,36 +1,55 @@
<div class="mb-4">
<page-pickup-shelf-details-header
[processId]="processId"
(handleAction)="handleAction($event)"
(updateDate)="updateDate($event)"
(editClick)="navigateToEditPage($event)"
></page-pickup-shelf-details-header>
<page-pickup-shelf-details-item
class="mb-px-2"
*ngFor="let item of orderItems$ | async; trackBy: trackByFnDBHOrderItemListItemDTO"
[orderItem]="item"
[selected]="true"
(historyClick)="navigateToHistoryPage($event)"
[order]="order$ | async"
(specialCommentChanged)="updateSpecialComment(item, $event)"
(sharedOnInit)="fetchNotifications(item)"
></page-pickup-shelf-details-item>
<page-pickup-shelf-details-tags
*ngIf="showTagsComponent$ | async"
[ngModel]="selectedCompartmentInfo$ | async"
(ngModelChange)="setSelectedCompartmentInfo($event)"
></page-pickup-shelf-details-tags>
</div>
<div class="grid overflow-y-scroll h-full" [class.page-pickup-shelf-out-details__container]="!(viewFetching$ | async)">
<ng-container *ngIf="viewFetching$ | async; else showView">
<div class="w-full flex flex-col justify-center items-center">
<ui-spinner [show]="true"></ui-spinner>
<div class="mt-6">Daten werden geladen ...</div>
</div>
</ng-container>
<div class="page-pickup-shelf-out-details__action-wrapper">
<button
[disabled]="actionsDisabled$ | async"
class="cta-action shadow-action"
[class.cta-action-primary]="action.selected"
[class.cta-action-secondary]="!action.selected"
*ngFor="let action of mainActions$ | async"
(click)="handleAction(action)"
>
<ui-spinner [show]="(changeActionLoader$ | async) === action.command">{{ action.label }}</ui-spinner>
</button>
<ng-template #showView>
<div class="mb-4">
<page-pickup-shelf-details-header
[processId]="processId"
(handleAction)="handleAction($event)"
(updateDate)="updateDate($event)"
(editClick)="navigateToEditPage($event)"
></page-pickup-shelf-details-header>
<ng-container *ngFor="let group of groupedItems$ | async; trackBy: trackByFnGroupDBHOrderItemListItemDTO">
<page-pickup-shelf-details-items-group
[orderType]="group.type"
[groupedItems]="group.items"
></page-pickup-shelf-details-items-group>
<page-pickup-shelf-details-item
class="mb-px-2"
*ngFor="let item of group.items; trackBy: trackByFnDBHOrderItemListItemDTO"
[orderItem]="item"
[selected]="true"
(historyClick)="navigateToHistoryPage($event)"
[order]="order$ | async"
(specialCommentChanged)="updateSpecialComment(item, $event)"
(sharedOnInit)="fetchNotifications(item)"
></page-pickup-shelf-details-item>
</ng-container>
<page-pickup-shelf-details-tags
*ngIf="showTagsComponent$ | async"
[ngModel]="selectedCompartmentInfo$ | async"
(ngModelChange)="setSelectedCompartmentInfo($event)"
></page-pickup-shelf-details-tags>
</div>
<div class="page-pickup-shelf-out-details__action-wrapper">
<button
[disabled]="actionsDisabled$ | async"
class="cta-action shadow-action"
[class.cta-action-primary]="action.selected"
[class.cta-action-secondary]="!action.selected"
*ngFor="let action of mainActions$ | async"
(click)="handleAction(action)"
>
<ui-spinner [show]="(changeActionLoader$ | async) === action.command">{{ action.label }}</ui-spinner>
</button>
</div>
</ng-template>
</div>

View File

@@ -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, OrderItemProcessingStatusValue } from '@swagger/oms';
import { DBHOrderItemListItemDTO, KeyValueDTOOfStringAndString } from '@swagger/oms';
import { PickUpShelfOutNavigationService } from '@shared/services';
import { BehaviorSubject, asapScheduler } from 'rxjs';
import { BehaviorSubject, Observable, asapScheduler, combineLatest } from 'rxjs';
import { map } from 'rxjs/operators';
import { PickUpShelfDetailsTagsComponent } from '../../shared/pickup-shelf-details-tags/pickup-shelf-details-tags.component';
import { UiSpinnerModule } from '@ui/spinner';
@@ -13,6 +13,7 @@ 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',
@@ -28,6 +29,7 @@ import { RunCheckTrigger } from '../../trigger';
PickUpShelfDetailsHeaderComponent,
PickUpShelfDetailsItemComponent,
PickUpShelfDetailsTagsComponent,
PickUpShelfDetailsItemsGroupComponent,
UiSpinnerModule,
OnInitDirective,
FormsModule,
@@ -44,9 +46,30 @@ export class PickupShelfOutDetailsComponent extends PickupShelfDetailsBaseCompon
order$ = this.store.order$;
orderItems$ = this.store.orderItems$;
groupedItems$: Observable<Array<{ type: string; items: DBHOrderItemListItemDTO[] }>> = this.store.orderItems$.pipe(
map((items) => {
const groups: Array<{ type: string; items: DBHOrderItemListItemDTO[] }> = [];
// New Set to remove duplicates
const types = Array.from(new Set(items.map((item) => item?.features?.orderType)));
for (let type of types) {
const filteredItemsByType = items.filter((item) => item?.features?.orderType === type);
if (!!type && filteredItemsByType.length > 0) {
// Add items to matching orderType group
groups.push({ type, items: filteredItemsByType });
}
}
return groups;
})
);
fetching$ = this.store.fetchingOrder$;
fetchingItems$ = this.store.fetchingOrderItems$;
viewFetching$ = combineLatest([this.fetching$, this.fetchingItems$]).pipe(map(([fetching, fetchingItems]) => fetching || fetchingItems));
selectedCompartmentInfo = this.store.selectedCompartmentInfo;
@@ -63,6 +86,8 @@ export class PickupShelfOutDetailsComponent extends PickupShelfDetailsBaseCompon
mainActions$ = this.store.mainActions$;
trackByFnGroupDBHOrderItemListItemDTO = (index: number, group: { type: string; items: DBHOrderItemListItemDTO[] }) => group.type;
trackByFnDBHOrderItemListItemDTO = (index: number, item: DBHOrderItemListItemDTO) => item.orderItemSubsetId;
get processId() {

View File

@@ -34,14 +34,3 @@ page-pickup-shelf-list-item {
.cta-action-secondary {
@apply bg-white text-brand;
}
::ng-deep page-pcikup-shelf-out-list ui-scroll-container {
height: 100% !important;
overflow: hidden !important;
}
::ng-deep page-pcikup-shelf-out-list ui-scroll-container .scroll-container {
height: 100% !important;
max-height: 100% !important;
gap: 0.625rem !important;
}

View File

@@ -46,7 +46,7 @@
die bearbeitet werden können.
</div>
<div
class="page-pickup-shelf-out-list__items-list w-full"
class="page-pickup-shelf-out-list__items-list w-full mb-[0.625rem]"
*ngFor="let bueryNumberGroup of list$ | async | groupBy: byBuyerNumberFn; trackBy: trackByGroupFn"
>
<ng-container *ngIf="bueryNumberGroup.items[0]; let firstItem">
@@ -94,76 +94,3 @@
</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> -->

View File

@@ -9,12 +9,13 @@ import { NavigationRoute, PickUpShelfOutNavigationService } from '@shared/servic
import { AsyncPipe } from '@angular/common';
import { DBHOrderItemListItemDTO } from '@swagger/oms';
import { Observable, of } from 'rxjs';
import { take } from 'rxjs/operators';
import { skipWhile, take } from 'rxjs/operators';
import { provideActionHandlers } from '@core/command';
import { ActionHandlerServices } from '@domain/oms';
import { ActionHandlerService } from '../services/action-handler.service';
import { FilterAutocompleteProvider } from '@shared/components/filter';
import { PickUpShelfOutAutocompleteProvider } from './providers/pickup-shelf-out-autocomplete.provider';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({
selector: 'page-pickup-shelf-out',
@@ -62,12 +63,44 @@ export class PickupShelfOutComponent extends PickupShelfBaseComponent {
return this._pickupShelfOutNavigationService.defaultRoute({ processId: data.processId });
}
getNameForListBreadcrumb(data: GetNameForBreadcrumbData): string | Promise<string> {
if (data.queryParams?.main_qs) {
return data.queryParams.main_qs;
private _getLatestList(): Promise<DBHOrderItemListItemDTO[]> {
return this.listStore.list$
.pipe(
takeUntilDestroyed(this.destroyRef),
skipWhile((list) => list.length === 0),
take(1)
)
.toPromise();
}
async getNameForListBreadcrumb(data: GetNameForBreadcrumbData): Promise<string> {
const mainQs = data.queryParams?.main_qs;
if (!mainQs) {
return 'Alle';
}
return 'Alle';
/**
* #4533
* Wenn ein Abholschein gescennt wird dann beinhaltet dieser einen Prefix "ORD:".
* Wenn dieser Prefix vorhanden ist, dann soll der Name des Kunden angezeigt werden.
*/
if (mainQs.startsWith('ORD:')) {
const items = await this._getLatestList();
const item = items[0];
if (item) {
let name = `${item.lastName} ${item.firstName}`.trim();
if (item.organisation) {
name = name.length === 0 ? item.organisation : `${name} - ${item.organisation}`;
}
return name;
}
}
return mainQs;
}
getPathFoListBreadcrumb(data: GetPathForBreadcrumbData): NavigationRoute | Promise<NavigationRoute> {

View File

@@ -145,96 +145,6 @@
</div>
</div>
</div>
<div class="flex flex-row items-center relative bg-[#F5F7FA] p-4 rounded-t">
<div *ngIf="showFeature" class="flex flex-row items-center mr-3">
<ng-container [ngSwitch]="order.features.orderType">
<ng-container *ngSwitchCase="'Versand'">
<div class="flex items-center justify-center bg-[#D8DFE5] w-[2.25rem] h-[2.25rem] rounded rounded-br-none mr-2">
<shared-icon [size]="24" icon="isa-truck"></shared-icon>
</div>
<p class="font-bold text-p1">Versand</p>
</ng-container>
<ng-container *ngSwitchCase="'DIG-Versand'">
<div class="flex items-center justify-center bg-[#D8DFE5] w-[2.25rem] h-[2.25rem] rounded rounded-br-none mr-2">
<shared-icon [size]="24" icon="isa-truck"></shared-icon>
</div>
<p class="font-bold text-p1">Versand</p>
</ng-container>
<ng-container *ngSwitchCase="'B2B-Versand'">
<div class="flex items-center justify-center bg-[#D8DFE5] w-[2.25rem] h-[2.25rem] rounded rounded-br-none mr-2">
<shared-icon [size]="24" icon="isa-b2b-truck"></shared-icon>
</div>
<p class="font-bold text-p1">B2B-Versand</p>
</ng-container>
<ng-container *ngSwitchCase="'Abholung'">
<div class="flex items-center justify-center bg-[#D8DFE5] w-[2.25rem] h-[2.25rem] rounded rounded-br-none mr-2">
<shared-icon [size]="24" icon="isa-box-out"></shared-icon>
</div>
<p class="font-bold text-p1 mr-3">Abholung</p>
{{ orderItem.targetBranch }}
</ng-container>
<ng-container *ngSwitchCase="'Rücklage'">
<div class="flex items-center justify-center bg-[#D8DFE5] w-[2.25rem] h-[2.25rem] rounded rounded-br-none mr-2">
<shared-icon [size]="24" icon="isa-shopping-bag"></shared-icon>
</div>
<p class="font-bold text-p1">Rücklage</p>
</ng-container>
<ng-container *ngSwitchCase="'Download'">
<div class="flex items-center justify-center bg-[#D8DFE5] w-[2.25rem] h-[2.25rem] rounded rounded-br-none mr-2">
<shared-icon [size]="24" icon="isa-download"></shared-icon>
</div>
<p class="font-bold text-p1">Download</p>
</ng-container>
</ng-container>
</div>
<div class="page-pickup-shelf-details-header__additional-addresses" *ngIf="showAddresses">
<button (click)="openAddresses = !openAddresses" class="text-[#0556B4]">
Lieferadresse / Rechnungsadresse {{ openAddresses ? 'ausblenden' : 'anzeigen' }}
</button>
<div class="page-pickup-shelf-details-header__addresses-popover" *ngIf="openAddresses">
<button (click)="openAddresses = !openAddresses" class="close">
<shared-icon icon="close" [size]="24"></shared-icon>
</button>
<div class="page-pickup-shelf-details-header__addresses-popover-data">
<div *ngIf="order.shipping" class="page-pickup-shelf-details-header__addresses-popover-delivery">
<p>Lieferadresse</p>
<div class="page-pickup-shelf-details-header__addresses-popover-delivery-data">
<ng-container *ngIf="order.shipping?.data?.organisation">
<p>{{ order.shipping?.data?.organisation?.name }}</p>
<p>{{ order.shipping?.data?.organisation?.department }}</p>
</ng-container>
<p>{{ order.shipping?.data?.firstName }} {{ order.shipping?.data?.lastName }}</p>
<p>{{ order.shipping?.data?.address?.info }}</p>
<p>{{ order.shipping?.data?.address?.street }} {{ order.shipping?.data?.address?.streetNumber }}</p>
<p>{{ order.shipping?.data?.address?.zipCode }} {{ order.shipping?.data?.address?.city }}</p>
</div>
</div>
<div *ngIf="order.billing" class="page-pickup-shelf-details-header__addresses-popover-billing">
<p>Rechnungsadresse</p>
<div class="page-pickup-shelf-details-header__addresses-popover-billing-data">
<ng-container *ngIf="order.billing?.data?.organisation">
<p>{{ order.billing?.data?.organisation?.name }}</p>
<p>{{ order.billing?.data?.organisation?.department }}</p>
</ng-container>
<p>{{ order.billing?.data?.firstName }} {{ order.billing?.data?.lastName }}</p>
<p>{{ order.billing?.data?.address?.info }}</p>
<p>{{ order.billing?.data?.address?.street }} {{ order.billing?.data?.address?.streetNumber }}</p>
<p>{{ order.billing?.data?.address?.zipCode }} {{ order.billing?.data?.address?.city }}</p>
</div>
</div>
</div>
</div>
</div>
<div class="page-pickup-shelf-details-header__select grow" *ngIf="showMultiselect$ | async">
<button class="cta-select-all" (click)="selectAll()">Alle auswählen</button>
{{ selectedOrderItemCount$ | async }} von {{ orderItemCount$ | async }} Titeln
</div>
</div>
</div>
<ng-template #featureLoading>

View File

@@ -81,51 +81,6 @@
}
}
.page-pickup-shelf-details-header__select {
@apply flex flex-col items-end;
}
.page-pickup-shelf-details-header__additional-addresses {
.page-pickup-shelf-details-header__addresses-popover {
@apply absolute inset-x-0 top-16 bottom-0 z-popover;
.close {
@apply bg-white absolute right-0 p-6;
}
.page-pickup-shelf-details-header__addresses-popover-data {
@apply flex flex-col bg-white p-6 z-popover min-h-[200px];
box-shadow: 0px 6px 24px rgba(206, 212, 219, 0.8);
.page-pickup-shelf-details-header__addresses-popover-delivery {
@apply grid mb-6;
grid-template-columns: 9.5625rem auto;
.page-pickup-shelf-details-header__addresses-popover-delivery-data {
p {
@apply font-bold;
}
}
}
.page-pickup-shelf-details-header__addresses-popover-billing {
@apply grid;
grid-template-columns: 9.5625rem auto;
.page-pickup-shelf-details-header__addresses-popover-billing-data {
p {
@apply font-bold;
}
}
}
}
}
}
.cta-select-all {
@apply text-brand bg-transparent text-p2 font-bold outline-none border-none;
}
.fetch-wrapper {
@apply grid grid-flow-col gap-4;
}

View File

@@ -92,10 +92,6 @@ export class PickUpShelfDetailsHeaderComponent {
minDateDatepicker = this.dateAdapter.addCalendarDays(this.dateAdapter.today(), -1);
today = this.dateAdapter.today();
selectedOrderItemCount$ = this._store.selectedOrderItemIds$.pipe(map((ids) => ids?.length ?? 0));
orderItemCount$ = this._store.orderItems$.pipe(map((items) => items?.length ?? 0));
orderItem$ = this._store.orderItems$.pipe(map((orderItems) => orderItems?.find((_) => true)));
changeDateLoader$ = new BehaviorSubject<boolean>(false);
@@ -115,14 +111,6 @@ export class PickUpShelfDetailsHeaderComponent {
statusActions$ = this.orderItem$.pipe(map((orderItem) => orderItem?.actions?.filter((action) => action.enabled === false)));
get isItemSelectable() {
return this._store.orderItems?.some((item) => !!item?.actions && item?.actions?.length > 0);
}
showMultiselect$ = combineLatest([this._store.orderItems$, this._store.fetchPartial$]).pipe(
map(([orderItems, fetchPartial]) => this.isItemSelectable && fetchPartial && orderItems?.length > 1)
);
crudaUpdate$ = this.orderItem$.pipe(map((orederItem) => !!(orederItem?.cruda & 4)));
editButtonDisabled$ = combineLatest([this.changeStatusLoader$, this.crudaUpdate$]).pipe(
@@ -133,20 +121,6 @@ export class PickUpShelfDetailsHeaderComponent {
map(([statusActions, crudaUpdate]) => statusActions?.length > 0 && crudaUpdate)
);
openAddresses: boolean = false;
get digOrderNumber(): string {
return this.order?.linkedRecords?.find((_) => true)?.number;
}
get showAddresses(): boolean {
return (this.order?.orderType === 2 || this.order?.orderType === 4) && (!!this.order?.shipping || !!this.order?.billing);
}
get showFeature(): boolean {
return !!this.order?.features && !!this.order?.features?.orderType;
}
constructor(private dateAdapter: DateAdapter, private cdr: ChangeDetectorRef) {}
async handleActionClick(action?: KeyValueDTOOfStringAndString) {
@@ -156,10 +130,6 @@ export class PickUpShelfDetailsHeaderComponent {
this.cdr.markForCheck();
}
selectAll() {
this._store.selectAllOrderItemIds();
}
updatePickupDeadline(date: Date) {
this.updateDate.emit({ date, type: 'pickup' });
}

View File

@@ -0,0 +1,22 @@
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'notificationType',
standalone: true,
})
export class NotificationTypePipe implements PipeTransform {
transform(notificationType: string): string {
switch (notificationType) {
case 'NOTIFICATION_EMAIL':
return 'Benachrichtigung';
case 'REMINDER_EMAIL':
return 'Erinnerung';
case 'ORDERCONFIRMATION_EMAIL':
return 'Bestellbestätigung';
case 'NOTIFICATION_SMS':
return 'Benachrichtigung';
default:
return notificationType;
}
}
}

View File

@@ -11,8 +11,10 @@
<img [uiOverlayTrigger]="emailTooltip" src="/assets/images/email_bookmark.svg" />
<ui-tooltip yPosition="above" xPosition="after" [yOffset]="-11" [xOffset]="-8" #emailTooltip [closeable]="true">
Per E-Mail benachrichtigt <br />
<ng-container *ngFor="let notification of emailNotificationDates$ | async">
{{ notification | date: 'dd.MM.yyyy | HH:mm' }} Uhr<br />
<ng-container *ngFor="let notifications of emailNotificationDates$ | async">
<ng-container *ngFor="let notificationDate of notifications.dates">
{{ notifications.type | notificationType }} {{ notificationDate | date: 'dd.MM.yyyy | HH:mm' }} Uhr<br />
</ng-container>
</ng-container>
</ui-tooltip>
</ng-container>
@@ -20,8 +22,10 @@
<img [uiOverlayTrigger]="smsTooltip" src="/assets/images/sms_bookmark.svg" />
<ui-tooltip yPosition="above" xPosition="after" [yOffset]="-11" [xOffset]="-8" #smsTooltip [closeable]="true">
Per SMS benachrichtigt <br />
<ng-container *ngFor="let notification of smsNotificationDates$ | async">
{{ notification | date: 'dd.MM.yyyy | HH:mm' }} Uhr<br />
<ng-container *ngFor="let notifications of smsNotificationDates$ | async">
<ng-container *ngFor="let notificationDate of notifications.dates">
{{ notificationDate | date: 'dd.MM.yyyy | HH:mm' }} Uhr<br />
</ng-container>
</ng-container>
</ui-tooltip>
</ng-container>

View File

@@ -24,6 +24,7 @@ import { map, switchMap } from 'rxjs/operators';
import { Subject, combineLatest } from 'rxjs';
import { PickupShelfDetailsStore } from '../../store';
import { UiQuantityDropdownModule } from '@ui/quantity-dropdown';
import { NotificationTypePipe } from './notification-type.pipe';
export interface PickUpShelfDetailsItemComponentState {
orderItem?: DBHOrderItemListItemDTO;
@@ -55,6 +56,7 @@ export interface PickUpShelfDetailsItemComponentState {
PickupShelfPaymentTypePipe,
IconModule,
UiQuantityDropdownModule,
NotificationTypePipe,
],
})
export class PickUpShelfDetailsItemComponent extends ComponentStore<PickUpShelfDetailsItemComponentState> implements OnInit {

View File

@@ -0,0 +1,89 @@
<div class="flex flex-row items-center relative bg-[#F5F7FA] p-4 rounded-t mb-[0.125rem]">
<div *ngIf="showFeature" class="flex flex-row items-center mr-3">
<ng-container [ngSwitch]="orderType">
<ng-container *ngSwitchCase="'Versand'">
<div class="flex items-center justify-center bg-[#D8DFE5] w-[2.25rem] h-[2.25rem] rounded rounded-br-none mr-2">
<shared-icon [size]="24" icon="isa-truck"></shared-icon>
</div>
<p class="font-bold text-p1">Versand</p>
</ng-container>
<ng-container *ngSwitchCase="'DIG-Versand'">
<div class="flex items-center justify-center bg-[#D8DFE5] w-[2.25rem] h-[2.25rem] rounded rounded-br-none mr-2">
<shared-icon [size]="24" icon="isa-truck"></shared-icon>
</div>
<p class="font-bold text-p1">Versand</p>
</ng-container>
<ng-container *ngSwitchCase="'B2B-Versand'">
<div class="flex items-center justify-center bg-[#D8DFE5] w-[2.25rem] h-[2.25rem] rounded rounded-br-none mr-2">
<shared-icon [size]="24" icon="isa-b2b-truck"></shared-icon>
</div>
<p class="font-bold text-p1">B2B-Versand</p>
</ng-container>
<ng-container *ngSwitchCase="'Abholung'">
<div class="flex items-center justify-center bg-[#D8DFE5] w-[2.25rem] h-[2.25rem] rounded rounded-br-none mr-2">
<shared-icon [size]="24" icon="isa-box-out"></shared-icon>
</div>
<p class="font-bold text-p1 mr-3">Abholung</p>
{{ targetBranches }}
</ng-container>
<ng-container *ngSwitchCase="'Rücklage'">
<div class="flex items-center justify-center bg-[#D8DFE5] w-[2.25rem] h-[2.25rem] rounded rounded-br-none mr-2">
<shared-icon [size]="24" icon="isa-shopping-bag"></shared-icon>
</div>
<p class="font-bold text-p1">Rücklage</p>
</ng-container>
<ng-container *ngSwitchCase="'Download'">
<div class="flex items-center justify-center bg-[#D8DFE5] w-[2.25rem] h-[2.25rem] rounded rounded-br-none mr-2">
<shared-icon [size]="24" icon="isa-download"></shared-icon>
</div>
<p class="font-bold text-p1">Download</p>
</ng-container>
</ng-container>
</div>
<div class="page-pickup-shelf-details-items-group__additional-addresses" *ngIf="showAddresses">
<button (click)="openAddresses = !openAddresses" class="text-[#0556B4]">
Lieferadresse / Rechnungsadresse {{ openAddresses ? 'ausblenden' : 'anzeigen' }}
</button>
<div class="page-pickup-shelf-details-items-group__addresses-popover" *ngIf="openAddresses">
<button (click)="openAddresses = !openAddresses" class="close">
<shared-icon icon="close" [size]="24"></shared-icon>
</button>
<div class="page-pickup-shelf-details-items-group__addresses-popover-data">
<div *ngIf="order.shipping" class="page-pickup-shelf-details-items-group__addresses-popover-delivery">
<p>Lieferadresse</p>
<div class="page-pickup-shelf-details-items-group__addresses-popover-delivery-data">
<ng-container *ngIf="order.shipping?.data?.organisation">
<p>{{ order.shipping?.data?.organisation?.name }}</p>
<p>{{ order.shipping?.data?.organisation?.department }}</p>
</ng-container>
<p>{{ order.shipping?.data?.firstName }} {{ order.shipping?.data?.lastName }}</p>
<p>{{ order.shipping?.data?.address?.info }}</p>
<p>{{ order.shipping?.data?.address?.street }} {{ order.shipping?.data?.address?.streetNumber }}</p>
<p>{{ order.shipping?.data?.address?.zipCode }} {{ order.shipping?.data?.address?.city }}</p>
</div>
</div>
<div *ngIf="order.billing" class="page-pickup-shelf-details-items-group__addresses-popover-billing">
<p>Rechnungsadresse</p>
<div class="page-pickup-shelf-details-items-group__addresses-popover-billing-data">
<ng-container *ngIf="order.billing?.data?.organisation">
<p>{{ order.billing?.data?.organisation?.name }}</p>
<p>{{ order.billing?.data?.organisation?.department }}</p>
</ng-container>
<p>{{ order.billing?.data?.firstName }} {{ order.billing?.data?.lastName }}</p>
<p>{{ order.billing?.data?.address?.info }}</p>
<p>{{ order.billing?.data?.address?.street }} {{ order.billing?.data?.address?.streetNumber }}</p>
<p>{{ order.billing?.data?.address?.zipCode }} {{ order.billing?.data?.address?.city }}</p>
</div>
</div>
</div>
</div>
</div>
<div class="page-pickup-shelf-details-items-group__select grow" *ngIf="showMultiselect$ | async">
<button class="cta-select-all" (click)="selectAll()">Alle auswählen</button>
{{ selectedOrderItemCount$ | async }} von {{ groupedItems.length }} Titeln
</div>
</div>

View File

@@ -0,0 +1,48 @@
:host {
@apply grid grid-flow-row;
}
.page-pickup-shelf-details-items-group__select {
@apply flex flex-col items-end;
}
.page-pickup-shelf-details-items-group__additional-addresses {
.page-pickup-shelf-details-items-group__addresses-popover {
@apply absolute inset-x-0 top-16 bottom-0 z-popover;
.close {
@apply bg-white absolute right-0 p-6;
}
.page-pickup-shelf-details-items-group__addresses-popover-data {
@apply flex flex-col bg-white p-6 z-popover min-h-[200px];
box-shadow: 0px 6px 24px rgba(206, 212, 219, 0.8);
.page-pickup-shelf-details-items-group__addresses-popover-delivery {
@apply grid mb-6;
grid-template-columns: 9.5625rem auto;
.page-pickup-shelf-details-items-group__addresses-popover-delivery-data {
p {
@apply font-bold;
}
}
}
.page-pickup-shelf-details-items-group__addresses-popover-billing {
@apply grid;
grid-template-columns: 9.5625rem auto;
.page-pickup-shelf-details-items-group__addresses-popover-billing-data {
p {
@apply font-bold;
}
}
}
}
}
}
.cta-select-all {
@apply text-brand bg-transparent text-p2 font-bold outline-none border-none;
}

View File

@@ -0,0 +1,65 @@
import { ChangeDetectionStrategy, Component, Input, OnInit, inject } from '@angular/core';
import { PickupShelfDetailsStore } from '../../store';
import { map } from 'rxjs/operators';
import { DBHOrderItemListItemDTO, OrderDTO } from '@swagger/oms';
import { AsyncPipe, NgFor, NgIf, NgSwitch, NgSwitchCase } from '@angular/common';
import { IconModule } from '@shared/components/icon';
@Component({
selector: 'page-pickup-shelf-details-items-group',
templateUrl: 'pickup-shelf-details-items-group.component.html',
styleUrls: ['pickup-shelf-details-items-group.component.scss'],
standalone: true,
host: { class: 'page-pickup-shelf-details-items-group' },
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [NgIf, NgFor, NgSwitch, NgSwitchCase, IconModule, AsyncPipe],
})
export class PickUpShelfDetailsItemsGroupComponent implements OnInit {
private _store = inject(PickupShelfDetailsStore);
get order(): OrderDTO {
return this._store.order;
}
@Input() orderType: string;
@Input() groupedItems: DBHOrderItemListItemDTO[];
get firstGroupedItem() {
return this.groupedItems?.find((_) => true);
}
openAddresses: boolean = false;
get showAddresses(): boolean {
return (this.order?.orderType === 2 || this.order?.orderType === 4) && (!!this.order?.shipping || !!this.order?.billing);
}
get showFeature(): boolean {
return !!this.firstGroupedItem?.features && !!this.firstGroupedItem?.features?.orderType;
}
get targetBranches(): string {
return Array.from(new Set(this.groupedItems?.map((item) => item?.targetBranch))).join('; ');
}
get isItemSelectable() {
return this.groupedItems?.some((item) => !!item?.actions && item?.actions?.length > 0);
}
showMultiselect$ = this._store.fetchPartial$.pipe(
map((fetchPartial) => this.isItemSelectable && fetchPartial && this.groupedItems?.length > 1)
);
selectedOrderItemCount$ = this._store.selectedOrderItemIds$.pipe(
map((ids) => this.groupedItems?.filter((groupedItem) => ids?.includes(groupedItem?.orderItemSubsetId))?.length ?? 0)
);
constructor() {}
ngOnInit() {}
selectAll() {
this._store.selectAllOrderItemIds();
}
}

View File

@@ -166,22 +166,33 @@ export const selectNotifications = (orderItemSubsetId: number) => (s: PickupShel
}, {} as Record<string, Date[]>);
};
export const selectLatestNotificationDatesFor = (orderItemSubsetId: number, key: string) => (s: PickupShelfDetailsState) => {
export const selectLatestNotificationDatesFor = (orderItemSubsetId: number, keys: string[]) => (s: PickupShelfDetailsState) => {
const notifications = selectNotifications(orderItemSubsetId)(s);
return (
notifications?.[key]?.filter((date) => {
let dates: Array<{ type: string; dates: Date[] }> = [];
for (const key of keys) {
const notification = notifications?.[key] ?? [];
const validDates = notification.filter((date) => {
// check if curr is an invalid date
return !isNaN(date.getTime());
}) ?? []
);
});
const mappedDates = { type: key, dates: validDates };
if (mappedDates.dates?.length > 0) {
dates.push(mappedDates);
}
}
return dates;
};
export const selectLatestEmailNotificationDates = (orderItemSubsetId: number) => (s: PickupShelfDetailsState) => {
return selectLatestNotificationDatesFor(orderItemSubsetId, 'NOTIFICATION_EMAIL')(s);
return selectLatestNotificationDatesFor(orderItemSubsetId, ['NOTIFICATION_EMAIL', 'REMINDER_EMAIL', 'ORDERCONFIRMATION_EMAIL'])(s);
};
export const selectLatestSmsNotificationDate2 = (orderItemSubsetId: number) => (s: PickupShelfDetailsState) => {
return selectLatestNotificationDatesFor(orderItemSubsetId, 'NOTIFICATION_SMS')(s);
return selectLatestNotificationDatesFor(orderItemSubsetId, ['NOTIFICATION_SMS'])(s);
};
export const selectCanSelectAction = (s: PickupShelfDetailsState) => {

View File

@@ -251,6 +251,10 @@ export class PickupShelfDetailsStore extends ComponentStore<PickupShelfDetailsSt
this.patchState({ fetchPartial });
}
resetCoverItems() {
this.patchState({ coverOrderItems: [] });
}
resetSelectedOrderItems() {
this.patchState({ selectedOrderItemIds: [] });
}

View File

@@ -23,7 +23,16 @@ export function getPurchaseOption(state: PurchaseOptionsState): PurchaseOption {
return options[0];
}
return state.purchaseOption;
if (state.purchaseOption) {
return state.purchaseOption;
}
// #4380 Abholung als Standard wenn möglich und keine Auswahl getroffen wurde
if (options.includes('pickup')) {
return 'pickup';
}
return undefined;
}
export function getSelectedItemIds(state: PurchaseOptionsState): number[] {

View File

@@ -1,6 +1,7 @@
import { DOCUMENT } from '@angular/common';
import { Inject, Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { map } from 'rxjs/operators';
@Injectable({
providedIn: 'root',
@@ -14,6 +15,32 @@ export class ShellService {
return this._fontSize$.value;
}
get scale() {
if (this._fontSize$.value === 'small') {
return 13 / 16;
} else if (this._fontSize$.value === 'normal') {
return 1;
} else if (this._fontSize$.value === 'large') {
return 19 / 16;
} else {
return 1;
}
}
scale$ = this._fontSize$.pipe(
map((size) => {
if (size === 'small') {
return 13 / 16;
} else if (size === 'normal') {
return 1;
} else if (size === 'large') {
return 19 / 16;
} else {
return 1;
}
})
);
private _sideMenuOpen$ = new BehaviorSubject<boolean>(false);
sideMenuOpen$ = this._sideMenuOpen$.asObservable();

View File

@@ -7,7 +7,6 @@ export { DialogOfString } from './models/dialog-of-string';
export { DialogSettings } from './models/dialog-settings';
export { DialogContentType } from './models/dialog-content-type';
export { KeyValueDTOOfStringAndString } from './models/key-value-dtoof-string-and-string';
export { IPublicUserInfo } from './models/ipublic-user-info';
export { ProblemDetails } from './models/problem-details';
export { ResponseArgsOfIEnumerableOfCountryDTO } from './models/response-args-of-ienumerable-of-country-dto';
export { CountryDTO } from './models/country-dto';
@@ -15,6 +14,7 @@ export { EntityDTOBaseOfCountryDTOAndICountry } from './models/entity-dtobase-of
export { EntityDTOBase } from './models/entity-dtobase';
export { EntityDTO } from './models/entity-dto';
export { EntityStatus } from './models/entity-status';
export { CRUDA } from './models/cruda';
export { ResponseArgsOfInputDTO } from './models/response-args-of-input-dto';
export { InputDTO } from './models/input-dto';
export { InputType } from './models/input-type';
@@ -92,5 +92,8 @@ export { DiffDTO } from './models/diff-dto';
export { ResponseArgsOfIEnumerableOfEntityKeyValueDTOOfStringAndString } from './models/response-args-of-ienumerable-of-entity-key-value-dtoof-string-and-string';
export { EntityKeyValueDTOOfStringAndString } from './models/entity-key-value-dtoof-string-and-string';
export { ResponseArgsOfIEnumerableOfKeyValueDTOOfStringAndString } from './models/response-args-of-ienumerable-of-key-value-dtoof-string-and-string';
export { ResponseArgsOfCheckLoyaltyCardResult } from './models/response-args-of-check-loyalty-card-result';
export { CheckLoyaltyCardResult } from './models/check-loyalty-card-result';
export { LoyaltyCardStatus } from './models/loyalty-card-status';
export { ResponseArgsOfPayerDTO } from './models/response-args-of-payer-dto';
export { ResponseArgsOfShippingAddressDTO } from './models/response-args-of-shipping-address-dto';

View File

@@ -0,0 +1,15 @@
/* tslint:disable */
import { CustomerInfoDTO } from './customer-info-dto';
import { LoyaltyCardStatus } from './loyalty-card-status';
export interface CheckLoyaltyCardResult {
/**
* Customer
*/
customer?: CustomerInfoDTO;
/**
* Status
*/
status: LoyaltyCardStatus;
}

View File

@@ -0,0 +1,2 @@
/* tslint:disable */
export type CRUDA = 0 | 1 | 2 | 4 | 8 | 16;

View File

@@ -1,11 +1,14 @@
/* tslint:disable */
import { TouchedBase } from './touched-base';
import { CRUDA } from './cruda';
import { EntityStatus } from './entity-status';
export interface EntityDTO extends TouchedBase{
changed?: string;
created?: string;
cruda?: CRUDA;
id?: number;
pId?: string;
status?: EntityStatus;
uId?: string;
version?: number;
}

View File

@@ -8,4 +8,5 @@ export interface EntityDTOReferenceContainer extends TouchedBase{
id?: number;
pId?: string;
selected?: boolean;
uId?: string;
}

View File

@@ -1,7 +0,0 @@
/* tslint:disable */
export interface IPublicUserInfo {
alias?: string;
displayName?: string;
isAuthenticated: boolean;
username?: string;
}

View File

@@ -0,0 +1,6 @@
/* tslint:disable */
/**
* Kundenkartenstatus
*/
export type LoyaltyCardStatus = 0 | 1 | 2 | 4 | 8;

View File

@@ -0,0 +1,6 @@
/* tslint:disable */
import { ResponseArgs } from './response-args';
import { CheckLoyaltyCardResult } from './check-loyalty-card-result';
export interface ResponseArgsOfCheckLoyaltyCardResult extends ResponseArgs{
result?: CheckLoyaltyCardResult;
}

View File

@@ -1,11 +1,9 @@
/* tslint:disable */
import { DialogOfString } from './dialog-of-string';
import { IPublicUserInfo } from './ipublic-user-info';
export interface ResponseArgs {
dialog?: DialogOfString;
error: boolean;
invalidProperties?: {[key: string]: string};
message?: string;
requestId?: number;
userInfo?: IPublicUserInfo;
}

View File

@@ -9,7 +9,7 @@ import { map as __map, filter as __filter } from 'rxjs/operators';
import { ResponseArgsOfIEnumerableOfEntityKeyValueDTOOfStringAndString } from '../models/response-args-of-ienumerable-of-entity-key-value-dtoof-string-and-string';
import { ResponseArgsOfIEnumerableOfKeyValueDTOOfStringAndString } from '../models/response-args-of-ienumerable-of-key-value-dtoof-string-and-string';
import { ResponseArgsOfQuerySettingsDTO } from '../models/response-args-of-query-settings-dto';
import { ResponseArgsOfCheckLoyaltyCardResult } from '../models/response-args-of-check-loyalty-card-result';
@Injectable({
providedIn: 'root',
})
@@ -121,7 +121,7 @@ class LoyaltyCardService extends __BaseService {
*
* - `customerId`: PK Kunde (optional)
*/
LoyaltyCardCheckLoyaltyCardResponse(params: LoyaltyCardService.LoyaltyCardCheckLoyaltyCardParams): __Observable<__StrictHttpResponse<ResponseArgsOfQuerySettingsDTO>> {
LoyaltyCardCheckLoyaltyCardResponse(params: LoyaltyCardService.LoyaltyCardCheckLoyaltyCardParams): __Observable<__StrictHttpResponse<ResponseArgsOfCheckLoyaltyCardResult>> {
let __params = this.newParams();
let __headers = new HttpHeaders();
let __body: any = null;
@@ -141,7 +141,7 @@ class LoyaltyCardService extends __BaseService {
return this.http.request<any>(req).pipe(
__filter(_r => _r instanceof HttpResponse),
__map((_r) => {
return _r as __StrictHttpResponse<ResponseArgsOfQuerySettingsDTO>;
return _r as __StrictHttpResponse<ResponseArgsOfCheckLoyaltyCardResult>;
})
);
}
@@ -155,9 +155,9 @@ class LoyaltyCardService extends __BaseService {
*
* - `customerId`: PK Kunde (optional)
*/
LoyaltyCardCheckLoyaltyCard(params: LoyaltyCardService.LoyaltyCardCheckLoyaltyCardParams): __Observable<ResponseArgsOfQuerySettingsDTO> {
LoyaltyCardCheckLoyaltyCard(params: LoyaltyCardService.LoyaltyCardCheckLoyaltyCardParams): __Observable<ResponseArgsOfCheckLoyaltyCardResult> {
return this.LoyaltyCardCheckLoyaltyCardResponse(params).pipe(
__map(_r => _r.body as ResponseArgsOfQuerySettingsDTO)
__map(_r => _r.body as ResponseArgsOfCheckLoyaltyCardResult)
);
}
}

View File

@@ -0,0 +1,9 @@
import { DebounceSettings, debounce as lodashDebounce } from 'lodash';
export function debounce(wait: number, options?: DebounceSettings): MethodDecorator {
return (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>): TypedPropertyDescriptor<any> => {
const currentMethod = descriptor.value;
descriptor.value = lodashDebounce(currentMethod, wait, options);
return descriptor;
};
}

View File

@@ -1,4 +1,5 @@
export * from './contains-element';
export * from './debounce.decorator';
export * from './geo-distance';
export * from './hash';
export * from './is-array';