# @isa/oms/shared/task-list

A specialized Angular component library for displaying and managing return receipt item tasks in the OMS (Order Management System) domain.

## Overview

The Task List library provides a complete task management interface for return processes, allowing users to view, filter, and complete receipt item tasks. It supports two distinct visual modes (main workflow and review workflow) and integrates tightly with the OMS data-access layer through NgRx Signals stores. The library handles complex task interactions including task completion, status updates (OK/NOK), and Tolino return receipt printing.

## Table of Contents

- [Features](#features)
- [Quick Start](#quick-start)
- [Core Concepts](#core-concepts)
- [Component API Reference](#component-api-reference)
- [Usage Examples](#usage-examples)
- [Styling and Customization](#styling-and-customization)
- [Architecture Notes](#architecture-notes)
- [Dependencies](#dependencies)
- [Testing](#testing)
- [Best Practices](#best-practices)

## Features

- **Dual appearance modes** - Main workflow (card-based) and review workflow (table-based) layouts
- **Task filtering** - Automatic filtering of completed tasks in main view
- **Task sorting** - Open tasks displayed first, completed tasks at the end
- **Task completion** - One-click task completion with optimistic store updates
- **Task status updates** - Update task type to OK (verkaufsfähig) or NOK (defekt)
- **Tolino receipt printing** - Integrated Tolino return receipt generation
- **NgRx Signals integration** - Reactive state management with entity store
- **Tab-based isolation** - Process ID derived from tab context for multi-tab support
- **Deferred rendering** - Viewport-based lazy loading for performance
- **Product information display** - Rich product details for each task item
- **E2E testing attributes** - Comprehensive data attributes for automated testing
- **Error handling** - Comprehensive logging and error recovery

## Quick Start

### 1. Import and Use in Template

```typescript
import { Component } from '@angular/core';
import { ReturnTaskListComponent } from '@isa/oms/shared/task-list';

@Component({
  selector: 'app-return-search',
  standalone: true,
  imports: [ReturnTaskListComponent],
  template: `
    <oms-shared-return-task-list
      [appearance]="'main'"
    ></oms-shared-return-task-list>
  `
})
export class ReturnSearchComponent {}
```

### 2. Main Workflow Usage

```html
<!-- Use in return search workflow - shows incomplete tasks in card layout -->
<oms-shared-return-task-list
  [appearance]="'main'"
></oms-shared-return-task-list>
```

### 3. Review Workflow Usage

```html
<!-- Use in return review workflow - shows all tasks in table layout -->
<oms-shared-return-task-list
  [appearance]="'review'"
></oms-shared-return-task-list>
```

## Core Concepts

### Task List Workflow

The task list component manages the complete lifecycle of receipt item tasks:

1. **Automatic Data Fetching** - Component fetches tasks on initialization using tab-based process ID
2. **Task Display** - Tasks rendered with product information and action buttons
3. **User Interaction** - Users complete tasks, update task status, or print receipts
4. **Store Updates** - Optimistic UI updates via NgRx Signals store
5. **Re-filtering** - UI automatically updates based on task completion state

### Appearance Modes

The component supports two distinct visual modes optimized for different workflows:

#### Main Appearance (`appearance="main"`)
- **Purpose**: Primary workflow for processing return tasks
- **Layout**: Card-based vertical layout (24.25rem width cards)
- **Filtering**: Shows only incomplete tasks
- **Styling**: White cards with rounded corners and shadows
- **Scrolling**: Vertical scroll container for desktop (max-height: calc(100vh - 13rem))
- **Use Case**: Return search main view where staff process tasks one by one

#### Review Appearance (`appearance="review"`)
- **Purpose**: Review completed and pending tasks
- **Layout**: Grid-based table layout (responsive columns)
- **Filtering**: Shows all tasks (completed and incomplete)
- **Styling**: Minimal borders, table-row style
- **Scrolling**: Natural page scroll
- **Use Case**: Return review page where staff review all task states

### Task Action Types

Tasks can have three distinct action types that determine available user interactions:

```typescript
const TaskActionTypes = {
  OK: 'OK',         // Task marked as OK (product sellable)
  NOK: 'NOK',       // Task marked as NOK (product defective)
  UNKNOWN: 'UNKNOWN' // Task needs classification (OK or NOK)
} as const;
```

#### OK Tasks
- **Display**: "Als erledigt markieren" button
- **Action**: Complete task
- **Post-completion**: Shows "Abgeschlossen" with green checkmark

#### NOK Tasks
- **Display**: "Als erledigt markieren" button
- **Action**: Complete task
- **Post-completion**: Shows "Abgeschlossen" with green checkmark

#### UNKNOWN Tasks
- **Display**: Two action buttons (Ja verkaufsfähig / Nein, defekt)
- **Action**: Update task type to OK or NOK
- **Post-update**: Task re-fetched with new type, becomes completable

### Store Integration

The component integrates with `ReturnTaskListStore` (NgRx Signals) for reactive state management:

```typescript
// Store structure
type ReturnTaskListItems = AsyncResult<ReceiptItemTaskListItem[]> & {
  id: number;  // Process ID (from tab context)
};

// Entity map structure
{
  [processId: number]: {
    id: number,
    status: AsyncResultStatus,  // Idle | Pending | Success | Error
    data: ReceiptItemTaskListItem[] | undefined,
    error?: unknown
  }
}
```

**Key Store Operations:**
- `fetchTaskListItems()` - Fetches tasks with query filter
- `updateTaskListItem()` - Updates individual task after completion/update
- `beforeFetch()` - Sets status to Pending
- `fetchSuccess()` - Updates data and sets status to Success
- `fetchError()` - Logs error and sets status to Error

### Tab-Based Process Isolation

The component uses `injectTabId()` from `@isa/core/tabs` to derive the process ID:

```typescript
processId = injectTabId();  // Returns Signal<number | undefined>
```

**Benefits:**
- Multiple return processes can run simultaneously in different tabs
- Each tab maintains independent task list state in the store
- Process ID automatically updates when tab context changes
- No manual process ID management required

## Component API Reference

### ReturnTaskListComponent

Main container component that manages task list display and orchestrates task actions.

**Selector**: `oms-shared-return-task-list`

#### Inputs

##### `appearance`
- **Type**: `'main' | 'review'`
- **Default**: `'main'`
- **Description**: Visual mode controlling layout and filtering behavior
- **Required**: No

**Example:**
```html
<oms-shared-return-task-list
  [appearance]="'review'"
></oms-shared-return-task-list>
```

#### Outputs

This component has no outputs. Task actions are handled internally via services and store updates.

#### Host Bindings

**CSS Classes:**
- `oms-shared-return-task-list` - Always applied base class
- `oms-shared-return-task-list__main` - Applied when `appearance="main"`
- `oms-shared-return-task-list__review` - Applied when `appearance="review"`

#### Computed Signals

##### `taskListItems()`
- **Type**: `Signal<ReceiptItemTaskListItem[]>`
- **Description**: Filtered and sorted task list based on appearance mode
- **Filtering Logic**:
  - Main mode: Excludes completed tasks
  - Review mode: Shows all tasks
- **Sorting Logic**: Open tasks first, completed tasks last

**Example:**
```typescript
// In main mode
taskListItems() // Returns only incomplete tasks

// In review mode
taskListItems() // Returns all tasks (incomplete + completed)
```

##### `appearanceClass()`
- **Type**: `Signal<string>`
- **Description**: Dynamic CSS class based on appearance input
- **Values**: `'oms-shared-return-task-list__main'` | `'oms-shared-return-task-list__review'`

#### Public Methods

##### `handleAction(action: TaskActionType): Promise<void>`

Handles task actions emitted from child task list items.

**Parameters:**
- `action: TaskActionType` - Action object containing task details

**Task Action Structure:**
```typescript
interface TaskActionType {
  type: TaskActionTypeType;           // 'OK' | 'NOK' | 'UNKNOWN'
  taskId: number;                     // Task identifier
  receiptItemId?: number;             // Receipt item identifier (for Tolino)
  updateTo?: 'OK' | 'NOK';            // Update target type (for UNKNOWN)
  actions?: Array<KeyValue>;          // Action metadata (for Tolino)
}
```

**Behavior:**
1. **Tolino Receipt Printing**: If action contains `PRINT_TOLINO_RETURN_RECEIPT` key
2. **UNKNOWN Task Update**: If type is UNKNOWN with updateTo value
3. **Standard Completion**: All other cases

**Example:**
```typescript
// Handled automatically by child components
// Called internally when user clicks action buttons
```

##### `completeTask(taskId: number): Promise<void>`

Marks a task as completed and updates the store.

**Parameters:**
- `taskId: number` - Unique task identifier

**Side Effects:**
- Calls `ReturnTaskListService.completeTask()`
- Updates store via `ReturnTaskListStore.updateTaskListItem()`
- Logs errors if completion fails

**Error Handling:**
```typescript
try {
  await completeTask(123);
} catch (error) {
  // Automatically logged with context
  // UI remains stable - no error display to user
}
```

##### `updateTask({ taskId, updateTo }): Promise<void>`

Updates a task's type (OK or NOK) for UNKNOWN tasks.

**Parameters:**
- `taskId: number` - Unique task identifier
- `updateTo: 'OK' | 'NOK'` - Target task type

**Side Effects:**
- Calls `ReturnTaskListService.updateTaskType()`
- Updates store via `ReturnTaskListStore.updateTaskListItem()`
- Logs errors if update fails

**Example:**
```typescript
// Handled automatically when user clicks "Ja verkaufsfähig"
await updateTask({ taskId: 123, updateTo: 'OK' });

// Handled automatically when user clicks "Nein, defekt"
await updateTask({ taskId: 123, updateTo: 'NOK' });
```

### ReturnTaskListItemComponent

Individual task list item component rendering product info and action buttons.

**Selector**: `oms-shared-return-task-list-item`

#### Inputs

##### `appearance`
- **Type**: `'main' | 'review'`
- **Default**: `'main'`
- **Description**: Visual mode inherited from parent component
- **Required**: No

##### `item`
- **Type**: `ReceiptItemTaskListItem`
- **Required**: Yes
- **Description**: Task item data including product, status, and actions

**Item Structure:**
```typescript
interface ReceiptItemTaskListItem {
  id: number;                         // Task ID
  type: TaskActionTypeType;           // 'OK' | 'NOK' | 'UNKNOWN'
  completed: boolean;                 // Completion status
  processingComment?: string;         // Task description/comment
  product?: Product;                  // Product information
  receiptItem?: { id: number };       // Receipt item reference
  actions?: Array<KeyValue>;          // Available actions (e.g., Tolino print)
}
```

#### Outputs

##### `action`
- **Type**: `EventEmitter<TaskActionType>`
- **Description**: Emits task action when user interacts with buttons
- **Payload**: TaskActionType object with task details

**Example:**
```typescript
// Emitted when user clicks completion button
{ type: 'OK', taskId: 123 }

// Emitted when user clicks "Ja verkaufsfähig"
{ type: 'UNKNOWN', taskId: 123, updateTo: 'OK' }

// Emitted when user clicks print receipt
{
  type: 'OK',
  taskId: 123,
  receiptItemId: 456,
  actions: [{ key: 'PRINT_TOLINO_RETURN_RECEIPT', value: '...' }]
}
```

#### Computed Signals

##### `product()`
- **Type**: `Signal<Product | undefined>`
- **Description**: Extracted product information from task item

##### `processingComment()`
- **Type**: `Signal<string | undefined>`
- **Description**: Task description/comment text

##### `displayPrintTolino()`
- **Type**: `Signal<boolean>`
- **Description**: Whether to show Tolino print button (checks for `PRINT_TOLINO_RETURN_RECEIPT` action)

##### `type()`
- **Type**: `Signal<TaskActionTypeType>`
- **Description**: Task action type (OK | NOK | UNKNOWN)

#### Host Bindings

**CSS Classes:**
- `oms-shared-return-task-list-item` - Always applied base class
- `oms-shared-return-task-list-item__main` - Applied when `appearance="main"`
- `oms-shared-return-task-list-item__review` - Applied when `appearance="review"`

## Usage Examples

### Basic Main Workflow

```typescript
import { Component } from '@angular/core';
import { ReturnTaskListComponent } from '@isa/oms/shared/task-list';

@Component({
  selector: 'app-return-search-main',
  standalone: true,
  imports: [ReturnTaskListComponent],
  template: `
    <div class="return-search-page">
      <h1>Rückgabe starten</h1>

      <!-- Search and filter components here -->

      <oms-shared-return-task-list
        [appearance]="'main'"
      ></oms-shared-return-task-list>
    </div>
  `
})
export class ReturnSearchMainComponent {}
```

### Review Workflow with Header

```typescript
import { Component } from '@angular/core';
import { ReturnTaskListComponent } from '@isa/oms/shared/task-list';

@Component({
  selector: 'app-return-review',
  standalone: true,
  imports: [ReturnTaskListComponent],
  template: `
    <div class="return-review-page">
      <header>
        <h1>Aufgaben überprüfen</h1>
        <button (click)="printReceipt()">Beleg drucken</button>
      </header>

      <oms-shared-return-task-list
        [appearance]="'review'"
      ></oms-shared-return-task-list>
    </div>
  `
})
export class ReturnReviewComponent {
  printReceipt(): void {
    // Receipt printing logic
  }
}
```

### Dynamic Appearance Mode

```typescript
import { Component, signal } from '@angular/core';
import { ReturnTaskListComponent } from '@isa/oms/shared/task-list';

@Component({
  selector: 'app-task-manager',
  standalone: true,
  imports: [ReturnTaskListComponent],
  template: `
    <div>
      <button (click)="toggleMode()">
        {{ viewMode() === 'main' ? 'Zur Übersicht' : 'Zur Bearbeitung' }}
      </button>

      <oms-shared-return-task-list
        [appearance]="viewMode()"
      ></oms-shared-return-task-list>
    </div>
  `
})
export class TaskManagerComponent {
  viewMode = signal<'main' | 'review'>('main');

  toggleMode(): void {
    this.viewMode.update(mode => mode === 'main' ? 'review' : 'main');
  }
}
```

### Integration with Custom Layout

```typescript
import { Component } from '@angular/core';
import { ReturnTaskListComponent } from '@isa/oms/shared/task-list';

@Component({
  selector: 'app-custom-return-layout',
  standalone: true,
  imports: [ReturnTaskListComponent],
  template: `
    <div class="grid grid-cols-3 gap-6">
      <!-- Left sidebar with filters -->
      <aside class="col-span-1">
        <h2>Filter</h2>
        <!-- Filter components -->
      </aside>

      <!-- Main content area with task list -->
      <main class="col-span-2">
        <oms-shared-return-task-list
          [appearance]="'main'"
        ></oms-shared-return-task-list>
      </main>
    </div>
  `
})
export class CustomReturnLayoutComponent {}
```

### Accessing Store Directly (Advanced)

```typescript
import { Component, inject, computed } from '@angular/core';
import { ReturnTaskListStore } from '@isa/oms/data-access';
import { injectTabId } from '@isa/core/tabs';
import { ReturnTaskListComponent } from '@isa/oms/shared/task-list';

@Component({
  selector: 'app-advanced-task-view',
  standalone: true,
  imports: [ReturnTaskListComponent],
  template: `
    <div>
      @if (taskCount() > 0) {
        <p>{{ taskCount() }} offene Aufgaben</p>
      }

      <oms-shared-return-task-list
        [appearance]="'main'"
      ></oms-shared-return-task-list>
    </div>
  `
})
export class AdvancedTaskViewComponent {
  #store = inject(ReturnTaskListStore);
  processId = injectTabId();

  taskCount = computed(() => {
    const processId = this.processId();
    if (!processId) return 0;

    const entity = this.#store.entityMap()[processId];
    return entity?.data?.filter(item => !item.completed).length ?? 0;
  });
}
```

### Manual Task Refresh (Advanced)

```typescript
import { Component, inject } from '@angular/core';
import { ReturnTaskListStore, QueryTokenInput } from '@isa/oms/data-access';
import { injectTabId } from '@isa/core/tabs';
import { ReturnTaskListComponent } from '@isa/oms/shared/task-list';

@Component({
  selector: 'app-refreshable-task-list',
  standalone: true,
  imports: [ReturnTaskListComponent],
  template: `
    <div>
      <button (click)="refreshTasks()">Aktualisieren</button>

      <oms-shared-return-task-list
        [appearance]="'main'"
      ></oms-shared-return-task-list>
    </div>
  `
})
export class RefreshableTaskListComponent {
  #store = inject(ReturnTaskListStore);
  processId = injectTabId();

  refreshTasks(): void {
    const processId = this.processId();
    if (!processId) return;

    const queryToken: QueryTokenInput = {
      filter: { completed: false }
    };

    this.#store.fetchTaskListItems({ processId, queryToken });
  }
}
```

## Styling and Customization

### Component Structure

The component emits a host class that controls the layout mode:

```scss
// Main appearance - card layout
.oms-shared-return-task-list__main {
  @apply flex flex-col items-center gap-6 justify-self-center;
}

// Review appearance - table layout
.oms-shared-return-task-list__review {
  // Layout controlled by child items
}
```

### Task List Container (Main Mode)

```scss
.return-search-main-task-list-styles {
  @apply gap-6
         desktop:overflow-y-scroll
         desktop:max-h-[calc(100vh-13rem)]
         justify-start;
}
```

**Customization:**
- Adjust `max-h-[calc(100vh-13rem)]` for different header heights
- Modify `gap-6` (24px) for different card spacing
- Override `overflow-y-scroll` behavior for custom scroll containers

### Task Item Cards (Main Mode)

```scss
.oms-shared-return-task-list-item__main {
  @apply bg-white
         rounded-2xl
         p-6
         flex
         flex-col
         gap-6
         w-[24.25rem];
}
```

**Customization:**
```scss
// Custom card width
.oms-shared-return-task-list-item__main {
  width: 20rem; // Override default 24.25rem
}

// Custom card padding
.oms-shared-return-task-list-item__main {
  padding: 2rem; // Override default 1.5rem (p-6)
}

// Custom card shadow
.oms-shared-return-task-list-item__main {
  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
}
```

### Task Item Table Rows (Review Mode)

```scss
.oms-shared-return-task-list-item__review {
  @apply grid
         grid-cols-[1fr,1fr]
         desktop:grid-cols-[1fr,1fr,minmax(20rem,auto)]
         gap-x-6
         desktop:gap-y-6
         py-6
         text-isa-secondary-900
         items-center
         border-b
         border-solid
         border-isa-neutral-300
         last:pb-0
         last:border-none;
}
```

**Responsive Grid Areas (Tablet/Mobile):**
```scss
@media screen and (max-width: 1024px) {
  grid-template-areas:
    "product infos"
    "unknown-comment actions";
}
```

**Customization:**
```scss
// Custom column proportions
.oms-shared-return-task-list-item__review {
  grid-template-columns: 2fr 1fr minmax(15rem, auto);
}

// Custom row padding
.oms-shared-return-task-list-item__review {
  padding-top: 2rem;
  padding-bottom: 2rem;
}

// Custom border color
.oms-shared-return-task-list-item__review {
  border-bottom-color: #e5e7eb; // Override isa-neutral-300
}
```

### Processing Comment Styling

```scss
// Standard tasks (OK/NOK)
.processing-comment {
  @apply isa-text-body-2-bold;
}

// UNKNOWN tasks (larger emphasis)
.processing-comment-unknown {
  @apply isa-text-body-1-regular;
}
```

### Task Action Buttons

```scss
// UNKNOWN task action buttons container
.task-unknown-actions {
  @apply flex
         flex-row
         gap-3
         h-full
         py-2
         items-center
         justify-self-end;
}

// Tolino print button positioning
.tolino-print-cta {
  @apply w-fit
         self-end
         justify-self-end
         desktop:self-start;
}
```

### Task Info Container

```scss
.task-item-infos {
  @apply h-full
         p-4
         flex
         flex-col
         gap-4
         rounded-lg
         bg-isa-secondary-100;
}
```

**Customization:**
```scss
// Custom background color
.task-item-infos {
  background-color: #f9fafb; // Override isa-secondary-100
}

// Custom padding
.task-item-infos {
  padding: 1rem; // Override p-4 (1rem)
}
```

### Responsive Behavior

#### Main Mode Breakpoints
```scss
// Desktop: Scrollable container
@media (min-width: 1024px) {
  .return-search-main-task-list-styles {
    overflow-y: scroll;
    max-height: calc(100vh - 13rem);
  }
}
```

#### Review Mode Breakpoints
```scss
// Tablet/Mobile: Stacked layout
@media screen and (max-width: 1024px) {
  .oms-shared-return-task-list-item__review {
    grid-template-areas:
      "product infos"
      "unknown-comment actions";
  }
}

// Desktop: Three-column layout
@media (min-width: 1024px) {
  .oms-shared-return-task-list-item__review {
    grid-template-columns: 1fr 1fr minmax(20rem, auto);
  }
}
```

### Theme Integration

The component uses ISA design system colors:

```scss
// Text colors
text-isa-secondary-900    // Main text
text-isa-accent-green     // Completed status

// Background colors
bg-white                  // Card backgrounds
bg-isa-secondary-100      // Info containers

// Border colors
border-isa-neutral-300    // Row separators
```

**Custom Theme:**
```scss
// Override in your global styles
.oms-shared-return-task-list-item__main {
  --card-bg: theme('colors.white');
  --info-bg: theme('colors.isa.secondary.100');

  background-color: var(--card-bg);

  .task-item-infos {
    background-color: var(--info-bg);
  }
}
```

### E2E Testing Attributes

All interactive elements include data attributes for E2E testing:

```html
<!-- Task list container -->
<div data-what="task-list" data-which="processing-comment">

<!-- Action buttons -->
<button data-what="button" data-which="complete">
<button data-what="button" data-which="resellable">
<button data-what="button" data-which="damaged">
<button data-what="button" data-which="print-receipt">

<!-- Status indicators -->
<span data-what="info" data-which="completed">

<!-- Components -->
<oms-shared-return-product-info data-what="component" data-which="return-product-info">
```

**Using in Tests:**
```typescript
// Playwright example
await page.click('[data-what="button"][data-which="complete"]');
await expect(page.locator('[data-what="info"][data-which="completed"]')).toBeVisible();
```

## Architecture Notes

### Current Architecture

```
Components/Features (return-search, return-review)
       ↓
  ReturnTaskListComponent (orchestrator)
       ↓
├─→ ReturnTaskListItemComponent (presentation)
├─→ ReturnProductInfoComponent (product display)
├─→ ReturnTaskListStore (state management)
├─→ ReturnTaskListService (business logic)
├─→ PrintTolinoReturnReceiptService (printing)
└─→ Tab Context (process isolation)
```

### Design Patterns

#### 1. Container/Presenter Pattern
- **Container**: `ReturnTaskListComponent` handles state, effects, and actions
- **Presenter**: `ReturnTaskListItemComponent` handles display and emits events
- **Benefits**: Clear separation of concerns, easier testing, reusable presenters

#### 2. Signal-Based Reactivity
```typescript
// Computed signals for derived state
taskListItems = computed(() => {
  const processId = this.processId();
  const appearance = this.appearance();

  // Filter and sort based on inputs
  return filteredAndSortedItems;
});
```

**Benefits:**
- Automatic dependency tracking
- Efficient change detection
- Type-safe reactive data flow

#### 3. Effect-Based Data Fetching
```typescript
constructor() {
  effect(() => {
    const processId = this.processId();
    const appearance = this.appearance();

    if (processId) {
      const filter = appearance === 'review'
        ? { eob: true }
        : { completed: false };

      untracked(() =>
        this.#store.fetchTaskListItems({ processId, filter })
      );
    }
  });
}
```

**Benefits:**
- Automatic refetch when dependencies change
- Untracked execution prevents infinite loops
- Declarative data synchronization

#### 4. Optimistic UI Updates
```typescript
async completeTask(taskId: number) {
  const result = await this.#service.completeTask(taskId);

  // Update store immediately (optimistic)
  if (result && processId) {
    this.#store.updateTaskListItem({ processId, taskListItem: result });
  }
}
```

**Benefits:**
- Immediate UI feedback
- Better perceived performance
- Automatic rollback on error

#### 5. Tab-Based Multi-Instance Support
```typescript
processId = injectTabId();  // Signal from tab context
```

**Benefits:**
- Multiple simultaneous return processes
- Isolated state per tab
- No cross-tab interference

### Known Architectural Considerations

#### 1. Component Review Needed (High Priority)

**Issue**: Component header comment indicates review needed
```typescript
// TODO: Komponente und logik benötigt review
```

**Current State:**
- Component works but may have architectural improvements pending
- Logic patterns established but not formally reviewed
- No known bugs or functional issues

**Proposed Review Areas:**
- Error handling consistency
- Store update patterns (optimistic vs pessimistic)
- Effect dependency management
- Template complexity reduction

**Impact**: Medium - component is functional, review would improve maintainability

#### 2. Deferred Rendering Performance (Low Priority)

**Current State:**
```html
@for (item of taskList; track item.id) {
  @defer (on viewport) {
    <oms-shared-return-task-list-item />
  } @placeholder {
    <!-- Spinner placeholder -->
  }
}
```

**Observation:**
- Uses spinner as placeholder (TODO notes suggest skeleton loader)
- Viewport-based loading may cause layout shifts
- No prefetching strategy

**Proposed Enhancements:**
- Replace spinner with skeleton loader matching item dimensions
- Add `@defer (on viewport; prefetch on idle)` for better UX
- Consider virtual scrolling for large task lists

**Impact**: Low - current implementation works, improvements would enhance UX

#### 3. Filter Query Complexity (Medium Priority)

**Current State:**
```typescript
const filter: Record<string, unknown> =
  appearance === 'review' ? { eob: true } : { completed: false };
```

**Observation:**
- Simple boolean filter for review mode (`eob: true`)
- Incomplete task filter for main mode (`completed: false`)
- No pagination or advanced filtering

**Potential Issues:**
- Large task lists not paginated (could impact performance)
- Filter criteria hardcoded (not extensible)
- No user-configurable filtering

**Proposed Enhancements:**
- Extract filter logic to separate helper function
- Add pagination support via QueryToken
- Allow custom filter injection via component input
- Add sort options (by date, product, type)

**Impact**: Medium - works for current use cases, scalability concerns

#### 4. Error Recovery Strategy (Medium Priority)

**Current State:**
```typescript
catch (error) {
  this.#logger.error('Error completing task', error);
  // No user feedback or retry mechanism
}
```

**Observation:**
- Errors logged but not surfaced to UI
- No retry mechanism for failed operations
- User has no indication task completion failed
- Store may be in inconsistent state after error

**Proposed Enhancements:**
- Add toast notification on task action failure
- Implement retry mechanism with exponential backoff
- Show error indicator on failed task items
- Revert optimistic updates on error

**Impact**: Medium - impacts user experience when errors occur

### Performance Considerations

#### 1. Viewport-Based Rendering
- Only renders tasks visible in viewport
- Reduces initial render time for large lists
- Trade-off: Layout shifts on scroll

#### 2. Change Detection Optimization
```typescript
changeDetection: ChangeDetectionStrategy.OnPush
```
- Only checks component when inputs change or events fire
- Reduces unnecessary change detection cycles
- Compatible with signal-based reactivity

#### 3. Store Entity Management
```typescript
// Efficient entity lookup by process ID
const tasks = this.#store.entityMap()[processId].data;
```
- O(1) lookup time via object map
- No array iteration for entity access
- Normalized data structure

#### 4. Task Item Tracking
```html
@for (item of taskList; track item.id) {
```
- Angular tracks items by ID for efficient DOM updates
- Prevents unnecessary re-renders on list changes
- Stable item references

### Future Enhancements

Potential improvements identified:

1. **Skeleton Loading** - Replace spinner placeholders with skeleton UI
2. **Virtual Scrolling** - Add `@angular/cdk` virtual scroll for large lists
3. **Undo Functionality** - Allow task completion undo within timeout
4. **Bulk Actions** - Multi-select and bulk complete/update
5. **Advanced Filtering** - User-configurable filters and sorting
6. **Pagination** - Add pagination for large task lists
7. **Error UI** - User-visible error states and retry buttons
8. **Accessibility** - ARIA labels and keyboard navigation
9. **Analytics** - Track task completion rates and times
10. **Export Functionality** - Export task list to CSV/PDF

## Dependencies

### Required Libraries

#### Angular Core
- `@angular/core` - Angular framework, signals, effects, dependency injection

#### OMS Domain
- `@isa/oms/data-access` - Data services, stores, and models
  - `ReturnTaskListService` - Task CRUD operations
  - `ReturnTaskListStore` - NgRx Signals store
  - `PrintTolinoReturnReceiptService` - Receipt printing
  - `ReceiptItemTaskListItem` - Task data model
  - `TaskActionType` - Action type definitions
  - `QueryTokenInput` - Query parameters

#### OMS Shared
- `@isa/oms/shared/product-info` - Product display component

#### UI Components
- `@isa/ui/buttons` - Button components
  - `ButtonComponent` - Standard button
  - `IconButtonComponent` - Icon-only button
  - `InfoButtonComponent` - Info button with icon

#### Core Services
- `@isa/core/tabs` - Tab context management
  - `injectTabId()` - Process ID from tab context
- `@isa/core/logging` - Logging infrastructure
  - `logger()` - Logger instance injection
  - `provideLoggerContext()` - Logger context provider

#### Icons
- `@isa/icons` - ISA icon set
  - `isaActionCheck` - Checkmark icon
  - `isaActionPrinter` - Printer icon
- `@ng-icons/core` - Icon component infrastructure

#### State Management
- `@ngrx/signals` - Signal-based state management
- `@ngrx/operators` - RxJS operators for NgRx

#### Utilities
- `rxjs` - Reactive programming
- `zod` - Schema validation (indirect via services)

### Generated API Clients
- `@generated/swagger/oms-api` - OMS API client (indirect via services)

### Development Dependencies
- `jest` - Testing framework
- `jest-preset-angular` - Angular-specific Jest configuration
- `@angular/compiler-cli` - Angular compiler
- `typescript` - TypeScript compiler

### Path Alias

Import from: `@isa/oms/shared/task-list`

**Example:**
```typescript
import { ReturnTaskListComponent } from '@isa/oms/shared/task-list';
```

## Testing

The library uses **Jest** with **Angular Testing Utilities** for testing.

### Running Tests

```bash
# Run tests for this library
npx nx test return-task-list --skip-nx-cache

# Run tests with coverage
npx nx test return-task-list --code-coverage --skip-nx-cache

# Run tests in watch mode
npx nx test return-task-list --watch

# Run specific test file
npx nx test return-task-list --testFile=src/lib/return-task-list/return-task-list.component.spec.ts --skip-nx-cache
```

### Test Structure (Recommended)

#### Testing ReturnTaskListComponent

```typescript
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { describe, it, expect, beforeEach, jest } from '@jest/globals';
import { ReturnTaskListComponent } from './return-task-list.component';
import { ReturnTaskListStore } from '@isa/oms/data-access';
import { signal } from '@angular/core';

describe('ReturnTaskListComponent', () => {
  let component: ReturnTaskListComponent;
  let fixture: ComponentFixture<ReturnTaskListComponent>;
  let mockStore: jest.Mocked<typeof ReturnTaskListStore>;

  beforeEach(async () => {
    // Mock store
    mockStore = {
      entityMap: jest.fn(() => ({})),
      fetchTaskListItems: jest.fn(),
      updateTaskListItem: jest.fn()
    } as any;

    await TestBed.configureTestingModule({
      imports: [ReturnTaskListComponent],
      providers: [
        { provide: ReturnTaskListStore, useValue: mockStore }
      ]
    }).compileComponents();

    fixture = TestBed.createComponent(ReturnTaskListComponent);
    component = fixture.componentInstance;
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  it('should filter completed tasks in main mode', () => {
    // Arrange
    fixture.componentRef.setInput('appearance', 'main');
    const mockTasks = [
      { id: 1, completed: false },
      { id: 2, completed: true },
      { id: 3, completed: false }
    ];
    mockStore.entityMap.mockReturnValue({
      123: { data: mockTasks, status: 'Success', id: 123 }
    });

    // Act
    const tasks = component.taskListItems();

    // Assert
    expect(tasks).toHaveLength(2);
    expect(tasks.every(t => !t.completed)).toBe(true);
  });

  it('should show all tasks in review mode', () => {
    // Arrange
    fixture.componentRef.setInput('appearance', 'review');
    const mockTasks = [
      { id: 1, completed: false },
      { id: 2, completed: true }
    ];
    mockStore.entityMap.mockReturnValue({
      123: { data: mockTasks, status: 'Success', id: 123 }
    });

    // Act
    const tasks = component.taskListItems();

    // Assert
    expect(tasks).toHaveLength(2);
  });

  it('should complete task and update store', async () => {
    // Arrange
    const mockService = {
      completeTask: jest.fn(() => Promise.resolve({ id: 1, completed: true }))
    };
    component['#returnTaskListService'] = mockService as any;

    // Act
    await component.completeTask(1);

    // Assert
    expect(mockService.completeTask).toHaveBeenCalledWith(1);
    expect(mockStore.updateTaskListItem).toHaveBeenCalled();
  });
});
```

#### Testing ReturnTaskListItemComponent

```typescript
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { describe, it, expect, beforeEach } from '@jest/globals';
import { ReturnTaskListItemComponent } from './return-task-list-item.component';
import { ReceiptItemTaskListItem } from '@isa/oms/data-access';

describe('ReturnTaskListItemComponent', () => {
  let component: ReturnTaskListItemComponent;
  let fixture: ComponentFixture<ReturnTaskListItemComponent>;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      imports: [ReturnTaskListItemComponent]
    }).compileComponents();

    fixture = TestBed.createComponent(ReturnTaskListItemComponent);
    component = fixture.componentInstance;
  });

  it('should emit action on complete button click', () => {
    // Arrange
    const mockItem: ReceiptItemTaskListItem = {
      id: 123,
      type: 'OK',
      completed: false
    };
    fixture.componentRef.setInput('item', mockItem);

    let emittedAction;
    component.action.subscribe(action => emittedAction = action);

    // Act
    component.onActionClick({ type: 'OK' });

    // Assert
    expect(emittedAction).toEqual({
      taskId: 123,
      type: 'OK'
    });
  });

  it('should display UNKNOWN task action buttons', () => {
    // Arrange
    const mockItem: ReceiptItemTaskListItem = {
      id: 123,
      type: 'UNKNOWN',
      completed: false,
      processingComment: 'Prüfen Sie das Produkt'
    };
    fixture.componentRef.setInput('item', mockItem);

    // Act
    fixture.detectChanges();

    // Assert
    const buttons = fixture.nativeElement.querySelectorAll(
      '[data-what="button"]'
    );
    expect(buttons.length).toBe(2); // OK and NOK buttons
  });

  it('should show completed status when task is completed', () => {
    // Arrange
    const mockItem: ReceiptItemTaskListItem = {
      id: 123,
      type: 'OK',
      completed: true
    };
    fixture.componentRef.setInput('item', mockItem);

    // Act
    fixture.detectChanges();

    // Assert
    const completedInfo = fixture.nativeElement.querySelector(
      '[data-what="info"][data-which="completed"]'
    );
    expect(completedInfo).toBeTruthy();
    expect(completedInfo.textContent).toContain('Abgeschlossen');
  });

  it('should display Tolino print button when action exists', () => {
    // Arrange
    const mockItem: ReceiptItemTaskListItem = {
      id: 123,
      type: 'OK',
      completed: false,
      actions: [
        { key: 'PRINT_TOLINO_RETURN_RECEIPT', value: 'true' }
      ]
    };
    fixture.componentRef.setInput('item', mockItem);

    // Act
    fixture.detectChanges();

    // Assert
    const printButton = fixture.nativeElement.querySelector(
      '[data-which="print-receipt"]'
    );
    expect(printButton).toBeTruthy();
  });
});
```

### Test Coverage Goals

- **Component Logic**: 80%+ coverage
- **Template Rendering**: Test all conditional paths (@if, @for)
- **User Interactions**: Test all button clicks and events
- **Store Integration**: Mock store methods and verify calls
- **E2E Attributes**: Verify all data-what/data-which attributes exist

### E2E Testing

The component includes comprehensive E2E testing attributes:

```typescript
// Example Playwright test
import { test, expect } from '@playwright/test';

test.describe('Return Task List', () => {
  test('should complete task on button click', async ({ page }) => {
    // Navigate to page with task list
    await page.goto('/return-search');

    // Click complete button
    await page.click('[data-what="button"][data-which="complete"]');

    // Verify task marked as completed
    await expect(
      page.locator('[data-what="info"][data-which="completed"]')
    ).toBeVisible();
  });

  test('should update UNKNOWN task to OK', async ({ page }) => {
    await page.goto('/return-search');

    // Click "Ja verkaufsfähig" button
    await page.click('[data-what="button"][data-which="resellable"]');

    // Verify complete button appears (task type updated)
    await expect(
      page.locator('[data-what="button"][data-which="complete"]')
    ).toBeVisible();
  });

  test('should print Tolino receipt', async ({ page }) => {
    await page.goto('/return-search');

    // Click print receipt button
    const [download] = await Promise.all([
      page.waitForEvent('download'),
      page.click('[data-what="button"][data-which="print-receipt"]')
    ]);

    // Verify download started
    expect(download).toBeTruthy();
  });
});
```

## Best Practices

### 1. Always Use Appearance Mode Appropriately

```typescript
// ✅ GOOD: Main workflow for processing tasks
<oms-shared-return-task-list [appearance]="'main'"></oms-shared-return-task-list>

// ✅ GOOD: Review workflow for checking all tasks
<oms-shared-return-task-list [appearance]="'review'"></oms-shared-return-task-list>

// ❌ BAD: Using review mode in main workflow
// (Shows completed tasks when user expects only open tasks)
```

### 2. Rely on Tab Context for Process ID

```typescript
// ✅ GOOD: Component automatically uses tab-based process ID
// No manual process ID management needed
<oms-shared-return-task-list [appearance]="'main'"></oms-shared-return-task-list>

// ❌ BAD: Trying to manually manage process IDs
// Component handles this internally via injectTabId()
```

### 3. Trust Optimistic Updates

```typescript
// ✅ GOOD: Let component handle store updates
// User sees immediate feedback
await component.completeTask(123);

// ❌ BAD: Manual store updates after completion
// Creates race conditions and duplicate updates
await service.completeTask(123);
store.updateTaskListItem(...); // Component already does this
```

### 4. Use E2E Attributes in Custom Templates

```typescript
// ✅ GOOD: Include data attributes for testing
<button
  (click)="completeTask()"
  data-what="button"
  data-which="custom-complete"
>
  Complete Task
</button>

// ❌ BAD: No testing attributes
<button (click)="completeTask()">
  Complete Task
</button>
```

### 5. Monitor Store State for Advanced Use Cases

```typescript
// ✅ GOOD: Access store for derived data only
taskCount = computed(() => {
  const entity = this.#store.entityMap()[this.processId()];
  return entity?.data?.filter(item => !item.completed).length ?? 0;
});

// ❌ BAD: Direct store manipulation
this.#store.entityMap()[processId].data = newData; // Use updateTaskListItem()
```

### 6. Handle Appearance Mode Switching Carefully

```typescript
// ✅ GOOD: Consider data refetch when switching modes
const appearance = signal<'main' | 'review'>('main');

effect(() => {
  // Component effect will refetch with appropriate filter
  console.log('Appearance changed to', appearance());
});

// ❌ BAD: Switching without understanding filter implications
// Review mode shows EOB tasks, main mode shows incomplete tasks
```

### 7. Respect Component Encapsulation

```typescript
// ✅ GOOD: Use component as black box
<oms-shared-return-task-list [appearance]="mode()"></oms-shared-return-task-list>

// ❌ BAD: Trying to access internal component state
const tasks = component.taskListItems(); // Component manages this
```

### 8. Style with CSS Classes, Not Inline Styles

```scss
// ✅ GOOD: Override via CSS classes
.oms-shared-return-task-list-item__main {
  width: 20rem;
  padding: 2rem;
}

// ❌ BAD: Inline styles in template
<oms-shared-return-task-list
  style="width: 20rem;"
></oms-shared-return-task-list>
```

### 9. Leverage Deferred Rendering Performance

```typescript
// ✅ GOOD: Component handles viewport-based loading
// Large task lists render efficiently
<oms-shared-return-task-list [appearance]="'main'"></oms-shared-return-task-list>

// ❌ BAD: Manual pagination when component handles it
// Trust the @defer implementation
```

### 10. Consider Accessibility

```typescript
// ✅ GOOD: Wrap in semantic HTML
<main role="main">
  <h1>Aufgaben</h1>
  <oms-shared-return-task-list [appearance]="'main'"></oms-shared-return-task-list>
</main>

// ⚠️ CONSIDERATION: Component could add ARIA labels
// Future enhancement for screen reader support
```

### 11. Error Handling Pattern

```typescript
// ✅ GOOD: Component logs errors automatically
// Monitor logs for task completion failures
// Consider adding error UI in future enhancement

// ⚠️ CURRENT LIMITATION: Errors not visible to users
// Plan for user feedback in production deployments
```

### 12. Testing Strategy

```typescript
// ✅ GOOD: Test component behavior, not implementation
it('should complete task', async () => {
  const button = fixture.nativeElement.querySelector('[data-which="complete"]');
  button.click();

  await fixture.whenStable();

  expect(mockStore.updateTaskListItem).toHaveBeenCalled();
});

// ❌ BAD: Testing internal signals
it('should have taskListItems signal', () => {
  expect(component.taskListItems).toBeDefined(); // Too implementation-specific
});
```

---

## License

Internal ISA Frontend library - not for external distribution.
