fix(checkout-data-access, checkout-reward-shopping-cart, checkout-reward-selection-dialog): Show Low Stock message inside Dialog, Adjusted Item Identifyer so that mergedItems inside reward-selection-dialog service works properly, Adjusted Error Message Logic and Quantity Select Logic based on purchasing Options for Abholung Ref: #5523
@isa/checkout/shared/reward-selection-dialog
A comprehensive Angular dialog library for managing reward selection in the checkout process, allowing customers to allocate cart items between regular purchases (paid with money) and reward redemptions (paid with loyalty points).
Overview
This library provides a sophisticated dialog system that enables customers to decide how they want to purchase items that are eligible for both regular checkout and reward redemption. The dialog presents items from both the regular shopping cart and reward shopping cart, allowing customers to allocate quantities between payment methods.
The library includes:
- A main dialog component with quantity allocation interface
- A trigger component for opening the dialog from various contexts
- State management using NgRx Signal Store
- Services for managing dialog lifecycle and popup behavior
- Automatic resource loading and validation
Installation
import {
RewardSelectionDialogComponent,
RewardSelectionService,
RewardSelectionPopUpService,
RewardSelectionTriggerComponent
} from '@isa/checkout/shared/reward-selection-dialog';
Components
RewardSelectionDialogComponent
The main dialog component that displays eligible items and allows customers to allocate quantities between regular cart and reward cart.
Features:
- Displays customer's available loyalty points
- Shows item grouping by order type (delivery, pickup, etc.) and branch
- Real-time calculation of total price and loyalty points needed
- Validates sufficient loyalty points before allowing save
- Integrates with shopping cart resources
RewardSelectionTriggerComponent
A button component that can be embedded in the UI to trigger the reward selection dialog.
Features:
- Automatically shows/hides based on eligibility
- Skeleton loader during resource loading
- Handles dialog result and navigation
- Provides feedback after selection
API Reference
Dialog Data Interface
export type RewardSelectionDialogData = {
rewardSelectionItems: RewardSelectionItem[];
customerRewardPoints: number;
closeText: string;
};
export type RewardSelectionDialogResult =
| {
rewardSelectionItems: RewardSelectionItem[];
}
| undefined;
RewardSelectionDialogData:
rewardSelectionItems: Array of items eligible for reward selection with current cart/reward quantitiescustomerRewardPoints: Total loyalty points available to the customercloseText: Text for the cancel/close button
RewardSelectionDialogResult:
- Returns updated
rewardSelectionItemsarray if customer saves changes - Returns
undefinedif dialog is cancelled or no changes made
Opening the Dialog
Using RewardSelectionService
import { inject } from '@angular/core';
import { RewardSelectionService } from '@isa/checkout/shared/reward-selection-dialog';
export class MyComponent {
#rewardSelectionService = inject(RewardSelectionService);
async openDialog() {
// Check if dialog can be opened (has eligible items and customer has points)
if (this.#rewardSelectionService.canOpen()) {
const result = await this.#rewardSelectionService.open({
closeText: 'Abbrechen'
});
if (result) {
// Handle the result
console.log('Updated items:', result.rewardSelectionItems);
}
}
}
}
Using RewardSelectionPopUpService
For automatic popup behavior (e.g., showing dialog once per session):
import { inject } from '@angular/core';
import {
RewardSelectionPopUpService,
NavigateAfterRewardSelection
} from '@isa/checkout/shared/reward-selection-dialog';
export class CheckoutComponent {
#popUpService = inject(RewardSelectionPopUpService);
async showPopupIfNeeded() {
const navigation = await this.#popUpService.popUp();
switch (navigation) {
case NavigateAfterRewardSelection.CART:
// Navigate to regular shopping cart
break;
case NavigateAfterRewardSelection.REWARD:
// Navigate to reward checkout
break;
case NavigateAfterRewardSelection.CATALOG:
// Navigate back to catalog
break;
case undefined:
// Stay on current page
break;
}
}
}
Using RewardSelectionTriggerComponent
Add the trigger component directly to your template:
<lib-reward-selection-trigger />
The trigger component:
- Automatically determines eligibility
- Shows loading skeleton during resource fetching
- Opens dialog on click
- Handles navigation after selection
Usage Examples
Basic Dialog Integration
import { Component, inject } from '@angular/core';
import { RewardSelectionService } from '@isa/checkout/shared/reward-selection-dialog';
@Component({
selector: 'app-checkout',
template: `
<button (click)="openRewardSelection()">
Select Reward Items
</button>
`,
providers: [RewardSelectionService]
})
export class CheckoutComponent {
#rewardSelectionService = inject(RewardSelectionService);
async openRewardSelection() {
await this.#rewardSelectionService.reloadResources();
if (this.#rewardSelectionService.canOpen()) {
const result = await this.#rewardSelectionService.open({
closeText: 'Cancel'
});
if (result?.rewardSelectionItems.length) {
// Process updated selections
}
}
}
}
Using Trigger Component
import { Component } from '@angular/core';
import { RewardSelectionTriggerComponent } from '@isa/checkout/shared/reward-selection-dialog';
@Component({
selector: 'app-cart-summary',
template: `
<div class="cart-actions">
<lib-reward-selection-trigger />
<button>Proceed to Checkout</button>
</div>
`,
imports: [RewardSelectionTriggerComponent]
})
export class CartSummaryComponent {}
One-Time Popup on Checkout Entry
import { Component, inject, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import {
RewardSelectionPopUpService,
NavigateAfterRewardSelection
} from '@isa/checkout/shared/reward-selection-dialog';
@Component({
selector: 'app-checkout-entry',
template: `<p>Loading checkout...</p>`,
providers: [RewardSelectionPopUpService]
})
export class CheckoutEntryComponent implements OnInit {
#router = inject(Router);
#popUpService = inject(RewardSelectionPopUpService);
async ngOnInit() {
const result = await this.#popUpService.popUp();
if (result === NavigateAfterRewardSelection.CART) {
await this.#router.navigate(['/cart']);
} else if (result === NavigateAfterRewardSelection.REWARD) {
await this.#router.navigate(['/reward-checkout']);
}
}
}
Checking Eligibility
import { Component, inject, computed } from '@angular/core';
import { RewardSelectionService } from '@isa/checkout/shared/reward-selection-dialog';
@Component({
selector: 'app-cart',
template: `
@if (hasRewardEligibleItems()) {
<div class="reward-banner">
You have items eligible for reward redemption!
<button (click)="openDialog()">Choose Payment Method</button>
</div>
}
`,
providers: [RewardSelectionService]
})
export class CartComponent {
#rewardSelectionService = inject(RewardSelectionService);
hasRewardEligibleItems = this.#rewardSelectionService.canOpen;
async openDialog() {
const result = await this.#rewardSelectionService.open({
closeText: 'Cancel'
});
// Handle result...
}
}
State Management
The library uses RewardSelectionStore (NgRx Signal Store) internally to manage:
- State: Item allocations, customer loyalty points
- Computed Values: Total price, total loyalty points needed
- Methods: Update cart quantities, update reward cart quantities
The store is scoped to each dialog instance and automatically initialized with dialog data.
Validation
The dialog automatically validates:
- Customer has sufficient loyalty points for selected reward items
- Items are eligible for reward redemption (have loyalty points configured)
- Changes were made before saving (prevents unnecessary API calls)
Dependencies
Key dependencies on other @isa libraries:
- @isa/checkout/data-access: Shopping cart resources, reward selection models and facades
- @isa/ui/dialog: Dialog infrastructure and directives
- @isa/ui/buttons: Button components
- @isa/core/tabs: Tab ID management for multi-tab support
- @isa/crm/data-access: Customer card resource for loyalty points
- @isa/icons: Order type icons for grouping display
- @ngrx/signals: Signal Store for state management
Architecture
The library follows a layered architecture:
- Presentation Layer: Dialog and trigger components
- State Layer: Signal Store for reactive state management
- Service Layer: Dialog lifecycle and popup behavior services
- Resource Layer: Price and redemption points loading
- Integration Layer: Shopping cart and customer card resources
This separation ensures the dialog can be used in various contexts (manual trigger, automatic popup, embedded component) while maintaining consistent behavior and state management.