# 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 |