Files
mercadodevida/work/artifacts/ADM-018/architect.md
2026-08-17 22:23:10 +02:00

78 lines
2.7 KiB
Markdown

# ADM-018 Design — Customer List
## Problem
`GET /users` only returns `users_profiles` fields (`userId`, `displayName`, `phone`).
The admin frontend needs `email`, `role`, `createdAt` from `identity_users`, plus search by email, pagination, and total count.
## Design
### Backend changes (users module)
**Domain layer** — extend `Profile` with optional `email` and `role`:
```ts
// profile.ts — add:
export interface CustomerSummary {
userId: string;
email: string;
role: string;
displayName: string | null;
phone: string | null;
createdAt: Date;
}
export interface CustomerListResult {
items: CustomerSummary[];
total: number;
}
```
**Port** — add new method to `ProfileRepository`:
```ts
listCustomers(opts: { q?: string; offset: number; limit: number }): Promise<CustomerListResult>;
```
**Infrastructure**`PgProfileRepository.listCustomers()`:
```sql
SELECT iu.id, iu.email, iu.role, up.display_name, up.phone, iu.created_at
FROM identity_users iu
LEFT JOIN users_profiles up ON up.user_id = iu.id
WHERE ($1::text IS NULL OR iu.email ILIKE '%' || $1 || '%')
ORDER BY iu.created_at DESC
LIMIT $2 OFFSET $3
```
Plus a `COUNT(*)` query for total.
**Application** — new use case `ListCustomers`:
```ts
export class ListCustomers {
constructor(private readonly profiles: ProfileRepository) {}
async execute(opts): Promise<CustomerListResult> { ... }
}
```
**API** — enhance `GET /users`:
- Query params: `q` (optional string), `offset` (default 0), `limit` (default 20)
- Response: `{ items: CustomerSummary[], total: number }`
- Serialize `userId` as `id` to match frontend `Customer` type
### Frontend changes
**api-client.ts**`customersApi.list()` already passes `offset`, `limit`, `q`. Update return type to include `total`.
**customers/page.tsx** — set `total` from API response: `setTotal(data.total)`.
## Boundaries
- No new module — stays in users module
- No migration needed — `identity_users` already has `email`, `role` (migration 003)
- Domain layer stays DB-free (ports pattern)
- Admin-only endpoint (already gated by `requireRole('admin')`)
## Files to change
1. `project/src/modules/users/domain/profile.ts` — add `CustomerSummary`, `CustomerListResult`
2. `project/src/modules/users/domain/ports.ts` — add `listCustomers()` to `ProfileRepository`
3. `project/src/modules/users/application/profile-use-cases.ts` — add `ListCustomers`
4. `project/src/modules/users/infrastructure/pg-profile-repository.ts` — implement `listCustomers()`
5. `project/src/modules/users/api/users.routes.ts` — enhance `GET /users`
6. `project/apps/admin/src/lib/api-client.ts` — update return type
7. `project/apps/admin/src/app/(dashboard)/customers/page.tsx` — wire `total`