# Purchase Options Modal

The Purchase Options Modal allows users to select how they want to receive their items (delivery, pickup, in-store, download) during the checkout process.

## Overview

This modal handles the complete purchase option selection flow including:
- Fetching availability for each purchase option
- Validating if items can be added to the shopping cart
- Managing item quantities and prices
- Supporting reward redemption flows

## Features

### Core Functionality
- **Multiple Purchase Options**: Delivery, B2B Delivery, Digital Delivery, Pickup, In-Store, Download
- **Availability Checking**: Real-time availability checks for each option
- **Branch Selection**: Pick branches for pickup and in-store options
- **Price Management**: Handle pricing, VAT, and manual price adjustments
- **Reward Redemption**: Support for redeeming loyalty points

### Advanced Features
- **Disabled Purchase Options**: Prevent specific options from being available (skips API calls)
- **Hide Disabled Options**: Toggle visibility of disabled options (show grayed out or hide completely)
- **Pre-selection**: Pre-select a specific purchase option on open
- **Single Option Mode**: Show only one specific purchase option

## Usage

### Basic Usage

```typescript
import { PurchaseOptionsModalService } from '@modal/purchase-options';

constructor(private purchaseOptionsModal: PurchaseOptionsModalService) {}

async openModal() {
  const modalRef = await this.purchaseOptionsModal.open({
    tabId: 123,
    shoppingCartId: 456,
    type: 'add', // or 'update'
    items: [/* array of items */],
  });

  const result = await firstValueFrom(modalRef.afterClosed$);
}
```

### Disabling Purchase Options

Prevent specific options from being available. The modal will **not make API calls** for disabled options.

```typescript
const modalRef = await this.purchaseOptionsModal.open({
  tabId: 123,
  shoppingCartId: 456,
  type: 'add',
  items: [item1, item2],
  disabledPurchaseOptions: ['b2b-delivery'], // Disable B2B delivery
});
```

### Hide vs Show Disabled Options

Control whether disabled options are hidden or shown with a disabled visual state:

**Hide Disabled Options (default)**
```typescript
const modalRef = await this.purchaseOptionsModal.open({
  tabId: 123,
  shoppingCartId: 456,
  type: 'add',
  items: [item],
  disabledPurchaseOptions: ['b2b-delivery'],
  hideDisabledPurchaseOptions: true, // Default - option not visible
});
```

**Show Disabled Options (grayed out)**
```typescript
const modalRef = await this.purchaseOptionsModal.open({
  tabId: 123,
  shoppingCartId: 456,
  type: 'add',
  items: [item],
  disabledPurchaseOptions: ['b2b-delivery'],
  hideDisabledPurchaseOptions: false, // Show disabled with visual indicator
});
```

### Pre-selecting an Option

```typescript
const modalRef = await this.purchaseOptionsModal.open({
  tabId: 123,
  shoppingCartId: 456,
  type: 'add',
  items: [item],
  preSelectOption: {
    option: 'in-store',
    showOptionOnly: false, // Optional: show only this option
  },
});
```

### Reward Redemption Flow

```typescript
const modalRef = await this.purchaseOptionsModal.open({
  tabId: 123,
  shoppingCartId: 456,
  type: 'add',
  items: [rewardItem],
  useRedemptionPoints: true,
  preSelectOption: { option: 'in-store' },
  disabledPurchaseOptions: ['b2b-delivery'], // Common for rewards
});
```

## API Reference

### PurchaseOptionsModalData

```typescript
interface PurchaseOptionsModalData {
  /** Tab ID for context */
  tabId: number;

  /** Shopping cart ID to add/update items */
  shoppingCartId: number;

  /** Action type: 'add' = new items, 'update' = existing items */
  type: 'add' | 'update';

  /** Enable redemption points mode */
  useRedemptionPoints?: boolean;

  /** Items to show in the modal */
  items: Array<ItemDTO | ShoppingCartItemDTO>;

  /** Pre-configured pickup branch */
  pickupBranch?: BranchDTO;

  /** Pre-configured in-store branch */
  inStoreBranch?: BranchDTO;

  /** Pre-select a specific purchase option */
  preSelectOption?: {
    option: PurchaseOption;
    showOptionOnly?: boolean;
  };

  /** Purchase options to disable (no API calls) */
  disabledPurchaseOptions?: PurchaseOption[];

  /** Hide disabled options (true) or show as grayed out (false). Default: true */
  hideDisabledPurchaseOptions?: boolean;
}
```

### PurchaseOption Type

```typescript
type PurchaseOption =
  | 'delivery'        // Standard delivery
  | 'dig-delivery'    // Digital delivery
  | 'b2b-delivery'    // B2B delivery
  | 'pickup'          // Pickup at branch
  | 'in-store'        // Reserve in store
  | 'download'        // Digital download
  | 'catalog';        // Catalog availability
```

## Architecture

### Component Structure

```
purchase-options/
├── purchase-options-modal.component.ts    # Main modal component
├── purchase-options-modal.service.ts      # Service to open modal
├── purchase-options-modal.data.ts         # Data interfaces
├── store/
│   ├── purchase-options.store.ts          # NgRx ComponentStore
│   ├── purchase-options.service.ts        # Business logic service
│   ├── purchase-options.state.ts          # State interface
│   ├── purchase-options.types.ts          # Type definitions
│   └── purchase-options.selectors.ts      # State selectors
├── purchase-options-tile/
│   ├── base-purchase-option.directive.ts  # Base directive for tiles
│   ├── delivery-purchase-options-tile.component.ts
│   ├── pickup-purchase-options-tile.component.ts
│   ├── in-store-purchase-options-tile.component.ts
│   └── download-purchase-options-tile.component.ts
└── purchase-options-list-item/            # Item list components
```

### State Management

The modal uses NgRx ComponentStore for state management:
- **Availability Loading**: Parallel API calls for each enabled option
- **Can Add Validation**: Check if items can be added to cart
- **Item Selection**: Track selected items for batch operations
- **Branch Management**: Handle branch selection for pickup/in-store

### Disabled Options Flow

1. **Configuration**: `disabledPurchaseOptions` array passed to modal
2. **State**: Stored in store via `initialize()` method
3. **Availability Loading**: `isOptionDisabled()` check skips API calls
4. **UI Rendering**:
   - If `hideDisabledPurchaseOptions: true` → not rendered
   - If `hideDisabledPurchaseOptions: false` → rendered with `.disabled` class
5. **Click Prevention**: Disabled tiles prevent click action

## Common Use Cases

### 1. Regular Checkout
```typescript
// Show all options
await modalService.open({
  tabId: processId,
  shoppingCartId: cartId,
  type: 'add',
  items: catalogItems,
});
```

### 2. Reward Redemption
```typescript
// Pre-select in-store, disable B2B
await modalService.open({
  tabId: processId,
  shoppingCartId: rewardCartId,
  type: 'add',
  items: rewardItems,
  useRedemptionPoints: true,
  preSelectOption: { option: 'in-store' },
  disabledPurchaseOptions: ['b2b-delivery'],
});
```

### 3. Update Existing Item
```typescript
// Allow user to change delivery option
await modalService.open({
  tabId: processId,
  shoppingCartId: cartId,
  type: 'update',
  items: [existingCartItem],
});
```

### 4. Gift Cards (In-Store Only)
```typescript
// Show only in-store and delivery
await modalService.open({
  tabId: processId,
  shoppingCartId: cartId,
  type: 'add',
  items: [giftCardItem],
  disabledPurchaseOptions: ['pickup', 'b2b-delivery'],
});
```

## Testing

### Running Tests
```bash
# Run all tests for the app
npx nx test isa-app --skip-nx-cache
```

### Testing Disabled Options
When testing the disabled options feature:
1. Verify no API calls are made for disabled options
2. Check UI rendering based on `hideDisabledPurchaseOptions` flag
3. Ensure click events are prevented on disabled tiles
4. Validate backward compatibility (defaults work correctly)

## Migration Notes

### From `hidePurchaseOptions` to `disabledPurchaseOptions`
The field was renamed for clarity:
- **Old**: `hidePurchaseOptions` (ambiguous - hides from UI)
- **New**: `disabledPurchaseOptions` (clear - disabled functionality, may or may not be hidden)

This is a **breaking change** if you were using `hidePurchaseOptions`. Update all usages:

```typescript
// Before
hidePurchaseOptions: ['b2b-delivery']

// After
disabledPurchaseOptions: ['b2b-delivery']
```

## Troubleshooting

### Problem: Disabled option still making API calls
**Solution**: Ensure the option is in the `disabledPurchaseOptions` array and spelled correctly.

### Problem: Disabled option not showing even with `hideDisabledPurchaseOptions: false`
**Solution**: Check that `showOption()` logic and availability checks are working correctly.

### Problem: All options disabled
**Solution**: Don't disable all options. At least one option must be available for users to proceed.

## Related Documentation

- [Checkout Data Access](../../../libs/checkout/data-access/README.md)
- [Shopping Cart Flow](../../page/checkout/README.md)
- [Reward System](../../../libs/checkout/feature/reward-catalog/README.md)
