Acheteur Analytics and Account Management
Summary
The admin acheteur module provides KPI dashboards, a searchable paginated acheteur list, and individual account management actions (disable, enable, soft-delete with anonymization, and resend verification email). Phone numbers are truncated in list views for privacy. Soft-delete anonymizes PII via lib/acheteur-lifecycle.ts. All moderation actions are recorded in AcheteurAccountAction and disabledAt/deletedAt timestamps for audit purposes.
State Diagram
stateDiagram-v2 state "Compte actif" as Active state "Compte désactivé" as Disabled state "Supprimé (anonymisé)" as SoftDeleted [*] --> Active: acheteur registered Active --> Disabled: admin disables account Disabled --> Active: admin enables account Active --> SoftDeleted: admin soft-deletes account Disabled --> SoftDeleted: admin soft-deletes account SoftDeleted --> [*]: irreversible
Steps
1. KPI Stats (Actor: admin)
GET /admin/acheteurs/stats?period=7d|30d|90d
Parallel queries return:
total: active acheteurs (non-deleted)unverified: email not yet verifiednewInPeriod: registered in the time windowavgPerDay: new registrations per day over the perioddeletionsInPeriod: soft-deletes in the periodwithFavorites: distinct acheteurs with at least one favorite (raw SQL)withMortgage: distinct acheteurs with at least one mortgage application (raw SQL)authGoogle: OAuth registrations;authEmail = total - authGooglefavoritesPct,mortgagePct: engagement percentages
Outcome: KPI object for dashboard
2. List Acheteurs (Actor: admin)
GET /admin/acheteurs with query parameters:
page,limit: offset paginationsearch: case-insensitive match on firstName, lastName, email; exact match on phoneemailVerified: boolean filterauthMethod:"email"or"google"hasFavorites,hasMortgage: boolean filters using Prisma relation existencesortBy,sortOrder: any valid columnincludeDeleted: include soft-deleted accounts (default false)
Phone numbers are truncated in the response: "0612345678" → "06••••••78".
Each row includes favoritesCount, mortgageStatus (most recent application status), and account state flags (disabledAt, deletedAt, lastLoginAt).
3. Acheteur Detail (Actor: admin)
GET /admin/acheteurs/:id — full profile including:
- All favorites (programme or lot, with name and city)
- All mortgage applications (id, status, step, timestamps)
- Full account action history (action, reason, performer, timestamp)
googleId is stripped from the response; authMethod is derived from it. Phone is returned untruncated in detail view.
4. Disable Account (Actor: admin)
POST /admin/acheteurs/:id/disable with { reason: string }:
- Guards: account must exist, not already disabled, not deleted.
- Prisma
$transaction:acheteur.disabledAt = now()acheteurRefreshToken.deleteMany— revokes all active sessionsacheteurAccountAction.createwithaction: "disabled"
Outcome: Account disabled, all sessions revoked
5. Enable Account (Actor: admin)
POST /admin/acheteurs/:id/enable with { reason: string }:
- Guard: account must be disabled.
- Prisma
$transaction:acheteur.disabledAt = nullacheteurAccountAction.createwithaction: "enabled"
Outcome: Account re-enabled
6. Soft Delete Account (Actor: admin)
POST /admin/acheteurs/:id/delete with { reason: string }:
- Guard: account must not already be deleted.
- Calls
anonymizeAndSoftDelete(id, adminId, reason, log)fromlib/acheteur-lifecycle.ts.
The lifecycle function anonymizes all PII fields (replaces name, email, phone with redacted values), sets deletedAt, records the deletedBy admin ID, creates a "deleted" account action, and revokes all sessions. This is irreversible.
Outcome: Account anonymized and soft-deleted (GDPR-compliant)
7. Resend Verification Email (Actor: admin)
POST /admin/acheteurs/:id/resend-verification:
- Guards: account must exist, email must not already be verified.
- Preserves existing
emailVerifyDeadlineif set (or extends by 48h if null). - Calls
buildVerifyUrl(acheteurId, deadline)andsendEmail({ template: "acheteur-verify", ... }).
Outcome: Verification email resent, or 500 EMAIL_FAILED if sending fails
Error States
- Acheteur not found → 404
NOT_FOUND - Disable already-disabled account → 400
ALREADY_DISABLED - Disable already-deleted account → 400
ALREADY_DELETED - Enable non-disabled account → 400
NOT_DISABLED - Delete already-deleted account → 400
ALREADY_DELETED - Resend verification to already-verified account → 400
ALREADY_VERIFIED - Email send failure → 500
EMAIL_FAILED
Related Processes
- impersonation — admin can impersonate a demo acheteur for QA