# ADM-018 Implementer Evidence

## Summary
Enhanced `GET /users` backend to support admin customer listing with search, pagination, and total count. Updated frontend to consume the new response shape.

## Changes

### Backend (users module)

**Domain layer** (`profile.ts`):
- Added `CustomerSummary` interface (userId, email, role, displayName, phone, createdAt)
- Added `CustomerListResult` interface (items, total)
- Added `CustomerListOptions` interface (q, offset, limit)

**Ports** (`ports.ts`):
- Added `listCustomers(opts)` and `findCustomerById(userId)` to `ProfileRepository`

**Application** (`profile-use-cases.ts`):
- Added `ListCustomers` use case
- Added `GetCustomer` use case

**Infrastructure** (`pg-profile-repository.ts`):
- Implemented `listCustomers()`: JOIN identity_users + users_profiles, ILIKE search on email, LIMIT/OFFSET pagination, COUNT for total
- Implemented `findCustomerById()`: single customer lookup with JOIN

**API** (`users.routes.ts`):
- `GET /users` now accepts `q`, `offset`, `limit` query params; returns `{ items, total }`
- `GET /users/:id` now returns CustomerSummary (includes email, role) instead of bare Profile
- Added `serializeCustomer()` function mapping `userId` → `id` for frontend compatibility

### Frontend (admin app)

**api-client.ts**: Updated `customersApi.list()` return type to `{ items, total }`

**customers/page.tsx**: Wired `setTotal(data.total)` from API response

## Verification

| Check | Result |
|-------|--------|
| Backend typecheck | ✅ clean |
| Backend build | ✅ clean |
| Backend tests | ✅ 123 passed |
| Admin typecheck | ✅ clean |
| Domain purity | ✅ no DB/HTTP imports in domain |
| Parameterized queries | ✅ all SQL uses `$1`, `$2`, etc. |

## Acceptance traceability

| Criterion | Evidence |
|-----------|----------|
| Customer list with search | `GET /users?q=foo` → ILIKE on email |
| Pagination | `offset`/`limit` params, default 20 per page |
| Total count | `COUNT(*)` query, returned as `total` |
| Email and role in response | JOIN identity_users + users_profiles |
| Admin-only | Existing `requireRole(user, 'admin')` gate |
