<!-- source-hash: 9effa67e5fd33a6782ade58cc9bf19ec -->
Customer-scoped ticket list hook that wraps `POST /api/chat/agent/find-ticket`, supporting free-text search, status filtering, and server-side pagination with a single round-trip returning both rows and total count.

## Key Components

### `useTicketsList(filters)`
Main exported hook. Accepts a `UseTicketsListFilters` object and returns `UseTicketsListReturn`.

### `UseTicketsListFilters`
| Field | Type | Description |
|-------|------|-------------|
| `customerEmail` | `string` | Resolved identity from parent — **must** be drilled in, not re-fetched |
| `search` | `string?` | Free-text FTS query; empty = list all tickets |
| `status` | `string?` | `'open'` \| `'closed'` \| `'all'` — empty/`'all'` skips filter |
| `page` | `number?` | 1-based page number, defaults to `1` |
| `pageSize` | `number?` | Items per page, server-capped at `100`, defaults to `20` |

### `UseTicketsListReturn`
| Field | Type | Description |
|-------|------|-------------|
| `tickets` | `TicketData[]` | Current page of tickets |
| `totalCount` | `number` | Total across all pages (drives pagination) |
| `totalPages` | `number` | Pre-computed, clamped to ≥1 |
| `page` / `pageSize` | `number` | Server-echoed values |
| `isLoading` | `boolean` | `true` when no data exists for the current query slot |
| `isFetching` | `boolean` | `true` during any background refetch |
| `lastUpdatedAt` | `number \| null` | Wall-clock timestamp of last successful fetch |

## Cache Behavior

- **Key:** `['tickets', 'self', identity, search, status, page, pageSize]` — each filter+page combo occupies its own slot
- **Caching disabled** (`staleTime: 0`, `gcTime: 0`) — always fetches fresh data on mount, focus, and navigation
- **Invalidation:** `useTicketActions` calls `queryClient.invalidateQueries({ queryKey: ['tickets'] })` after mutations, refreshing all slots

## Usage Example

```typescript
const {
  tickets,
  isLoading,
  totalCount,
  totalPages,
  page,
  pageSize,
} = useTicketsList({
  customerEmail: identity.user.email,
  search: 'printer not working',
  status: 'open',
  page: 1,
  pageSize: 20,
})
```

> **Note:** Never call `useChatIdentity` inside this hook. Always drill `customerEmail` from the parent to avoid a null-identity race that causes an `EmptyState` flash before the first fetch fires.