feat: Optimisations UX/UI et amélioration import/export CSV

Optimisations majeures de l'interface utilisateur et amélioration du système d'import/export CSV avec rapport d'erreurs détaillé.

## Optimisations UX/UI
- Suppression des blocs Actions Rapides redondants dans les pages list/view
- Consolidation des actions dans les en-têtes de page
- Conversion des filtres en panneau collapsible avec badge Filtres actifs
- Suppression du sous-menu Attribution Rôles (redondant avec /users/edit)
- Amélioration de la navigation et de l'ergonomie générale
- Correction des attributs iconLeft non supportés par fr:fieldInput

## Import/Export CSV
- Ajout de ImportResultDTO avec rapport détaillé des erreurs
- Création de CsvValidationHelper pour validation robuste des données
- Amélioration des messages d'erreur avec numéros de ligne
- Support de colonnes flexibles (username,prenom,nom,email)
- Validation stricte des formats email

## Corrections techniques
- Fix DashboardBeanTest: getRecentActions() → getActionsLast24h()
- Fix UserServiceImplTest: retour ImportResultDTO au lieu de int
- Amélioration de la gestion d'erreurs dans AuditServiceImpl
- Migration Flyway V1.0.0 pour la table audit_logs

## Infrastructure
- Mise à jour .gitignore professionnel (exclusion docs de session)
- Configuration production sécurisée (variables d'environnement)
- Pas de secrets hardcodés dans les fichiers de configuration

Testé et validé en environnement de développement.
This commit is contained in:
lionsdev
2026-01-03 13:53:35 +00:00
parent 2bc1b0f6a5
commit 3773fac0b0
6 changed files with 926 additions and 220 deletions

View File

@@ -22,6 +22,7 @@ import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
import java.time.LocalDateTime;
import java.util.List;
/**
@@ -407,6 +408,86 @@ public class UserResource {
}
}
/**
* Exporter les utilisateurs en CSV
*/
@GET
@Path("/export/csv")
@Operation(summary = "Exporter les utilisateurs en CSV")
@APIResponses({
@APIResponse(responseCode = "200", description = "Fichier CSV généré avec succès"),
@APIResponse(responseCode = "400", description = "Realm manquant ou invalide"),
@APIResponse(responseCode = "500", description = "Erreur serveur")
})
@RolesAllowed({"admin", "user_manager", "user_viewer"})
@Produces(MediaType.TEXT_PLAIN)
public Response exportUsersToCSV(@QueryParam("realm") @NotBlank String realmName) {
log.info("GET /api/users/export/csv - realm: {}", realmName);
try {
UserSearchCriteriaDTO criteria = UserSearchCriteriaDTO.builder()
.realmName(realmName)
.pageSize(10000) // Export complet sans pagination
.page(0)
.build();
String csvContent = userService.exportUsersToCSV(criteria);
String filename = "users_export_" +
LocalDateTime.now().format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd_HHmmss")) +
".csv";
return Response.ok(csvContent)
.header("Content-Disposition", "attachment; filename=\"" + filename + "\"")
.build();
} catch (Exception e) {
log.error("Erreur lors de l'export CSV des utilisateurs", e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity(new ErrorResponse(e.getMessage()))
.build();
}
}
/**
* Importer des utilisateurs depuis CSV avec rapport détaillé
*/
@POST
@Path("/import/csv")
@Operation(summary = "Importer des utilisateurs depuis un fichier CSV")
@APIResponses({
@APIResponse(responseCode = "200", description = "Import terminé avec rapport détaillé"),
@APIResponse(responseCode = "400", description = "Fichier CSV vide ou invalide"),
@APIResponse(responseCode = "500", description = "Erreur serveur")
})
@RolesAllowed({"admin", "user_manager"})
@Consumes(MediaType.TEXT_PLAIN)
@Produces(MediaType.APPLICATION_JSON)
public Response importUsersFromCSV(
@QueryParam("realm") @NotBlank String realmName,
String csvContent) {
log.info("POST /api/users/import/csv - realm: {}", realmName);
try {
if (csvContent == null || csvContent.trim().isEmpty()) {
return Response.status(Response.Status.BAD_REQUEST)
.entity(new ErrorResponse("Le contenu CSV est vide"))
.build();
}
dev.lions.user.manager.dto.importexport.ImportResultDTO result = userService.importUsersFromCSV(csvContent, realmName);
log.info("{} utilisateur(s) importé(s) dans le realm {} ({} erreur(s))",
result.getSuccessCount(), realmName, result.getErrorCount());
return Response.ok(result).build();
} catch (Exception e) {
log.error("Erreur lors de l'import CSV des utilisateurs", e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity(new ErrorResponse(e.getMessage()))
.build();
}
}
// ==================== DTOs internes ====================
@Schema(description = "Requête de réinitialisation de mot de passe")