Files
Nino Righi de3edaa0f9 Merged PR 2077: fix(checkout-data-access, checkout-reward-shopping-cart, checkout-reward-sele...
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
2025-12-10 17:12:47 +00:00
..

@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 quantities
  • customerRewardPoints: Total loyalty points available to the customer
  • closeText: Text for the cancel/close button

RewardSelectionDialogResult:

  • Returns updated rewardSelectionItems array if customer saves changes
  • Returns undefined if 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:

  1. Presentation Layer: Dialog and trigger components
  2. State Layer: Signal Store for reactive state management
  3. Service Layer: Dialog lifecycle and popup behavior services
  4. Resource Layer: Price and redemption points loading
  5. 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.