feat(unionflow): ajout Spec-Kit, constitution, mission mutuelles
- Config Spec-Kit pour Spec-Driven Development - CONSTITUTION.md + .specify/memory/constitution.md - Commandes Cursor /speckit.*, règles projet - Mission: associations + mutuelles d'épargne et de financement - .gitignore: versionner config spec-kit unionflow Made-with: Cursor
This commit is contained in:
@@ -0,0 +1,192 @@
|
||||
/// Gestionnaire d'erreurs global pour l'application
|
||||
library error_handler;
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
|
||||
/// Classe utilitaire pour gérer les erreurs de manière centralisée
|
||||
class ErrorHandler {
|
||||
/// Convertit une erreur en message utilisateur lisible
|
||||
static String getErrorMessage(dynamic error) {
|
||||
if (error is DioException) {
|
||||
return _handleDioError(error);
|
||||
} else if (error is String) {
|
||||
return error;
|
||||
} else if (error is Exception) {
|
||||
return error.toString().replaceAll('Exception: ', '');
|
||||
}
|
||||
return 'Une erreur inattendue s\'est produite.';
|
||||
}
|
||||
|
||||
/// Gère les erreurs Dio spécifiques
|
||||
static String _handleDioError(DioException error) {
|
||||
switch (error.type) {
|
||||
case DioExceptionType.connectionTimeout:
|
||||
return 'Délai de connexion dépassé.\nVérifiez votre connexion internet.';
|
||||
|
||||
case DioExceptionType.sendTimeout:
|
||||
return 'Délai d\'envoi dépassé.\nVérifiez votre connexion internet.';
|
||||
|
||||
case DioExceptionType.receiveTimeout:
|
||||
return 'Délai de réception dépassé.\nLe serveur met trop de temps à répondre.';
|
||||
|
||||
case DioExceptionType.badResponse:
|
||||
return _handleBadResponse(error.response);
|
||||
|
||||
case DioExceptionType.cancel:
|
||||
return 'Requête annulée.';
|
||||
|
||||
case DioExceptionType.connectionError:
|
||||
return 'Erreur de connexion.\nVérifiez votre connexion internet.';
|
||||
|
||||
case DioExceptionType.badCertificate:
|
||||
return 'Erreur de certificat SSL.\nLa connexion n\'est pas sécurisée.';
|
||||
|
||||
case DioExceptionType.unknown:
|
||||
default:
|
||||
if (error.message?.contains('SocketException') ?? false) {
|
||||
return 'Impossible de se connecter au serveur.\nVérifiez votre connexion internet.';
|
||||
}
|
||||
return 'Erreur de connexion.\nVeuillez réessayer.';
|
||||
}
|
||||
}
|
||||
|
||||
/// Gère les réponses HTTP avec erreur
|
||||
static String _handleBadResponse(Response? response) {
|
||||
if (response == null) {
|
||||
return 'Erreur serveur inconnue.';
|
||||
}
|
||||
|
||||
// Essayer d'extraire le message d'erreur du body
|
||||
String? errorMessage;
|
||||
if (response.data is Map) {
|
||||
errorMessage = response.data['message'] ??
|
||||
response.data['error'] ??
|
||||
response.data['details'];
|
||||
}
|
||||
|
||||
switch (response.statusCode) {
|
||||
case 400:
|
||||
return errorMessage ?? 'Requête invalide.\nVérifiez les données saisies.';
|
||||
|
||||
case 401:
|
||||
return errorMessage ?? 'Non authentifié.\nVeuillez vous reconnecter.';
|
||||
|
||||
case 403:
|
||||
return errorMessage ?? 'Accès refusé.\nVous n\'avez pas les permissions nécessaires.';
|
||||
|
||||
case 404:
|
||||
return errorMessage ?? 'Ressource non trouvée.';
|
||||
|
||||
case 409:
|
||||
return errorMessage ?? 'Conflit.\nCette ressource existe déjà.';
|
||||
|
||||
case 422:
|
||||
return errorMessage ?? 'Données invalides.\nVérifiez les informations saisies.';
|
||||
|
||||
case 429:
|
||||
return 'Trop de requêtes.\nVeuillez patienter quelques instants.';
|
||||
|
||||
case 500:
|
||||
return errorMessage ?? 'Erreur serveur interne.\nVeuillez réessayer plus tard.';
|
||||
|
||||
case 502:
|
||||
return 'Passerelle incorrecte.\nLe serveur est temporairement indisponible.';
|
||||
|
||||
case 503:
|
||||
return 'Service temporairement indisponible.\nVeuillez réessayer plus tard.';
|
||||
|
||||
case 504:
|
||||
return 'Délai d\'attente de la passerelle dépassé.\nLe serveur met trop de temps à répondre.';
|
||||
|
||||
default:
|
||||
return errorMessage ?? 'Erreur serveur (${response.statusCode}).\nVeuillez réessayer.';
|
||||
}
|
||||
}
|
||||
|
||||
/// Détermine si l'erreur est une erreur réseau
|
||||
static bool isNetworkError(dynamic error) {
|
||||
if (error is DioException) {
|
||||
return error.type == DioExceptionType.connectionTimeout ||
|
||||
error.type == DioExceptionType.sendTimeout ||
|
||||
error.type == DioExceptionType.receiveTimeout ||
|
||||
error.type == DioExceptionType.connectionError ||
|
||||
(error.message?.contains('SocketException') ?? false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Détermine si l'erreur est une erreur d'authentification
|
||||
static bool isAuthError(dynamic error) {
|
||||
if (error is DioException && error.response != null) {
|
||||
return error.response!.statusCode == 401;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Détermine si l'erreur est une erreur de permissions
|
||||
static bool isPermissionError(dynamic error) {
|
||||
if (error is DioException && error.response != null) {
|
||||
return error.response!.statusCode == 403;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Détermine si l'erreur est une erreur de validation
|
||||
static bool isValidationError(dynamic error) {
|
||||
if (error is DioException && error.response != null) {
|
||||
return error.response!.statusCode == 400 ||
|
||||
error.response!.statusCode == 422;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Détermine si l'erreur est une erreur serveur
|
||||
static bool isServerError(dynamic error) {
|
||||
if (error is DioException && error.response != null) {
|
||||
final statusCode = error.response!.statusCode ?? 0;
|
||||
return statusCode >= 500 && statusCode < 600;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Extrait les détails de validation d'une erreur
|
||||
static Map<String, dynamic>? getValidationErrors(dynamic error) {
|
||||
if (error is DioException &&
|
||||
error.response != null &&
|
||||
error.response!.data is Map) {
|
||||
final data = error.response!.data as Map;
|
||||
if (data.containsKey('errors')) {
|
||||
return data['errors'] as Map<String, dynamic>?;
|
||||
}
|
||||
if (data.containsKey('validationErrors')) {
|
||||
return data['validationErrors'] as Map<String, dynamic>?;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension pour faciliter l'utilisation de ErrorHandler
|
||||
extension ErrorHandlerExtension on Object {
|
||||
/// Convertit l'objet en message d'erreur lisible
|
||||
String toErrorMessage() => ErrorHandler.getErrorMessage(this);
|
||||
|
||||
/// Vérifie si c'est une erreur réseau
|
||||
bool get isNetworkError => ErrorHandler.isNetworkError(this);
|
||||
|
||||
/// Vérifie si c'est une erreur d'authentification
|
||||
bool get isAuthError => ErrorHandler.isAuthError(this);
|
||||
|
||||
/// Vérifie si c'est une erreur de permissions
|
||||
bool get isPermissionError => ErrorHandler.isPermissionError(this);
|
||||
|
||||
/// Vérifie si c'est une erreur de validation
|
||||
bool get isValidationError => ErrorHandler.isValidationError(this);
|
||||
|
||||
/// Vérifie si c'est une erreur serveur
|
||||
bool get isServerError => ErrorHandler.isServerError(this);
|
||||
|
||||
/// Récupère les erreurs de validation
|
||||
Map<String, dynamic>? get validationErrors => ErrorHandler.getValidationErrors(this);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/// Exception de base pour l'application
|
||||
abstract class AppException implements Exception {
|
||||
final String message;
|
||||
final String? code;
|
||||
|
||||
const AppException(this.message, [this.code]);
|
||||
|
||||
@override
|
||||
String toString() => 'AppException: $message${code != null ? ' (Code: $code)' : ''}';
|
||||
}
|
||||
|
||||
/// Exception serveur
|
||||
class ServerException extends AppException {
|
||||
const ServerException(super.message, [super.code]);
|
||||
|
||||
@override
|
||||
String toString() => 'ServerException: $message${code != null ? ' (Code: $code)' : ''}';
|
||||
}
|
||||
|
||||
/// Exception de cache
|
||||
class CacheException extends AppException {
|
||||
const CacheException(super.message, [super.code]);
|
||||
|
||||
@override
|
||||
String toString() => 'CacheException: $message${code != null ? ' (Code: $code)' : ''}';
|
||||
}
|
||||
|
||||
/// Exception de réseau
|
||||
class NetworkException extends AppException {
|
||||
const NetworkException(super.message, [super.code]);
|
||||
|
||||
@override
|
||||
String toString() => 'NetworkException: $message${code != null ? ' (Code: $code)' : ''}';
|
||||
}
|
||||
|
||||
/// Exception d'authentification
|
||||
class AuthException extends AppException {
|
||||
const AuthException(super.message, [super.code]);
|
||||
|
||||
@override
|
||||
String toString() => 'AuthException: $message${code != null ? ' (Code: $code)' : ''}';
|
||||
}
|
||||
|
||||
/// Exception de validation
|
||||
class ValidationException extends AppException {
|
||||
const ValidationException(super.message, [super.code]);
|
||||
|
||||
@override
|
||||
String toString() => 'ValidationException: $message${code != null ? ' (Code: $code)' : ''}';
|
||||
}
|
||||
71
unionflow/unionflow-mobile-apps/lib/core/error/failures.dart
Normal file
71
unionflow/unionflow-mobile-apps/lib/core/error/failures.dart
Normal file
@@ -0,0 +1,71 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// Classe de base pour tous les échecs
|
||||
abstract class Failure extends Equatable {
|
||||
final String message;
|
||||
final String? code;
|
||||
|
||||
const Failure(this.message, [this.code]);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [message, code];
|
||||
|
||||
@override
|
||||
String toString() => 'Failure: $message${code != null ? ' (Code: $code)' : ''}';
|
||||
}
|
||||
|
||||
/// Échec serveur
|
||||
class ServerFailure extends Failure {
|
||||
const ServerFailure(super.message, [super.code]);
|
||||
|
||||
@override
|
||||
String toString() => 'ServerFailure: $message${code != null ? ' (Code: $code)' : ''}';
|
||||
}
|
||||
|
||||
/// Échec de cache
|
||||
class CacheFailure extends Failure {
|
||||
const CacheFailure(super.message, [super.code]);
|
||||
|
||||
@override
|
||||
String toString() => 'CacheFailure: $message${code != null ? ' (Code: $code)' : ''}';
|
||||
}
|
||||
|
||||
/// Échec de réseau
|
||||
class NetworkFailure extends Failure {
|
||||
const NetworkFailure(super.message, [super.code]);
|
||||
|
||||
@override
|
||||
String toString() => 'NetworkFailure: $message${code != null ? ' (Code: $code)' : ''}';
|
||||
}
|
||||
|
||||
/// Échec d'authentification
|
||||
class AuthFailure extends Failure {
|
||||
const AuthFailure(super.message, [super.code]);
|
||||
|
||||
@override
|
||||
String toString() => 'AuthFailure: $message${code != null ? ' (Code: $code)' : ''}';
|
||||
}
|
||||
|
||||
/// Échec de validation
|
||||
class ValidationFailure extends Failure {
|
||||
const ValidationFailure(super.message, [super.code]);
|
||||
|
||||
@override
|
||||
String toString() => 'ValidationFailure: $message${code != null ? ' (Code: $code)' : ''}';
|
||||
}
|
||||
|
||||
/// Échec de permission
|
||||
class PermissionFailure extends Failure {
|
||||
const PermissionFailure(super.message, [super.code]);
|
||||
|
||||
@override
|
||||
String toString() => 'PermissionFailure: $message${code != null ? ' (Code: $code)' : ''}';
|
||||
}
|
||||
|
||||
/// Échec de données non trouvées
|
||||
class NotFoundFailure extends Failure {
|
||||
const NotFoundFailure(super.message, [super.code]);
|
||||
|
||||
@override
|
||||
String toString() => 'NotFoundFailure: $message${code != null ? ' (Code: $code)' : ''}';
|
||||
}
|
||||
Reference in New Issue
Block a user