Story CP-016 : Interface Journal d'Audit + Contrôle d'Accès¶
Module : control-plane Slice : Slice 4 from architecture (split from CP-004) Brand context : [BOTH] Papillon HR Suite + ALTARYS ENTERPRISE HR Suite Priority : 4 Depends on : CP-004 Estimated complexity : M
Objectif¶
Construire l'interface de visualisation du journal d'audit et l'endpoint de requête avec filtres. Le DG peut parcourir, filtrer et inspecter les entrées d'audit. Les utilisateurs non-DG se voient refuser l'accès. Cette story s'appuie sur l'infrastructure AuditService et AuditEntry de CP-004.
Périmètre Backend¶
Entités & Records¶
Aucune nouvelle entité. Utilise l'entité AuditEntry de CP-004.
DTOs (depuis CP-004, utilisés par l'endpoint de requête) :
public record AuditEntryResponse(
UUID id, UUID userId, String userFullName, Instant timestamp,
String entityType, UUID entityId, String moduleCode,
AuditAction action, JsonNode beforeValue, JsonNode afterValue,
JsonNode metadata
) {}
public record AuditPageResponse(
List<AuditEntryResponse> entries, int totalCount, int page, int pageSize
) {}
public record AuditQueryParams(
@Nullable String entityType,
@Nullable UUID entityId,
@Nullable UUID userId,
@Nullable AuditAction action,
@Nullable String moduleCode,
@Nullable Instant dateFrom,
@Nullable Instant dateTo,
int page, // default 0
int pageSize // default 50
) {}
Couche Repository¶
Utilise AuditRepository de CP-004 :
- findByFilters(UUID tenantId, AuditQueryParams) → Page<AuditEntry> — Requête paginée avec filtres optionnels sur entity_type, entity_id, user_id, action, module_code, plage de dates
Couche Service¶
AuditService.java (ajout au AuditService de CP-004) :
- query(AuditQueryParams params) → AuditPageResponse — Requête tenant-scopée pour l'UI. Scoped automatiquement par TenantContext.current().tenantId().
Endpoints API¶
| Méthode | Chemin | Corps de requête | Réponse | Auth |
|---|---|---|---|---|
| GET | /api/v1/audit | Paramètres de requête (filtres) | AuditPageResponse | ROLE_DG |
Règles de validation¶
entity_typemax 100 caractèresmodule_codemax 20 caractères- Plage de dates de requête :
date_fromdoit être antérieur àdate_to - Taille de page : 1–100 (défaut 50)
- Seul ROLE_DG peut accéder à cet endpoint (403 pour tous les autres rôles)
Périmètre Frontend¶
Pages & Routes¶
/admin/audit— Page de visualisation du journal d'audit
Composants¶
AuditTrailPage.tsx— Journal d'audit filtrable et paginéAuditEntryCard.tsx— Entrée d'audit unique (vue carte mobile)AuditEntryRow.tsx— Ligne de tableau (vue bureau)AuditDetailSheet.tsx— Bottom sheet (mobile) / panneau latéral (bureau) affichant le diff avant/après completAuditFilterBar.tsx— Chips de filtres : Module, Action, Plage de dates
États UI (TOUS REQUIS)¶
| État | Comportement | Texte en Français |
|---|---|---|
| Loading | Squelette : 5 placeholders de cartes d'entrée | — |
| Empty | Illustration + message | "Aucune activité enregistrée pour cette période." |
| Error | Toast rouge | "Une erreur est survenue. Veuillez réessayer." |
| Offline | Message hors ligne pleine page | "Le journal d'audit n'est pas disponible hors ligne." |
| Success | Entrées affichées avec pagination | — |
| Unauthorized | Redirection ou message | "Accès non autorisé" |
Comportement responsive¶
- 360px (mobile) : Liste chronologique. Chaque carte d'entrée affiche : date/heure, nom d'utilisateur, libellé d'action lisible, nom d'entité entre guillemets, badge module, lien "Détails -->".
- 768px (tablette) : Vue tableau compacte.
- 1024px+ (bureau) : Tableau pleine largeur avec colonnes : Date/Heure, Utilisateur, Action, Entité, Module, Résumé. Lignes extensibles affichant le diff avant/après en ligne. Filtres avancés : sélecteur de plage de dates, dropdown module, cases à cocher type d'action, recherche d'utilisateur.
Interactions¶
- Filtres : Dropdown Module, Type d'action (Création/Modification/Suppression), Sélecteur de plage de dates. Les filtres s'empilent sous forme de chips.
- Pagination : Bouton "Charger plus..." en bas (mobile). Numéros de pages (bureau).
- Vue détail : Appuyer sur "Détails -->" sur n'importe quelle entrée → bottom sheet (mobile) / panneau latéral (bureau) affichant :
- Action, Entité, Par (utilisateur), Date
- Section "Modifications" montrant le avant/après champ par champ
- Affichage Avant/Après : Format lisible :
Micro-textes français pour l'UI d'audit :
| Clé | Texte Français |
|---|---|
audit.title |
"Journal d'audit" |
audit.filter.module |
"Module" |
audit.filter.action |
"Action" |
audit.filter.date |
"Période" |
audit.action.create |
"Création" |
audit.action.update |
"Modification" |
audit.action.delete |
"Suppression" |
audit.detail.title |
"Détail de l'audit" |
audit.detail.action |
"Action" |
audit.detail.entity |
"Entité" |
audit.detail.by |
"Par" |
audit.detail.date |
"Date" |
audit.detail.changes |
"Modifications" |
audit.detail.before |
"Avant" |
audit.detail.after |
"Après" |
audit.load_more |
"Charger plus..." |
empty.audit |
"Aucune activité enregistrée pour cette période." |
Critères d'acceptation¶
AC-029: Audit query with filters
Given 50 audit entries exist for my tenant
When I call GET /api/v1/audit?entityType=Employee&action=CREATE&dateFrom=2026-02-01
Then I receive only audit entries matching all three filters
AC-031: Audit UI displays timeline
Given I am DG and navigate to /admin/audit
When the page loads
Then I see a chronological list of audit entries with filters at the top
AC-032: Audit detail view shows before/after
Given an UPDATE audit entry for a department rename
When I tap "Détails -->" on the entry
Then I see the before/after diff in human-readable format: "Nom -- Avant: Production -- Apres: Prod."
AC-033: Audit accessible only to DG
Given I am authenticated with role EMPLOYE
When I navigate to /admin/audit
Then I am redirected or shown "Acces non autorise"
Règles OHADA & Réglementaires¶
- AUDCIF Art. 24 : Le visualiseur d'audit fournit au DG la traçabilité de toutes les mutations de données, satisfaisant l'exigence de visibilité de conservation sur 10 ans.
- AUDCIF Art. 22 : Les entrées d'audit affichées sont immuables — l'UI est en lecture seule. Aucune action d'édition ou de suppression n'existe.
- Loi 2013-450 : Le visualiseur du journal d'audit restreint au rôle DG applique la responsabilité pour l'accès aux données personnelles.
Standards & Conventions¶
docs/standards/react-typescript-guidelines.md— §Composants liste/tableau, §Pattern filtres, §Détail en bottom sheet, §Paginationdocs/standards/api-guidelines.md— §Pagination, §Conventions de paramètres de requête
Exigences de test¶
Tests unitaires¶
- AuditService.query() : combinaisons de filtres (entity_type, action, plage de dates, module)
- AuditService.query() : pagination (page, pageSize)
- Génération du diff avant/après pour l'affichage lisible
Tests d'intégration¶
- GET /api/v1/audit — avec plusieurs combinaisons de filtres
- GET /api/v1/audit — pagination (50 par page, page suivante)
- GET /api/v1/audit — isolation tenant (requêtes du tenant A, pas de données du tenant B)
- GET /api/v1/audit — rôle EMPLOYE → 403
- GET /api/v1/audit — rôle DG → 200
Ce que le QA validera¶
- La page d'audit se charge avec les entrées en ordre chronologique inverse
- Les filtres fonctionnent : Module, Type d'action, Plage de dates
- La vue détail affiche correctement le diff avant/après
- La pagination ("Charger plus...") charge la page suivante
- La page affiche le message hors ligne quand déconnecté
- Le DG peut accéder à la page d'audit ; EMPLOYE ne peut pas
- Tous les points de rupture s'affichent correctement (360px, 768px, 1024px)
Hors périmètre¶
- Export CSV du journal d'audit — V2
- Requête d'audit inter-tenant (pour l'admin plateforme) — CP-012
- Journal d'audit pour les actions de la console admin — CP-012
Définition de Done¶
- [ ] L'endpoint GET /api/v1/audit avec filtres retourne les données paginées correctes
- [ ] Toutes les pages frontend s'affichent correctement à 360px, 768px, 1024px
- [ ] Les 6 états UI implémentés (loading, empty, error, offline, success, unauthorized)
- [ ] Les micro-textes français correspondent exactement à la spec design
- [ ] Tous les critères d'acceptation passent manuellement
- [ ] Tests unitaires écrits et passants
- [ ] Tests d'intégration écrits et passants
- [ ] Accès DG uniquement appliqué (403 pour les autres rôles)
- [ ] Pas de
anyTypeScript — tous les types explicites - [ ] Pas de types Java bruts — tous les génériques explicites