2.7 KiB
2.7 KiB
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:
// 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:
listCustomers(opts: { q?: string; offset: number; limit: number }): Promise<CustomerListResult>;
Infrastructure — PgProfileRepository.listCustomers():
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:
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
userIdasidto match frontendCustomertype
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_usersalready hasemail,role(migration 003) - Domain layer stays DB-free (ports pattern)
- Admin-only endpoint (already gated by
requireRole('admin'))
Files to change
project/src/modules/users/domain/profile.ts— addCustomerSummary,CustomerListResultproject/src/modules/users/domain/ports.ts— addlistCustomers()toProfileRepositoryproject/src/modules/users/application/profile-use-cases.ts— addListCustomersproject/src/modules/users/infrastructure/pg-profile-repository.ts— implementlistCustomers()project/src/modules/users/api/users.routes.ts— enhanceGET /usersproject/apps/admin/src/lib/api-client.ts— update return typeproject/apps/admin/src/app/(dashboard)/customers/page.tsx— wiretotal