mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-31 09:37:15 +01:00
📝 docs: update README documentation for 13 libraries
This commit is contained in:
@@ -1,205 +1,310 @@
|
||||
# Reward Selection Dialog
|
||||
# @isa/checkout/shared/reward-selection-dialog
|
||||
|
||||
Angular library for managing reward selection in shopping cart context. Allows users to toggle between regular purchase and reward redemption using bonus points.
|
||||
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).
|
||||
|
||||
## Features
|
||||
## Overview
|
||||
|
||||
- 🎯 Pre-built trigger component or direct service integration
|
||||
- 🔄 Automatic resource management (carts, bonus cards)
|
||||
- 📊 Smart grouping by order type and branch
|
||||
- 💾 NgRx Signals state management
|
||||
- ✅ Full TypeScript support
|
||||
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
|
||||
|
||||
```typescript
|
||||
```ts
|
||||
import {
|
||||
RewardSelectionDialogComponent,
|
||||
RewardSelectionService,
|
||||
RewardSelectionPopUpService,
|
||||
RewardSelectionTriggerComponent,
|
||||
RewardSelectionTriggerComponent
|
||||
} from '@isa/checkout/shared/reward-selection-dialog';
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
## Components
|
||||
|
||||
### Using the Trigger Component (Recommended)
|
||||
### RewardSelectionDialogComponent
|
||||
|
||||
Simplest integration - includes all providers automatically:
|
||||
The main dialog component that displays eligible items and allows customers to allocate quantities between regular cart and reward cart.
|
||||
|
||||
```typescript
|
||||
**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
|
||||
|
||||
```ts
|
||||
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
|
||||
|
||||
```ts
|
||||
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):
|
||||
|
||||
```ts
|
||||
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:
|
||||
|
||||
```html
|
||||
<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
|
||||
|
||||
```ts
|
||||
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
|
||||
|
||||
```ts
|
||||
import { Component } from '@angular/core';
|
||||
import { RewardSelectionTriggerComponent } from '@isa/checkout/shared/reward-selection-dialog';
|
||||
|
||||
@Component({
|
||||
selector: 'app-checkout',
|
||||
template: `<lib-reward-selection-trigger />`,
|
||||
imports: [RewardSelectionTriggerComponent],
|
||||
selector: 'app-cart-summary',
|
||||
template: `
|
||||
<div class="cart-actions">
|
||||
<lib-reward-selection-trigger />
|
||||
<button>Proceed to Checkout</button>
|
||||
</div>
|
||||
`,
|
||||
imports: [RewardSelectionTriggerComponent]
|
||||
})
|
||||
export class CheckoutComponent {}
|
||||
export class CartSummaryComponent {}
|
||||
```
|
||||
|
||||
### Using the Pop-Up Service
|
||||
### One-Time Popup on Checkout Entry
|
||||
|
||||
More control over navigation flow:
|
||||
|
||||
```typescript
|
||||
import { Component, inject } from '@angular/core';
|
||||
```ts
|
||||
import { Component, inject, OnInit } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import {
|
||||
RewardSelectionPopUpService,
|
||||
NavigateAfterRewardSelection,
|
||||
RewardSelectionService,
|
||||
NavigateAfterRewardSelection
|
||||
} from '@isa/checkout/shared/reward-selection-dialog';
|
||||
import {
|
||||
SelectedShoppingCartResource,
|
||||
SelectedRewardShoppingCartResource,
|
||||
} from '@isa/checkout/data-access';
|
||||
|
||||
@Component({
|
||||
selector: 'app-custom-checkout',
|
||||
template: `<button (click)="openRewardSelection()">Select Rewards</button>`,
|
||||
providers: [
|
||||
// Required providers
|
||||
SelectedShoppingCartResource,
|
||||
RewardSelectionService,
|
||||
RewardSelectionPopUpService,
|
||||
],
|
||||
selector: 'app-checkout-entry',
|
||||
template: `<p>Loading checkout...</p>`,
|
||||
providers: [RewardSelectionPopUpService]
|
||||
})
|
||||
export class CustomCheckoutComponent {
|
||||
export class CheckoutEntryComponent implements OnInit {
|
||||
#router = inject(Router);
|
||||
#popUpService = inject(RewardSelectionPopUpService);
|
||||
|
||||
async openRewardSelection() {
|
||||
async ngOnInit() {
|
||||
const result = await this.#popUpService.popUp();
|
||||
|
||||
// Handle navigation: 'cart' | 'reward' | 'catalog' | undefined
|
||||
|
||||
if (result === NavigateAfterRewardSelection.CART) {
|
||||
// Navigate to cart
|
||||
await this.#router.navigate(['/cart']);
|
||||
} else if (result === NavigateAfterRewardSelection.REWARD) {
|
||||
await this.#router.navigate(['/reward-checkout']);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Using the Service Directly
|
||||
### Checking Eligibility
|
||||
|
||||
For custom UI or advanced use cases:
|
||||
|
||||
```typescript
|
||||
import { Component, inject } from '@angular/core';
|
||||
```ts
|
||||
import { Component, inject, computed } from '@angular/core';
|
||||
import { RewardSelectionService } from '@isa/checkout/shared/reward-selection-dialog';
|
||||
import {
|
||||
SelectedShoppingCartResource,
|
||||
} from '@isa/checkout/data-access';
|
||||
|
||||
@Component({
|
||||
selector: 'app-advanced',
|
||||
selector: 'app-cart',
|
||||
template: `
|
||||
@if (canOpen()) {
|
||||
<button (click)="openDialog()" [disabled]="isLoading()">
|
||||
{{ eligibleItemsCount() }} items as rewards ({{ availablePoints() }} points)
|
||||
</button>
|
||||
@if (hasRewardEligibleItems()) {
|
||||
<div class="reward-banner">
|
||||
You have items eligible for reward redemption!
|
||||
<button (click)="openDialog()">Choose Payment Method</button>
|
||||
</div>
|
||||
}
|
||||
`,
|
||||
providers: [
|
||||
SelectedShoppingCartResource,
|
||||
RewardSelectionService,
|
||||
],
|
||||
providers: [RewardSelectionService]
|
||||
})
|
||||
export class AdvancedComponent {
|
||||
#service = inject(RewardSelectionService);
|
||||
export class CartComponent {
|
||||
#rewardSelectionService = inject(RewardSelectionService);
|
||||
|
||||
canOpen = this.#service.canOpen;
|
||||
isLoading = this.#service.isLoading;
|
||||
eligibleItemsCount = computed(() => this.#service.eligibleItems().length);
|
||||
availablePoints = this.#service.primaryBonusCardPoints;
|
||||
hasRewardEligibleItems = this.#rewardSelectionService.canOpen;
|
||||
|
||||
async openDialog() {
|
||||
const result = await this.#service.open({ closeText: 'Cancel' });
|
||||
if (result) {
|
||||
// Handle result.rewardSelectionItems
|
||||
await this.#service.reloadResources();
|
||||
}
|
||||
const result = await this.#rewardSelectionService.open({
|
||||
closeText: 'Cancel'
|
||||
});
|
||||
// Handle result...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
## State Management
|
||||
|
||||
### RewardSelectionService
|
||||
The library uses `RewardSelectionStore` (NgRx Signal Store) internally to manage:
|
||||
|
||||
**Key Signals:**
|
||||
- `canOpen()`: `boolean` - Can dialog be opened
|
||||
- `isLoading()`: `boolean` - Loading state
|
||||
- `eligibleItems()`: `RewardSelectionItem[]` - Items available as rewards
|
||||
- `primaryBonusCardPoints()`: `number` - Available points
|
||||
- **State:** Item allocations, customer loyalty points
|
||||
- **Computed Values:** Total price, total loyalty points needed
|
||||
- **Methods:** Update cart quantities, update reward cart quantities
|
||||
|
||||
**Methods:**
|
||||
- `open({ closeText }): Promise<RewardSelectionDialogResult>` - Opens dialog
|
||||
- `reloadResources(): Promise<void>` - Reloads all data
|
||||
The store is scoped to each dialog instance and automatically initialized with dialog data.
|
||||
|
||||
### RewardSelectionPopUpService
|
||||
## Validation
|
||||
|
||||
**Methods:**
|
||||
- `popUp(): Promise<NavigateAfterRewardSelection | undefined>` - Opens dialog with navigation flow
|
||||
|
||||
**Return values:**
|
||||
- `'cart'` - Navigate to shopping cart
|
||||
- `'reward'` - Navigate to reward checkout
|
||||
- `'catalog'` - Navigate to catalog
|
||||
- `undefined` - No navigation needed
|
||||
|
||||
### Types
|
||||
|
||||
```typescript
|
||||
interface RewardSelectionItem {
|
||||
item: ShoppingCartItem;
|
||||
catalogPrice: Price | undefined;
|
||||
availabilityPrice: Price | undefined;
|
||||
catalogRewardPoints: number | undefined;
|
||||
cartQuantity: number;
|
||||
rewardCartQuantity: number;
|
||||
}
|
||||
|
||||
type RewardSelectionDialogResult = {
|
||||
rewardSelectionItems: RewardSelectionItem[];
|
||||
} | undefined;
|
||||
|
||||
type NavigateAfterRewardSelection = 'cart' | 'reward' | 'catalog';
|
||||
```
|
||||
|
||||
## Required Providers
|
||||
|
||||
When using `RewardSelectionService` or `RewardSelectionPopUpService` directly, provide:
|
||||
|
||||
```typescript
|
||||
providers: [
|
||||
SelectedShoppingCartResource, // Regular cart data
|
||||
RewardSelectionService, // Core service
|
||||
RewardSelectionPopUpService, // Optional: only if using pop-up
|
||||
]
|
||||
```
|
||||
|
||||
**Note:** `RewardSelectionTriggerComponent` includes all required providers automatically.
|
||||
|
||||
## Testing
|
||||
|
||||
```bash
|
||||
nx test reward-selection-dialog
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
reward-selection-dialog/
|
||||
├── helper/ # Pure utility functions
|
||||
├── resource/ # Data resources
|
||||
├── service/ # Business logic
|
||||
├── store/ # NgRx Signals state
|
||||
└── trigger/ # Trigger component
|
||||
```
|
||||
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
|
||||
|
||||
- `@isa/checkout/data-access` - Cart resources
|
||||
- `@isa/crm/data-access` - Customer data
|
||||
- `@isa/catalogue/data-access` - Product catalog
|
||||
- `@isa/ui/dialog` - Dialog infrastructure
|
||||
- `@ngrx/signals` - State management
|
||||
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.
|
||||
|
||||
Reference in New Issue
Block a user