Feature #1145
openFeature #1133: Implémenter une page de profil utilisateur avec gestion des crédits et des tiers d'abonnement
Fullstack — Page admin (liste filtrable, édition tier, ajustement crédits, historique)
0%
issue:1133 — spec in epic + ticket description
Description
## Objectif
Interface d'administration OmDev : lister/filtrer les clients, modifier leur tier, ajuster leurs crédits, et consulter l'historique des modifications. Couvre les endpoints admin backend et la page admin frontend.
## Dépendances
Dépend du backend modèle/permissions/historique (#1141) et des notifications email (#1142, pour l'email de changement de tier).
## Portée — Backend (`api/`)
- Endpoints admin (sous garde permission « admin OmDev », 403 sinon) : liste paginée + filtrable des clients (nom, email, tier, crédits restants), modification du tier, ajustement manuel des crédits (+/-), lecture de l'historique.
- Toute mutation écrit une entrée d'historique et émet l'événement correspondant (tier changé → email via #1142).
## Portée — Frontend (`src/`)
- Page admin App Router (visible uniquement pour les admins OmDev) : tableau filtrable des clients, menu déroulant d'édition du tier, contrôle d'ajustement des crédits (optionnel), vue de l'historique des modifications.
- TanStack Query (queries + mutations); i18n `en.ts` ET `fr.ts`.
## Acceptance criteria
- [ ] La page admin liste et filtre les clients (nom, email, tier, crédits).
- [ ] Le tier d'un client est modifiable via un menu déroulant; le changement est persisté.
- [ ] Les crédits d'un client peuvent être ajustés (+/-) depuis l'admin.
- [ ] Chaque modification tier/crédits apparaît dans un historique consultable.
- [ ] Un changement de tier déclenche l'email au client (via #1142).
- [ ] Un utilisateur non-admin OmDev ne peut pas accéder à la page ni aux endpoints (403 / non rendu).
- [ ] Clés i18n cohérentes `en.ts`/`fr.ts`.
- [ ] Backend : pytest verts, `ruff check .`, `mypy src` propres. Frontend : `vitest`, `tsc`, `eslint`, `build` verts.
Files
RA Updated by Redmine Admin about 2 months ago
- Status changed from Submitted to Spec
- spec_ref updated (diff)
RA Updated by Redmine Admin about 2 months ago
- File design-1145-after.png design-1145-after.png added
RA Updated by Redmine Admin about 2 months ago
- File design-1145-before.png design-1145-before.png added
RA Updated by Redmine Admin about 2 months ago
- File design-1145-mockup.html design-1145-mockup.html added
RA Updated by Redmine Admin about 2 months ago
## Brief de design — Page admin Gestion des clients (Frontend)
**Stade :** Design (maquette HTML + preuve avant/après ci-jointes). Surface **frontend/admin uniquement** ; les endpoints backend du même ticket ne nécessitent pas de maquette. Section de design system ajoutée : **« Admin — Gestion des clients (AG) »** (`docs/design/design-system.md`, additive, après §MC).
### Atomic Design — réutiliser avant d'inventer (aucune nouvelle couleur, aucun nouvel atome)
- **Réutilisé** : `WorkspaceLayout`/TopBar §7b.4 + gating admin §AD.1 (non-admin → `/projects`, 401 → `/login`, 403 endpoints) ; contrat de **tableau §AD.3** ; atomes §7b.1 Button, §7b.2 TextInput, §6.1 pill, select §AD.4 ; **§TD.7 ConfirmDialog** (unique modale).
- **Référencé depuis #1143 (§MC) — pas dupliqué** : couleurs de tier §MC.5 (Démarrage slate `#475569` · Maintenance cyan `#0e7490` · Évolutions violet `#6d28d9` · Pilotage emerald `#047857`) ; seuils/verdicts crédits §MC.3 (`>50 %` Sain `success-fg`, `<30 %` Attention `warning-fg`, `<10 %` Critique `danger-fg`) ; règles signe/couleur historique §MC.6.
- **Nouveaux composants** : `ClientFilters` (molecule, AG.2), `ClientTable` (organism, AG.3), `TierSelect` (atome, AG.3a), `CreditStepper` (molecule, AG.3b), `ChangeHistoryPanel` + `ChangeHistoryRow` (organism, AG.5).
### Écrans / composants
- **Page « Gestion des clients »** (`/admin/clients`), grille `lg:grid-cols-[1.45fr_0.95fr]` : gauche = liste filtrable, droite = historique.
- **Barre de filtres** : recherche nom/e-mail (debounce, params backend) + filtre tier + compteur `{n} clients` (ICU plural).
- **Tableau filtrable** : colonnes **Client** (nom + e-mail mono) · **Tier** (dropdown éditable) · **Crédits restants** (montant coloré par seuil §MC.3 + pastille verdict) · **Ajuster** (stepper +/−).
- **Édition tier (AG.3a)** : select coloré au token de niveau, mutation persistée (`PATCH …/tier`) optimiste ; **§TD.7 ConfirmDialog** avant persistance car un changement de tier déclenche l'e-mail client (#1142).
- **Ajustement crédits (AG.3b)** : stepper `−|pas|+`, `PATCH …/credits {delta}`, optimiste, garde solde ≥ 0.
- **Historique consultable (AG.5)** : lecture seule — type (Tier/Crédits), client, ancien → nouveau, qui (`par {acteur}`, `· e-mail client envoyé` pour un tier), quand (`<time>` ISO) ; re-fetch après chaque mutation.
### États (empty / loading / error / populated)
- Tableau : skeleton §AD.3 · `Aucun client` / `Aucun client ne correspond à votre recherche.` · `role="alert"` + `Réessayer`.
- Tier : succès `Tier mis à jour` (`role="status"`) · erreur rollback `role="alert"`.
- Stepper : succès `Crédits ajustés` · solde→0 bouton `−` désactivé + hint `Le solde ne peut pas être négatif.` · erreur rollback.
- Historique : skeleton · `Aucune modification enregistrée.` · `role="alert"` + `Réessayer`.
### i18n
Namespace **`admin.clients`**, clés ajoutées dans **`en.ts` ET `fr.ts`** (vraie traduction FR, mêmes clés/placeholders ICU `{n} plural`, `{amount}`, `{verdict}`, `{client}`, `{actor}` — tsc via `Translated<typeof en>`). Échantillon complet dans §AG.8.
### Accessibilité (§7)
Jamais la couleur seule : tier en **mot** (nom de formule), verdict crédits en **mot** + montant, type historique en **mot** ; `<th scope="row">` sur le client ; cibles `+/−` ≥44px ; ConfirmDialog focus-trap/Esc=Annuler ; gardes via `disabled`+`title` ; `motion-safe:` partout.
### Preuve avant/après (jointe)
- **`design-1145-after.png`** = la maquette proposée (liste + édition tier + steppers crédits + panneau historique).
- **`design-1145-before.png`** = **reconstruction** de l'écran admin actuel (`/admin` = onboarding projet + vue d'ensemble projets, **aucune gestion clients/tier/crédits**). Le `/admin` est derrière l'auth (gating §AD.1) donc non pilotable headless — le « before » reconstruit fidèlement l'état actuel, ce n'est pas une capture live.
- **`design-1145-mockup.html`** = source HTML auto-portée de la maquette.
Transition **Spec → Design**. Le développement FE attend les endpoints admin de ce ticket.
RA Updated by Redmine Admin about 2 months ago
- Status changed from Spec to Design
RA Updated by Redmine Admin about 2 months ago
- Status changed from Design to In development
RA Updated by Redmine Admin about 2 months ago
- Status changed from In development to QA
- branch set to feat/1145-admin-page
- pr_url set to https://github.com/omdev-tech/PipeLiner-Client/pull/70
PR ouverte vers `dev` : https://github.com/omdev-tech/PipeLiner-Client/pull/70
**Backend (`api/`)** — nouvel endpoint admin-only `GET /billing/clients` : liste paginée/filtrable des clients avec résumé billing (recherche nom/e-mail + filtre tier). Chaque ligne = `{ client_id, name, email, tier_code, tier_name, remaining_credits, monthly_credits, usage_ratio, verdict }`. Architecture hexagonale (port `ClientBillingDirectory` + use-case `ListClientsBilling`). Les mutations tier/crédits et l'historique de #1141 sont **réutilisés** (aucune réimplémentation) ; un changement de tier déclenche bien l'e-mail #1142. Aucun changement de schéma.
**Frontend (`src/`, App Router)** — page `/admin/clients` (réservée aux admins OmDev, gating §AD.1 + `require_admin` côté serveur) : tableau filtrable (nom, e-mail, tier, crédits restants + verdict), dropdown d'édition du tier avec **ConfirmDialog** notant que le client est notifié par e-mail, stepper d'ajustement des crédits +/− (garde solde ≥ 0), panneau d'historique consultable par client. TanStack Query (queries + mutations, invalidation après mutation). Clés i18n dans **`en.ts` ET `fr.ts`** (namespace `adminClients`). Section design system **§AG** ajoutée.
**Gates verts** — API : `pytest` (576 passed, vert 2× sur pgvector éphémère + `alembic upgrade head`), `ruff`, `mypy`. FE : `vitest` (571 passed), `tsc`, `eslint`, `build`.
Statut : In development → QA.