From f04e36e7102303715545bd7be50bc13494889d3b Mon Sep 17 00:00:00 2001 From: Nino Righi Date: Thu, 6 Nov 2025 16:32:10 +0000 Subject: [PATCH] Merged PR 2008: fix(reward-print, reward-popup, reward-destination): improve reward cart stab... fix(reward-print, reward-popup, reward-destination): improve reward cart stability and UX - fix: remove console.log statement from calculate-price-value helper - fix: add loading/pending state to print button to prevent duplicate prints - fix: debounce reward selection resource reloading to prevent race conditions - fix: correct reward cart item destination-info alignment and flex behavior - fix: support OrderType in OrderDestinationComponent alongside OrderTypeFeature - fix: use unitPrice instead of total for price calculations in reward items - refactor: update calculatePriceValue test descriptions for clarity - fix: fallback to order.orderType when features don't contain orderType The reward selection popup now properly waits for all resources to reload before resolving, preventing timing issues with cart synchronization. Print button shows pending state during print operations. Destination info components now handle both legacy OrderType and new OrderTypeFeature enums for better compatibility. Ref: #5442, #5445 --- .../article-details/article-details.component.ts | 6 ++---- .../helpers/calculate-price-value.helper.spec.ts | 6 +++--- .../lib/helpers/calculate-price-value.helper.ts | 7 ++++--- ...rder-confirmation-item-list-item.component.ts | 6 ++---- .../reward-shopping-cart-item.component.html | 2 +- .../destination-info.component.css | 2 +- .../display-order-destination-info.component.ts | 12 +++++------- .../service/reward-selection.service.ts | 11 +++++------ .../print-button/print-button.component.html | 2 ++ .../order-destination.component.spec.ts | 16 ++++++++-------- .../order-destination.component.ts | 2 +- 11 files changed, 34 insertions(+), 38 deletions(-) diff --git a/apps/isa-app/src/page/catalog/article-details/article-details.component.ts b/apps/isa-app/src/page/catalog/article-details/article-details.component.ts index 0fe446ffe..509ade6fb 100644 --- a/apps/isa-app/src/page/catalog/article-details/article-details.component.ts +++ b/apps/isa-app/src/page/catalog/article-details/article-details.component.ts @@ -55,8 +55,6 @@ import { RewardSelectionPopUpService, } from '@isa/checkout/shared/reward-selection-dialog'; -import { injectConfirmationDialog, injectFeedbackDialog } from '@isa/ui/dialog'; - @Component({ selector: 'page-article-details', templateUrl: 'article-details.component.html', @@ -210,7 +208,7 @@ export class ArticleDetailsComponent implements OnInit, OnDestroy { ).path; } - showMore: boolean = false; + showMore = false; @ViewChild('detailsContainer', { read: ElementRef, static: false }) detailsContainer: ElementRef; @@ -610,7 +608,7 @@ export class ArticleDetailsComponent implements OnInit, OnDestroy { async navigateToResultList() { const processId = this.applicationService.activatedProcessId; - let crumbs = await this.breadcrumb + const crumbs = await this.breadcrumb .getBreadcrumbsByKeyAndTags$(this.applicationService.activatedProcessId, [ 'catalog', 'details', diff --git a/libs/checkout/data-access/src/lib/helpers/calculate-price-value.helper.spec.ts b/libs/checkout/data-access/src/lib/helpers/calculate-price-value.helper.spec.ts index c30686588..8ca0da3d3 100644 --- a/libs/checkout/data-access/src/lib/helpers/calculate-price-value.helper.spec.ts +++ b/libs/checkout/data-access/src/lib/helpers/calculate-price-value.helper.spec.ts @@ -3,10 +3,10 @@ import { calculatePriceValue } from './calculate-price-value.helper'; import { RewardSelectionItem } from '@isa/checkout/data-access'; describe('calculatePriceValue', () => { - it('should return item total price when available', () => { + it('should return item unit price when available', () => { const item: RewardSelectionItem = { item: { - total: { value: { value: 99.99 } }, + unitPrice: { value: { value: 99.99 } }, }, cartQuantity: 1, rewardCartQuantity: 0, @@ -20,7 +20,7 @@ describe('calculatePriceValue', () => { expect(result).toBe(99.99); }); - it('should return availability price when total price not available', () => { + it('should return availability price when unit price not available', () => { const item: RewardSelectionItem = { item: { availability: { price: { value: { value: 79.99 } } }, diff --git a/libs/checkout/data-access/src/lib/helpers/calculate-price-value.helper.ts b/libs/checkout/data-access/src/lib/helpers/calculate-price-value.helper.ts index 804ddb7c3..d2421862e 100644 --- a/libs/checkout/data-access/src/lib/helpers/calculate-price-value.helper.ts +++ b/libs/checkout/data-access/src/lib/helpers/calculate-price-value.helper.ts @@ -3,13 +3,14 @@ import { RewardSelectionItem } from '@isa/checkout/data-access'; export const calculatePriceValue = ( rewardSelectionItem: RewardSelectionItem, ): number => { - const itemTotalPrice = rewardSelectionItem.item?.total?.value?.value; + console.log(rewardSelectionItem); + const itemUnitPrice = rewardSelectionItem.item?.unitPrice?.value?.value; const availabilityPrice = rewardSelectionItem.item?.availability?.price?.value?.value; const catalogPrice = rewardSelectionItem.catalogPrice?.value?.value; - if (itemTotalPrice != null && itemTotalPrice !== 0) { - return itemTotalPrice; + if (itemUnitPrice != null && itemUnitPrice !== 0) { + return itemUnitPrice; } if (availabilityPrice != null && availabilityPrice !== 0) { diff --git a/libs/checkout/feature/reward-order-confirmation/src/lib/order-confirmation-item-list/order-confirmation-item-list-item/order-confirmation-item-list-item.component.ts b/libs/checkout/feature/reward-order-confirmation/src/lib/order-confirmation-item-list/order-confirmation-item-list-item/order-confirmation-item-list-item.component.ts index 2535a16fd..2b9f21488 100644 --- a/libs/checkout/feature/reward-order-confirmation/src/lib/order-confirmation-item-list/order-confirmation-item-list-item/order-confirmation-item-list-item.component.ts +++ b/libs/checkout/feature/reward-order-confirmation/src/lib/order-confirmation-item-list/order-confirmation-item-list-item/order-confirmation-item-list-item.component.ts @@ -9,9 +9,7 @@ import { ProductInfoComponent, DisplayOrderDestinationInfoComponent, } from '@isa/checkout/shared/product-info'; -import { - DisplayOrderItemDTO, -} from '@generated/swagger/oms-api'; +import { DisplayOrderItemDTO } from '@generated/swagger/oms-api'; import { Product } from '@isa/common/data-access'; import { type OrderItemGroup } from '@isa/checkout/data-access'; @@ -34,7 +32,7 @@ export class OrderConfirmationItemListItemComponent { * This now receives an OrderItemGroup which contains the necessary order information * (features, targetBranch, shippingAddress) required by the DisplayOrderDestinationInfoComponent. */ - order = input.required>(); + order = input.required(); productItem = computed(() => { return this.item()?.product; diff --git a/libs/checkout/feature/reward-shopping-cart/src/lib/reward-shopping-cart-item/reward-shopping-cart-item.component.html b/libs/checkout/feature/reward-shopping-cart/src/lib/reward-shopping-cart-item/reward-shopping-cart-item.component.html index 76c164041..3643207ef 100644 --- a/libs/checkout/feature/reward-shopping-cart/src/lib/reward-shopping-cart-item/reward-shopping-cart-item.component.html +++ b/libs/checkout/feature/reward-shopping-cart/src/lib/reward-shopping-cart-item/reward-shopping-cart-item.component.html @@ -29,7 +29,7 @@ @if (!isHorizontal()) { diff --git a/libs/checkout/shared/product-info/src/lib/destination-info/destination-info.component.css b/libs/checkout/shared/product-info/src/lib/destination-info/destination-info.component.css index 05588b5a5..4ab33c2ce 100644 --- a/libs/checkout/shared/product-info/src/lib/destination-info/destination-info.component.css +++ b/libs/checkout/shared/product-info/src/lib/destination-info/destination-info.component.css @@ -1,3 +1,3 @@ :host { - @apply contents; + @apply flex; } diff --git a/libs/checkout/shared/product-info/src/lib/display-order-destination-info/display-order-destination-info.component.ts b/libs/checkout/shared/product-info/src/lib/display-order-destination-info/display-order-destination-info.component.ts index 90427d752..e5eb44273 100644 --- a/libs/checkout/shared/product-info/src/lib/display-order-destination-info/display-order-destination-info.component.ts +++ b/libs/checkout/shared/product-info/src/lib/display-order-destination-info/display-order-destination-info.component.ts @@ -1,7 +1,7 @@ -import { Component, computed, inject, input } from '@angular/core'; +import { Component, computed, input } from '@angular/core'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; -import { getOrderTypeFeature } from '@isa/checkout/data-access'; -import { DisplayOrder, DisplayOrderItem } from '@isa/oms/data-access'; +import { OrderItemGroup } from '@isa/checkout/data-access'; +import { DisplayOrderItem } from '@isa/oms/data-access'; import { OrderDestinationComponent, ShippingAddress, @@ -22,15 +22,13 @@ export class DisplayOrderDestinationInfoComponent { }); // Accept the parent DisplayOrder (required for branch info) - order = input.required(); + order = input.required(); // Optionally accept DisplayOrderItem (for potential future item-specific logic) item = input(); orderType = computed(() => { - const order = this.order(); - const features = order.features; - return getOrderTypeFeature(features); + return this.order().orderType; }); mappedBranch = computed(() => { diff --git a/libs/checkout/shared/reward-selection-dialog/src/lib/reward-selection-dialog/service/reward-selection.service.ts b/libs/checkout/shared/reward-selection-dialog/src/lib/reward-selection-dialog/service/reward-selection.service.ts index d50f59e7c..833a1fe75 100644 --- a/libs/checkout/shared/reward-selection-dialog/src/lib/reward-selection-dialog/service/reward-selection.service.ts +++ b/libs/checkout/shared/reward-selection-dialog/src/lib/reward-selection-dialog/service/reward-selection.service.ts @@ -16,7 +16,7 @@ import { import { PrimaryCustomerCardResource } from '@isa/crm/data-access'; import { firstValueFrom } from 'rxjs'; import { toObservable } from '@angular/core/rxjs-interop'; -import { filter, first } from 'rxjs/operators'; +import { debounceTime, filter } from 'rxjs/operators'; import { PriceAndRedemptionPointsResource, ItemWithOrderType, @@ -177,15 +177,14 @@ export class RewardSelectionService { } async reloadResources(): Promise { - // Start reloading all resources - // Note: PrimaryCustomerCard, Price and redemption points will be loaded automatically by the effect - // when selectionItemsWithOrderType changes after cart resources are reloaded this.#shoppingCartResource.reload(); this.#rewardShoppingCartResource.reload(); - // Wait until all resources are fully loaded (isLoading becomes false) await firstValueFrom( - this.#isLoading$.pipe(filter((isLoading) => !isLoading)), + this.#isLoading$.pipe( + debounceTime(50), + filter((isLoading) => !isLoading), + ), ); } } diff --git a/libs/common/print/src/lib/components/print-button/print-button.component.html b/libs/common/print/src/lib/components/print-button/print-button.component.html index c97ab3b08..995cbc3bc 100644 --- a/libs/common/print/src/lib/components/print-button/print-button.component.html +++ b/libs/common/print/src/lib/components/print-button/print-button.component.html @@ -3,6 +3,8 @@ data-which="print" class="self-start" (click)="print()" + [pending]="printing()" + [disabled]="printing()" uiInfoButton > diff --git a/libs/shared/delivery/src/lib/order-destination/order-destination.component.spec.ts b/libs/shared/delivery/src/lib/order-destination/order-destination.component.spec.ts index 8926b943d..e5b3e48cb 100644 --- a/libs/shared/delivery/src/lib/order-destination/order-destination.component.spec.ts +++ b/libs/shared/delivery/src/lib/order-destination/order-destination.component.spec.ts @@ -2,7 +2,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { provideHttpClient } from '@angular/common/http'; import { provideHttpClientTesting } from '@angular/common/http/testing'; import { OrderDestinationComponent } from './order-destination.component'; -import { OrderType } from '@isa/common/data-access'; +import { OrderTypeFeature } from '@isa/common/data-access'; describe('OrderDestinationComponent', () => { let component: OrderDestinationComponent; @@ -23,28 +23,28 @@ describe('OrderDestinationComponent', () => { }); it('should display delivery icon for delivery order type', () => { - fixture.componentRef.setInput('orderType', OrderType.Delivery); + fixture.componentRef.setInput('orderType', OrderTypeFeature.Delivery); fixture.detectChanges(); expect(component.destinationIcon()).toBe('isaDeliveryVersand'); }); it('should display pickup icon for pickup order type', () => { - fixture.componentRef.setInput('orderType', OrderType.Pickup); + fixture.componentRef.setInput('orderType', OrderTypeFeature.Pickup); fixture.detectChanges(); expect(component.destinationIcon()).toBe('isaDeliveryRuecklage2'); }); it('should display in-store icon for in-store order type', () => { - fixture.componentRef.setInput('orderType', OrderType.InStore); + fixture.componentRef.setInput('orderType', OrderTypeFeature.InStore); fixture.detectChanges(); expect(component.destinationIcon()).toBe('isaDeliveryRuecklage1'); }); it('should display branch name when branch is provided', () => { - fixture.componentRef.setInput('orderType', OrderType.Pickup); + fixture.componentRef.setInput('orderType', OrderTypeFeature.Pickup); fixture.componentRef.setInput('branch', { name: 'Test Branch' }); fixture.detectChanges(); @@ -52,7 +52,7 @@ describe('OrderDestinationComponent', () => { }); it('should display shipping address name for delivery', () => { - fixture.componentRef.setInput('orderType', OrderType.Delivery); + fixture.componentRef.setInput('orderType', OrderTypeFeature.Delivery); fixture.componentRef.setInput('shippingAddress', { firstName: 'John', lastName: 'Doe', @@ -68,7 +68,7 @@ describe('OrderDestinationComponent', () => { city: 'Vienna', zipCode: '1010', }; - fixture.componentRef.setInput('orderType', OrderType.Delivery); + fixture.componentRef.setInput('orderType', OrderTypeFeature.Delivery); fixture.componentRef.setInput('shippingAddress', { firstName: 'John', lastName: 'Doe', @@ -85,7 +85,7 @@ describe('OrderDestinationComponent', () => { city: 'Vienna', zipCode: '1020', }; - fixture.componentRef.setInput('orderType', OrderType.Pickup); + fixture.componentRef.setInput('orderType', OrderTypeFeature.Pickup); fixture.componentRef.setInput('branch', { name: 'Test Branch', address: testAddress, diff --git a/libs/shared/delivery/src/lib/order-destination/order-destination.component.ts b/libs/shared/delivery/src/lib/order-destination/order-destination.component.ts index 894a56c6d..90664543c 100644 --- a/libs/shared/delivery/src/lib/order-destination/order-destination.component.ts +++ b/libs/shared/delivery/src/lib/order-destination/order-destination.component.ts @@ -9,7 +9,7 @@ import { } from '@isa/icons'; import { InlineAddressComponent } from '@isa/shared/address'; import { logger } from '@isa/core/logging'; -import { OrderTypeFeature } from '@isa/common/data-access'; +import { OrderType, OrderTypeFeature } from '@isa/common/data-access'; export type Address = { apartment?: string;