# @isa/crm/feature/customer-loyalty-cards

Customer loyalty cards feature module for displaying and managing customer bonus cards (Kundenkarten) with points tracking and card management actions.

## Overview

This feature library provides a complete customer loyalty cards management interface for the CRM module. It displays all bonus cards associated with a customer, including:

- Points summary with total Lesepunkte (reading points) from the primary card
- Call-to-action button to navigate to the Prämienshop (rewards shop)
- Interactive carousel for browsing multiple customer cards with barcodes
- Card management actions: add new cards, lock/unlock cards
- Visual indication of blocked cards with overlay states
- Automatic sorting of blocked cards to the end of the carousel

The main component uses the Resource API pattern via `CustomerBonusCardsResource` for reactive data loading with automatic race condition prevention.

## Architecture

This is a **feature library** (prefix: `crm`) containing:
- Main container component that orchestrates the feature
- Presentational components for cards, points summary, and carousel
- Integration with `@isa/crm/data-access` for state management
- Reactive data loading using Angular's Resource API

## Components

### CustomerLoyaltyCardsComponent

Main container component for the customer loyalty cards feature.

**Selector:** `crm-customer-loyalty-cards`

**Inputs:**
- `customerId: number` (required) - Customer ID to load loyalty cards for. When changed, triggers automatic reload of bonus cards.
- `tabId: number` (required) - Tab ID for telemetry/logging purposes.

**Outputs:**
- `navigateToPraemienshop: void` - Emitted when user clicks the "Zum Prämienshop" button. Parent must handle navigation using the autoTriggerContinueFn pattern.
- `cardUpdated: void` - Emitted when a card has been added, locked, or unlocked. Parent should reload cards and transactions.

**State:**
- `cards: Signal<BonusCardInfo[] | undefined>` - All bonus cards for the selected customer
- `isLoading: Signal<boolean>` - Loading state for bonus cards
- `error: Signal<Error | undefined>` - Error state from loading bonus cards

**Example:**
```html
<crm-customer-loyalty-cards
  [customerId]="selectedCustomerId()"
  [tabId]="currentTabId()"
  (navigateToPraemienshop)="handlePraemienshopNavigation()"
  (cardUpdated)="reloadCardsAndTransactions()"
/>
```

### CustomerCardPointsSummaryComponent

Displays the points summary with CTA to Prämienshop.

**Selector:** `crm-customer-card-points-summary`

**Inputs:**
- `cards: BonusCardInfo[]` (required) - All bonus cards for the customer

**Outputs:**
- `navigateToPraemienshop: void` - Emitted when user clicks "Zum Prämienshop" button

**Features:**
- Extracts total points from primary card
- Formats points with German thousands separator (dot)
- Centered layout matching Figma design

### CustomerCardsCarouselComponent

Carousel container for displaying multiple customer loyalty cards with horizontal scrolling.

**Selector:** `crm-customer-cards-carousel`

**Inputs:**
- `cards: BonusCardInfo[]` (required) - All bonus cards to display in carousel

**Outputs:**
- `cardLocked: void` - Emitted when a card has been successfully locked or unlocked

**Features:**
- Horizontal scrolling with navigation arrows (via `@isa/ui/carousel`)
- Automatic sorting: blocked cards always appear at the end
- Gap spacing between cards
- Smooth scrolling animations

### CustomerCardComponent

Individual customer loyalty card display component.

**Selector:** `crm-customer-card`

**Inputs:**
- `card: BonusCardInfo` (required) - Bonus card data to display

**Outputs:**
- `cardLocked: void` - Emitted when a card has been successfully locked or unlocked

**Features:**
- Displays card type (Kundenkarte/Mitarbeitendenkarte)
- Shows card number and customer name
- Renders barcode using `@isa/shared/barcode`
- Blocked state overlay for inactive cards
- Fixed dimensions: 337×213px (based on Figma design)

### AddCustomerCardComponent

Button and dialog for adding new customer cards.

**Selector:** `crm-add-customer-card`

**Outputs:**
- `cardAdded: void` - Emitted when a card has been successfully added

**Features:**
- Opens text input dialog for scanning or entering 8-digit card code
- Validates card code input (required)
- Calls `CustomerCardsFacade.addCard()` to add card
- Shows success feedback dialog
- Emits event for parent to reload cards

### LockCustomerCardComponent

Component for locking/unlocking customer cards.

**Selector:** `crm-lock-customer-card`

**Inputs:**
- `isActive: boolean` (required) - Whether the card is currently active or blocked
- `cardCode: string` (required) - Card code to lock/unlock

**Outputs:**
- `cardLocked: void` - Emitted when a card has been successfully locked or unlocked

**Features:**
- Shows "Karte sperren" button for active cards
- Shows "Karte entsperren" button for blocked cards
- Handles loading state during API calls
- Shows success/error feedback dialogs
- Emits event for parent to reload cards

## Usage

### Integration in Parent Component

```typescript
import { Component, signal } from '@angular/core';
import { CustomerLoyaltyCardsComponent } from '@isa/crm/feature/customer-loyalty-cards';

@Component({
  selector: 'app-customer-detail',
  imports: [CustomerLoyaltyCardsComponent],
  template: `
    <crm-customer-loyalty-cards
      [customerId]="customerId()"
      [tabId]="tabId()"
      (navigateToPraemienshop)="navigateToPraemienshop()"
      (cardUpdated)="handleCardUpdated()"
    />
  `
})
export class CustomerDetailComponent {
  customerId = signal(12345);
  tabId = signal(1);

  navigateToPraemienshop(): void {
    // Use autoTriggerContinueFn pattern to navigate with proper customer selection
    this.router.navigate(['/praemienshop'], {
      queryParams: { customerId: this.customerId() }
    });
  }

  handleCardUpdated(): void {
    // Reload cards and related data (e.g., transactions)
    this.customerService.reload(this.customerId());
  }
}
```

### Required Provider Setup

The component requires `CustomerBonusCardsResource` to be provided:

```typescript
import { CustomerBonusCardsResource } from '@isa/crm/data-access';

@Component({
  providers: [CustomerBonusCardsResource],
  // ...
})
```

This is typically provided at the feature route or parent component level to enable scoped resource management.

## Dependencies

### Internal Dependencies

**Data Access:**
- `@isa/crm/data-access` - State management and API integration
  - `CustomerBonusCardsResource` - Resource API for loading bonus cards
  - `CustomerCardsFacade` - Facade for add/lock/unlock operations
  - `BonusCardInfo` - Type definition for bonus card data

**UI Components:**
- `@isa/ui/buttons` - `ButtonComponent`, `TextButtonComponent`
- `@isa/ui/carousel` - `CarouselComponent` for card scrolling
- `@isa/ui/dialog` - Dialog services for add card and feedback
- `@isa/shared/barcode` - `BarcodeComponent` for rendering card barcodes

**Core:**
- `@isa/core/logging` - Logger factory for consistent logging

### External Dependencies

- `@angular/core` - Angular framework
- `@angular/forms` - Form validation for card code input
- `@angular/cdk/coercion` - Number input coercion
- `rxjs` - For dialog result handling

## State Management

This feature uses the **Resource API pattern** for state management:

```typescript
// Reactive loading with automatic race condition prevention
effect(() => {
  const customerId = this.customerId();
  this.#bonusCardsResource.params({ customerId });
});

// Signals for state
readonly cards = this.#bonusCardsResource.resource.value;
readonly isLoading = this.#bonusCardsResource.resource.isLoading;
readonly error = this.#bonusCardsResource.resource.error;
```

This approach provides:
- Automatic race condition handling (cancels previous requests)
- Declarative resource management without RxJS subscriptions
- Reactive updates when `customerId` changes
- Built-in loading and error states

## Testing

The library uses Vitest for testing with Angular Testing Utilities. Test files follow the pattern:

- `*.component.spec.ts` - Component unit tests
- Uses `ComponentFixture` and `TestBed` for component testing
- Mocks for `CustomerBonusCardsResource` and `CustomerCardsFacade`

Run tests:
```bash
nx test crm-feature-customer-loyalty-cards
```

## Design

Based on Figma designs with:
- Card dimensions: 337×213px
- Black header, white footer
- Barcode sizing: 4.5rem height, 0.125rem width, 0.5rem margin
- Carousel with horizontal scrolling and navigation arrows
- Blocked cards display overlay and sort to end of carousel

## Notes

- **Blocked cards sorting:** Per Figma annotation, blocked cards are always sorted to the end of the carousel ("gesperrte Karte immer nach hinten")
- **Primary card points:** Only the primary card's points are displayed in the summary
- **Navigation pattern:** Uses output events instead of direct router navigation to support the autoTriggerContinueFn pattern for proper customer context
- **Card updates:** All card mutation operations (add, lock, unlock) emit events for parent coordination to reload related data
