57 lines
2.1 KiB
Markdown
57 lines
2.1 KiB
Markdown
# 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 |
|