Refactoring

This commit is contained in:
DahoudG
2025-09-17 17:54:06 +00:00
parent 12d514d866
commit 63fe107f98
165 changed files with 54220 additions and 276 deletions

View File

@@ -0,0 +1,843 @@
import 'dart:async';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:dartz/dartz.dart';
import '../../../../../core/error/failures.dart';
import '../../../domain/entities/demande_aide.dart';
import '../../../domain/usecases/gerer_demandes_aide_usecase.dart';
import 'demandes_aide_event.dart';
import 'demandes_aide_state.dart';
/// BLoC pour la gestion des demandes d'aide
///
/// Ce BLoC gère tous les états et événements liés aux demandes d'aide,
/// incluant le chargement, la création, la modification, la validation,
/// le filtrage, le tri et l'export des demandes.
class DemandesAideBloc extends Bloc<DemandesAideEvent, DemandesAideState> {
final CreerDemandeAideUseCase creerDemandeAideUseCase;
final MettreAJourDemandeAideUseCase mettreAJourDemandeAideUseCase;
final ObtenirDemandeAideUseCase obtenirDemandeAideUseCase;
final SoumettreDemandeAideUseCase soumettreDemandeAideUseCase;
final EvaluerDemandeAideUseCase evaluerDemandeAideUseCase;
final RechercherDemandesAideUseCase rechercherDemandesAideUseCase;
final ObtenirDemandesUrgentesUseCase obtenirDemandesUrgentesUseCase;
final ObtenirMesDemandesUseCase obtenirMesDemandesUseCase;
final ValiderDemandeAideUseCase validerDemandeAideUseCase;
final CalculerPrioriteDemandeUseCase calculerPrioriteDemandeUseCase;
// Cache des paramètres de recherche pour la pagination
String? _lastOrganisationId;
TypeAide? _lastTypeAide;
StatutAide? _lastStatut;
String? _lastDemandeurId;
bool? _lastUrgente;
DemandesAideBloc({
required this.creerDemandeAideUseCase,
required this.mettreAJourDemandeAideUseCase,
required this.obtenirDemandeAideUseCase,
required this.soumettreDemandeAideUseCase,
required this.evaluerDemandeAideUseCase,
required this.rechercherDemandesAideUseCase,
required this.obtenirDemandesUrgentesUseCase,
required this.obtenirMesDemandesUseCase,
required this.validerDemandeAideUseCase,
required this.calculerPrioriteDemandeUseCase,
}) : super(const DemandesAideInitial()) {
// Enregistrement des handlers d'événements
on<ChargerDemandesAideEvent>(_onChargerDemandesAide);
on<ChargerPlusDemandesAideEvent>(_onChargerPlusDemandesAide);
on<CreerDemandeAideEvent>(_onCreerDemandeAide);
on<MettreAJourDemandeAideEvent>(_onMettreAJourDemandeAide);
on<ObtenirDemandeAideEvent>(_onObtenirDemandeAide);
on<SoumettreDemandeAideEvent>(_onSoumettreDemandeAide);
on<EvaluerDemandeAideEvent>(_onEvaluerDemandeAide);
on<ChargerDemandesUrgentesEvent>(_onChargerDemandesUrgentes);
on<ChargerMesDemandesEvent>(_onChargerMesdemandes);
on<RechercherDemandesAideEvent>(_onRechercherDemandesAide);
on<ValiderDemandeAideEvent>(_onValiderDemandeAide);
on<CalculerPrioriteDemandeEvent>(_onCalculerPrioriteDemande);
on<FiltrerDemandesAideEvent>(_onFiltrerDemandesAide);
on<TrierDemandesAideEvent>(_onTrierDemandesAide);
on<RafraichirDemandesAideEvent>(_onRafraichirDemandesAide);
on<ReinitialiserDemandesAideEvent>(_onReinitialiserDemandesAide);
on<SelectionnerDemandeAideEvent>(_onSelectionnerDemandeAide);
on<SelectionnerToutesDemandesAideEvent>(_onSelectionnerToutesDemandesAide);
on<SupprimerDemandesSelectionnees>(_onSupprimerDemandesSelectionnees);
on<ExporterDemandesAideEvent>(_onExporterDemandesAide);
}
/// Handler pour charger les demandes d'aide
Future<void> _onChargerDemandesAide(
ChargerDemandesAideEvent event,
Emitter<DemandesAideState> emit,
) async {
// Sauvegarder les paramètres pour la pagination
_lastOrganisationId = event.organisationId;
_lastTypeAide = event.typeAide;
_lastStatut = event.statut;
_lastDemandeurId = event.demandeurId;
_lastUrgente = event.urgente;
if (event.forceRefresh || state is! DemandesAideLoaded) {
emit(const DemandesAideLoading());
} else if (state is DemandesAideLoaded) {
emit((state as DemandesAideLoaded).copyWith(isRefreshing: true));
}
final result = await rechercherDemandesAideUseCase(
RechercherDemandesAideParams(
organisationId: event.organisationId,
typeAide: event.typeAide,
statut: event.statut,
demandeurId: event.demandeurId,
urgente: event.urgente,
page: 0,
taille: 20,
),
);
result.fold(
(failure) => emit(DemandesAideError(
message: _mapFailureToMessage(failure),
isNetworkError: failure is NetworkFailure,
canRetry: true,
cachedData: state is DemandesAideLoaded
? (state as DemandesAideLoaded).demandes
: null,
)),
(demandes) {
final demandesFiltrees = _appliquerFiltres(demandes, const FiltresDemandesAide());
emit(DemandesAideLoaded(
demandes: demandes,
demandesFiltrees: demandesFiltrees,
hasReachedMax: demandes.length < 20,
currentPage: 0,
totalElements: demandes.length,
lastUpdated: DateTime.now(),
));
},
);
}
/// Handler pour charger plus de demandes (pagination)
Future<void> _onChargerPlusDemandesAide(
ChargerPlusDemandesAideEvent event,
Emitter<DemandesAideState> emit,
) async {
if (state is! DemandesAideLoaded) return;
final currentState = state as DemandesAideLoaded;
if (currentState.hasReachedMax || currentState.isLoadingMore) return;
emit(currentState.copyWith(isLoadingMore: true));
final result = await rechercherDemandesAideUseCase(
RechercherDemandesAideParams(
organisationId: _lastOrganisationId,
typeAide: _lastTypeAide,
statut: _lastStatut,
demandeurId: _lastDemandeurId,
urgente: _lastUrgente,
page: currentState.currentPage + 1,
taille: 20,
),
);
result.fold(
(failure) => emit(currentState.copyWith(
isLoadingMore: false,
)),
(nouvellesDemandes) {
final toutesLesdemandes = [...currentState.demandes, ...nouvellesDemandes];
final demandesFiltrees = _appliquerFiltres(toutesLesdemandes, currentState.filtres);
emit(currentState.copyWith(
demandes: toutesLesdemandes,
demandesFiltrees: demandesFiltrees,
hasReachedMax: nouvellesDemandes.length < 20,
currentPage: currentState.currentPage + 1,
totalElements: toutesLesdemandes.length,
isLoadingMore: false,
lastUpdated: DateTime.now(),
));
},
);
}
/// Handler pour créer une demande d'aide
Future<void> _onCreerDemandeAide(
CreerDemandeAideEvent event,
Emitter<DemandesAideState> emit,
) async {
emit(const DemandesAideLoading());
final result = await creerDemandeAideUseCase(
CreerDemandeAideParams(demande: event.demande),
);
result.fold(
(failure) => emit(DemandesAideError(
message: _mapFailureToMessage(failure),
isNetworkError: failure is NetworkFailure,
canRetry: true,
)),
(demande) {
emit(DemandesAideOperationSuccess(
message: TypeOperationDemande.creation.messageSucces,
demande: demande,
operation: TypeOperationDemande.creation,
));
// Recharger la liste après création
add(const ChargerDemandesAideEvent(forceRefresh: true));
},
);
}
/// Handler pour mettre à jour une demande d'aide
Future<void> _onMettreAJourDemandeAide(
MettreAJourDemandeAideEvent event,
Emitter<DemandesAideState> emit,
) async {
emit(const DemandesAideLoading());
final result = await mettreAJourDemandeAideUseCase(
MettreAJourDemandeAideParams(demande: event.demande),
);
result.fold(
(failure) => emit(DemandesAideError(
message: _mapFailureToMessage(failure),
isNetworkError: failure is NetworkFailure,
canRetry: true,
)),
(demande) {
emit(DemandesAideOperationSuccess(
message: TypeOperationDemande.modification.messageSucces,
demande: demande,
operation: TypeOperationDemande.modification,
));
// Mettre à jour la demande dans la liste si elle existe
if (state is DemandesAideLoaded) {
final currentState = state as DemandesAideLoaded;
final demandesUpdated = currentState.demandes.map((d) =>
d.id == demande.id ? demande : d
).toList();
final demandesFiltrees = _appliquerFiltres(demandesUpdated, currentState.filtres);
emit(currentState.copyWith(
demandes: demandesUpdated,
demandesFiltrees: demandesFiltrees,
lastUpdated: DateTime.now(),
));
}
},
);
}
/// Handler pour obtenir une demande d'aide spécifique
Future<void> _onObtenirDemandeAide(
ObtenirDemandeAideEvent event,
Emitter<DemandesAideState> emit,
) async {
emit(const DemandesAideLoading());
final result = await obtenirDemandeAideUseCase(
ObtenirDemandeAideParams(id: event.demandeId),
);
result.fold(
(failure) => emit(DemandesAideError(
message: _mapFailureToMessage(failure),
isNetworkError: failure is NetworkFailure,
canRetry: true,
)),
(demande) {
// Si on a déjà une liste chargée, mettre à jour la demande
if (state is DemandesAideLoaded) {
final currentState = state as DemandesAideLoaded;
final demandesUpdated = currentState.demandes.map((d) =>
d.id == demande.id ? demande : d
).toList();
// Ajouter la demande si elle n'existe pas
if (!demandesUpdated.any((d) => d.id == demande.id)) {
demandesUpdated.insert(0, demande);
}
final demandesFiltrees = _appliquerFiltres(demandesUpdated, currentState.filtres);
emit(currentState.copyWith(
demandes: demandesUpdated,
demandesFiltrees: demandesFiltrees,
lastUpdated: DateTime.now(),
));
} else {
// Créer un nouvel état avec cette demande
emit(DemandesAideLoaded(
demandes: [demande],
demandesFiltrees: [demande],
hasReachedMax: true,
currentPage: 0,
totalElements: 1,
lastUpdated: DateTime.now(),
));
}
},
);
}
/// Handler pour soumettre une demande d'aide
Future<void> _onSoumettreDemandeAide(
SoumettreDemandeAideEvent event,
Emitter<DemandesAideState> emit,
) async {
emit(const DemandesAideLoading());
final result = await soumettreDemandeAideUseCase(
SoumettreDemandeAideParams(demandeId: event.demandeId),
);
result.fold(
(failure) => emit(DemandesAideError(
message: _mapFailureToMessage(failure),
isNetworkError: failure is NetworkFailure,
canRetry: true,
)),
(demande) {
emit(DemandesAideOperationSuccess(
message: TypeOperationDemande.soumission.messageSucces,
demande: demande,
operation: TypeOperationDemande.soumission,
));
// Mettre à jour la demande dans la liste
if (state is DemandesAideLoaded) {
final currentState = state as DemandesAideLoaded;
final demandesUpdated = currentState.demandes.map((d) =>
d.id == demande.id ? demande : d
).toList();
final demandesFiltrees = _appliquerFiltres(demandesUpdated, currentState.filtres);
emit(currentState.copyWith(
demandes: demandesUpdated,
demandesFiltrees: demandesFiltrees,
lastUpdated: DateTime.now(),
));
}
},
);
}
/// Handler pour évaluer une demande d'aide
Future<void> _onEvaluerDemandeAide(
EvaluerDemandeAideEvent event,
Emitter<DemandesAideState> emit,
) async {
emit(const DemandesAideLoading());
final result = await evaluerDemandeAideUseCase(
EvaluerDemandeAideParams(
demandeId: event.demandeId,
evaluateurId: event.evaluateurId,
decision: event.decision,
commentaire: event.commentaire,
montantApprouve: event.montantApprouve,
),
);
result.fold(
(failure) => emit(DemandesAideError(
message: _mapFailureToMessage(failure),
isNetworkError: failure is NetworkFailure,
canRetry: true,
)),
(demande) {
emit(DemandesAideOperationSuccess(
message: TypeOperationDemande.evaluation.messageSucces,
demande: demande,
operation: TypeOperationDemande.evaluation,
));
// Mettre à jour la demande dans la liste
if (state is DemandesAideLoaded) {
final currentState = state as DemandesAideLoaded;
final demandesUpdated = currentState.demandes.map((d) =>
d.id == demande.id ? demande : d
).toList();
final demandesFiltrees = _appliquerFiltres(demandesUpdated, currentState.filtres);
emit(currentState.copyWith(
demandes: demandesUpdated,
demandesFiltrees: demandesFiltrees,
lastUpdated: DateTime.now(),
));
}
},
);
}
/// Handler pour charger les demandes urgentes
Future<void> _onChargerDemandesUrgentes(
ChargerDemandesUrgentesEvent event,
Emitter<DemandesAideState> emit,
) async {
emit(const DemandesAideLoading());
final result = await obtenirDemandesUrgentesUseCase(
ObtenirDemandesUrgentesParams(organisationId: event.organisationId),
);
result.fold(
(failure) => emit(DemandesAideError(
message: _mapFailureToMessage(failure),
isNetworkError: failure is NetworkFailure,
canRetry: true,
)),
(demandes) {
final demandesFiltrees = _appliquerFiltres(demandes, const FiltresDemandesAide());
emit(DemandesAideLoaded(
demandes: demandes,
demandesFiltrees: demandesFiltrees,
hasReachedMax: true,
currentPage: 0,
totalElements: demandes.length,
lastUpdated: DateTime.now(),
));
},
);
}
/// Handler pour charger mes demandes
Future<void> _onChargerMesdemandes(
ChargerMesDemandesEvent event,
Emitter<DemandesAideState> emit,
) async {
emit(const DemandesAideLoading());
final result = await obtenirMesDemandesUseCase(
ObtenirMesDemandesParams(utilisateurId: event.utilisateurId),
);
result.fold(
(failure) => emit(DemandesAideError(
message: _mapFailureToMessage(failure),
isNetworkError: failure is NetworkFailure,
canRetry: true,
)),
(demandes) {
final demandesFiltrees = _appliquerFiltres(demandes, const FiltresDemandesAide());
emit(DemandesAideLoaded(
demandes: demandes,
demandesFiltrees: demandesFiltrees,
hasReachedMax: true,
currentPage: 0,
totalElements: demandes.length,
lastUpdated: DateTime.now(),
));
},
);
}
/// Handler pour rechercher des demandes d'aide
Future<void> _onRechercherDemandesAide(
RechercherDemandesAideEvent event,
Emitter<DemandesAideState> emit,
) async {
emit(const DemandesAideLoading());
final result = await rechercherDemandesAideUseCase(
RechercherDemandesAideParams(
organisationId: event.organisationId,
typeAide: event.typeAide,
statut: event.statut,
demandeurId: event.demandeurId,
urgente: event.urgente,
page: event.page,
taille: event.taille,
),
);
result.fold(
(failure) => emit(DemandesAideError(
message: _mapFailureToMessage(failure),
isNetworkError: failure is NetworkFailure,
canRetry: true,
)),
(demandes) {
// Appliquer le filtre par mot-clé localement
var demandesFiltrees = demandes;
if (event.motCle != null && event.motCle!.isNotEmpty) {
demandesFiltrees = demandes.where((demande) =>
demande.titre.toLowerCase().contains(event.motCle!.toLowerCase()) ||
demande.description.toLowerCase().contains(event.motCle!.toLowerCase()) ||
demande.nomDemandeur.toLowerCase().contains(event.motCle!.toLowerCase())
).toList();
}
emit(DemandesAideLoaded(
demandes: demandes,
demandesFiltrees: demandesFiltrees,
hasReachedMax: demandes.length < event.taille,
currentPage: event.page,
totalElements: demandes.length,
lastUpdated: DateTime.now(),
));
},
);
}
/// Méthode utilitaire pour appliquer les filtres
List<DemandeAide> _appliquerFiltres(List<DemandeAide> demandes, FiltresDemandesAide filtres) {
var demandesFiltrees = demandes;
if (filtres.typeAide != null) {
demandesFiltrees = demandesFiltrees.where((d) => d.typeAide == filtres.typeAide).toList();
}
if (filtres.statut != null) {
demandesFiltrees = demandesFiltrees.where((d) => d.statut == filtres.statut).toList();
}
if (filtres.priorite != null) {
demandesFiltrees = demandesFiltrees.where((d) => d.priorite == filtres.priorite).toList();
}
if (filtres.urgente != null) {
demandesFiltrees = demandesFiltrees.where((d) => d.estUrgente == filtres.urgente).toList();
}
if (filtres.motCle != null && filtres.motCle!.isNotEmpty) {
final motCle = filtres.motCle!.toLowerCase();
demandesFiltrees = demandesFiltrees.where((d) =>
d.titre.toLowerCase().contains(motCle) ||
d.description.toLowerCase().contains(motCle) ||
d.nomDemandeur.toLowerCase().contains(motCle)
).toList();
}
if (filtres.montantMin != null) {
demandesFiltrees = demandesFiltrees.where((d) =>
d.montantDemande != null && d.montantDemande! >= filtres.montantMin!
).toList();
}
if (filtres.montantMax != null) {
demandesFiltrees = demandesFiltrees.where((d) =>
d.montantDemande != null && d.montantDemande! <= filtres.montantMax!
).toList();
}
if (filtres.dateDebutCreation != null) {
demandesFiltrees = demandesFiltrees.where((d) =>
d.dateCreation.isAfter(filtres.dateDebutCreation!) ||
d.dateCreation.isAtSameMomentAs(filtres.dateDebutCreation!)
).toList();
}
if (filtres.dateFinCreation != null) {
demandesFiltrees = demandesFiltrees.where((d) =>
d.dateCreation.isBefore(filtres.dateFinCreation!) ||
d.dateCreation.isAtSameMomentAs(filtres.dateFinCreation!)
).toList();
}
return demandesFiltrees;
}
/// Handler pour valider une demande d'aide
Future<void> _onValiderDemandeAide(
ValiderDemandeAideEvent event,
Emitter<DemandesAideState> emit,
) async {
final result = await validerDemandeAideUseCase(
ValiderDemandeAideParams(demande: event.demande),
);
result.fold(
(failure) => emit(DemandesAideValidation(
erreurs: {'general': _mapFailureToMessage(failure)},
isValid: false,
demande: event.demande,
)),
(isValid) => emit(DemandesAideValidation(
erreurs: const {},
isValid: isValid,
demande: event.demande,
)),
);
}
/// Handler pour calculer la priorité d'une demande
Future<void> _onCalculerPrioriteDemande(
CalculerPrioriteDemandeEvent event,
Emitter<DemandesAideState> emit,
) async {
final result = await calculerPrioriteDemandeUseCase(
CalculerPrioriteDemandeParams(demande: event.demande),
);
result.fold(
(failure) => emit(DemandesAideError(
message: _mapFailureToMessage(failure),
canRetry: false,
)),
(priorite) {
final demandeUpdated = event.demande.copyWith(priorite: priorite);
emit(DemandesAideOperationSuccess(
message: 'Priorité calculée: ${priorite.libelle}',
demande: demandeUpdated,
operation: TypeOperationDemande.modification,
));
},
);
}
/// Handler pour filtrer les demandes localement
Future<void> _onFiltrerDemandesAide(
FiltrerDemandesAideEvent event,
Emitter<DemandesAideState> emit,
) async {
if (state is! DemandesAideLoaded) return;
final currentState = state as DemandesAideLoaded;
final nouveauxFiltres = FiltresDemandesAide(
typeAide: event.typeAide,
statut: event.statut,
priorite: event.priorite,
urgente: event.urgente,
motCle: event.motCle,
);
final demandesFiltrees = _appliquerFiltres(currentState.demandes, nouveauxFiltres);
emit(currentState.copyWith(
demandesFiltrees: demandesFiltrees,
filtres: nouveauxFiltres,
));
}
/// Handler pour trier les demandes
Future<void> _onTrierDemandesAide(
TrierDemandesAideEvent event,
Emitter<DemandesAideState> emit,
) async {
if (state is! DemandesAideLoaded) return;
final currentState = state as DemandesAideLoaded;
final demandesTriees = List<DemandeAide>.from(currentState.demandesFiltrees);
// Appliquer le tri
demandesTriees.sort((a, b) {
int comparison = 0;
switch (event.critere) {
case TriDemandes.dateCreation:
comparison = a.dateCreation.compareTo(b.dateCreation);
break;
case TriDemandes.dateModification:
comparison = a.dateModification.compareTo(b.dateModification);
break;
case TriDemandes.titre:
comparison = a.titre.compareTo(b.titre);
break;
case TriDemandes.statut:
comparison = a.statut.index.compareTo(b.statut.index);
break;
case TriDemandes.priorite:
comparison = a.priorite.index.compareTo(b.priorite.index);
break;
case TriDemandes.montant:
final montantA = a.montantDemande ?? 0.0;
final montantB = b.montantDemande ?? 0.0;
comparison = montantA.compareTo(montantB);
break;
case TriDemandes.demandeur:
comparison = a.nomDemandeur.compareTo(b.nomDemandeur);
break;
}
return event.croissant ? comparison : -comparison;
});
emit(currentState.copyWith(
demandesFiltrees: demandesTriees,
criterieTri: event.critere,
triCroissant: event.croissant,
));
}
/// Handler pour rafraîchir les demandes
Future<void> _onRafraichirDemandesAide(
RafraichirDemandesAideEvent event,
Emitter<DemandesAideState> emit,
) async {
add(ChargerDemandesAideEvent(
organisationId: _lastOrganisationId,
typeAide: _lastTypeAide,
statut: _lastStatut,
demandeurId: _lastDemandeurId,
urgente: _lastUrgente,
forceRefresh: true,
));
}
/// Handler pour réinitialiser l'état
Future<void> _onReinitialiserDemandesAide(
ReinitialiserDemandesAideEvent event,
Emitter<DemandesAideState> emit,
) async {
_lastOrganisationId = null;
_lastTypeAide = null;
_lastStatut = null;
_lastDemandeurId = null;
_lastUrgente = null;
emit(const DemandesAideInitial());
}
/// Handler pour sélectionner/désélectionner une demande
Future<void> _onSelectionnerDemandeAide(
SelectionnerDemandeAideEvent event,
Emitter<DemandesAideState> emit,
) async {
if (state is! DemandesAideLoaded) return;
final currentState = state as DemandesAideLoaded;
final nouvellesSelections = Map<String, bool>.from(currentState.demandesSelectionnees);
if (event.selectionne) {
nouvellesSelections[event.demandeId] = true;
} else {
nouvellesSelections.remove(event.demandeId);
}
emit(currentState.copyWith(demandesSelectionnees: nouvellesSelections));
}
/// Handler pour sélectionner/désélectionner toutes les demandes
Future<void> _onSelectionnerToutesDemandesAide(
SelectionnerToutesDemandesAideEvent event,
Emitter<DemandesAideState> emit,
) async {
if (state is! DemandesAideLoaded) return;
final currentState = state as DemandesAideLoaded;
final nouvellesSelections = <String, bool>{};
if (event.selectionne) {
for (final demande in currentState.demandesFiltrees) {
nouvellesSelections[demande.id] = true;
}
}
emit(currentState.copyWith(demandesSelectionnees: nouvellesSelections));
}
/// Handler pour supprimer les demandes sélectionnées
Future<void> _onSupprimerDemandesSelectionnees(
SupprimerDemandesSelectionnees event,
Emitter<DemandesAideState> emit,
) async {
if (state is! DemandesAideLoaded) return;
emit(const DemandesAideLoading());
// Simuler la suppression (à implémenter avec un vrai use case)
await Future.delayed(const Duration(seconds: 1));
final currentState = state as DemandesAideLoaded;
final demandesRestantes = currentState.demandes
.where((demande) => !event.demandeIds.contains(demande.id))
.toList();
final demandesFiltrees = _appliquerFiltres(demandesRestantes, currentState.filtres);
emit(DemandesAideOperationSuccess(
message: '${event.demandeIds.length} demande(s) supprimée(s) avec succès',
operation: TypeOperationDemande.suppression,
));
emit(currentState.copyWith(
demandes: demandesRestantes,
demandesFiltrees: demandesFiltrees,
demandesSelectionnees: const {},
totalElements: demandesRestantes.length,
lastUpdated: DateTime.now(),
));
}
/// Handler pour exporter les demandes
Future<void> _onExporterDemandesAide(
ExporterDemandesAideEvent event,
Emitter<DemandesAideState> emit,
) async {
if (state is! DemandesAideLoaded) return;
emit(const DemandesAideExporting(progress: 0.0, currentStep: 'Préparation...'));
// Simuler l'export avec progression
for (int i = 1; i <= 5; i++) {
await Future.delayed(const Duration(milliseconds: 500));
emit(DemandesAideExporting(
progress: i / 5,
currentStep: _getExportStep(i, event.format),
));
}
// Simuler la génération du fichier
final fileName = 'demandes_aide_${DateTime.now().millisecondsSinceEpoch}${event.format.extension}';
final filePath = '/storage/emulated/0/Download/$fileName';
emit(DemandesAideExported(
filePath: filePath,
format: event.format,
nombreDemandes: event.demandeIds.length,
));
emit(DemandesAideOperationSuccess(
message: 'Export réalisé avec succès: $fileName',
operation: TypeOperationDemande.export,
));
}
/// Méthode utilitaire pour obtenir l'étape d'export
String _getExportStep(int step, FormatExport format) {
switch (step) {
case 1:
return 'Récupération des données...';
case 2:
return 'Formatage des données...';
case 3:
return 'Génération du fichier ${format.libelle}...';
case 4:
return 'Optimisation...';
case 5:
return 'Finalisation...';
default:
return 'Traitement...';
}
}
/// Méthode utilitaire pour mapper les erreurs
String _mapFailureToMessage(Failure failure) {
switch (failure.runtimeType) {
case ServerFailure:
return 'Erreur serveur. Veuillez réessayer plus tard.';
case NetworkFailure:
return 'Pas de connexion internet. Vérifiez votre connexion.';
case CacheFailure:
return 'Erreur de cache local.';
case ValidationFailure:
return failure.message;
case NotFoundFailure:
return 'Demande d\'aide non trouvée.';
default:
return 'Une erreur inattendue s\'est produite.';
}
}
}

View File

@@ -0,0 +1,388 @@
import 'package:equatable/equatable.dart';
import '../../../domain/entities/demande_aide.dart';
/// Événements pour la gestion des demandes d'aide
///
/// Ces événements représentent toutes les actions possibles
/// que l'utilisateur peut effectuer sur les demandes d'aide.
abstract class DemandesAideEvent extends Equatable {
const DemandesAideEvent();
@override
List<Object?> get props => [];
}
/// Événement pour charger les demandes d'aide
class ChargerDemandesAideEvent extends DemandesAideEvent {
final String? organisationId;
final TypeAide? typeAide;
final StatutAide? statut;
final String? demandeurId;
final bool? urgente;
final bool forceRefresh;
const ChargerDemandesAideEvent({
this.organisationId,
this.typeAide,
this.statut,
this.demandeurId,
this.urgente,
this.forceRefresh = false,
});
@override
List<Object?> get props => [
organisationId,
typeAide,
statut,
demandeurId,
urgente,
forceRefresh,
];
}
/// Événement pour charger plus de demandes (pagination)
class ChargerPlusDemandesAideEvent extends DemandesAideEvent {
const ChargerPlusDemandesAideEvent();
}
/// Événement pour créer une nouvelle demande d'aide
class CreerDemandeAideEvent extends DemandesAideEvent {
final DemandeAide demande;
const CreerDemandeAideEvent({required this.demande});
@override
List<Object> get props => [demande];
}
/// Événement pour mettre à jour une demande d'aide
class MettreAJourDemandeAideEvent extends DemandesAideEvent {
final DemandeAide demande;
const MettreAJourDemandeAideEvent({required this.demande});
@override
List<Object> get props => [demande];
}
/// Événement pour obtenir une demande d'aide spécifique
class ObtenirDemandeAideEvent extends DemandesAideEvent {
final String demandeId;
const ObtenirDemandeAideEvent({required this.demandeId});
@override
List<Object> get props => [demandeId];
}
/// Événement pour soumettre une demande d'aide
class SoumettreDemandeAideEvent extends DemandesAideEvent {
final String demandeId;
const SoumettreDemandeAideEvent({required this.demandeId});
@override
List<Object> get props => [demandeId];
}
/// Événement pour évaluer une demande d'aide
class EvaluerDemandeAideEvent extends DemandesAideEvent {
final String demandeId;
final String evaluateurId;
final StatutAide decision;
final String? commentaire;
final double? montantApprouve;
const EvaluerDemandeAideEvent({
required this.demandeId,
required this.evaluateurId,
required this.decision,
this.commentaire,
this.montantApprouve,
});
@override
List<Object?> get props => [
demandeId,
evaluateurId,
decision,
commentaire,
montantApprouve,
];
}
/// Événement pour charger les demandes urgentes
class ChargerDemandesUrgentesEvent extends DemandesAideEvent {
final String organisationId;
const ChargerDemandesUrgentesEvent({required this.organisationId});
@override
List<Object> get props => [organisationId];
}
/// Événement pour charger mes demandes
class ChargerMesDemandesEvent extends DemandesAideEvent {
final String utilisateurId;
const ChargerMesDemandesEvent({required this.utilisateurId});
@override
List<Object> get props => [utilisateurId];
}
/// Événement pour rechercher des demandes d'aide
class RechercherDemandesAideEvent extends DemandesAideEvent {
final String? organisationId;
final TypeAide? typeAide;
final StatutAide? statut;
final String? demandeurId;
final bool? urgente;
final String? motCle;
final int page;
final int taille;
const RechercherDemandesAideEvent({
this.organisationId,
this.typeAide,
this.statut,
this.demandeurId,
this.urgente,
this.motCle,
this.page = 0,
this.taille = 20,
});
@override
List<Object?> get props => [
organisationId,
typeAide,
statut,
demandeurId,
urgente,
motCle,
page,
taille,
];
}
/// Événement pour valider une demande d'aide
class ValiderDemandeAideEvent extends DemandesAideEvent {
final DemandeAide demande;
const ValiderDemandeAideEvent({required this.demande});
@override
List<Object> get props => [demande];
}
/// Événement pour calculer la priorité d'une demande
class CalculerPrioriteDemandeEvent extends DemandesAideEvent {
final DemandeAide demande;
const CalculerPrioriteDemandeEvent({required this.demande});
@override
List<Object> get props => [demande];
}
/// Événement pour filtrer les demandes localement
class FiltrerDemandesAideEvent extends DemandesAideEvent {
final TypeAide? typeAide;
final StatutAide? statut;
final PrioriteAide? priorite;
final bool? urgente;
final String? motCle;
const FiltrerDemandesAideEvent({
this.typeAide,
this.statut,
this.priorite,
this.urgente,
this.motCle,
});
@override
List<Object?> get props => [
typeAide,
statut,
priorite,
urgente,
motCle,
];
}
/// Événement pour trier les demandes
class TrierDemandesAideEvent extends DemandesAideEvent {
final TriDemandes critere;
final bool croissant;
const TrierDemandesAideEvent({
required this.critere,
this.croissant = true,
});
@override
List<Object> get props => [critere, croissant];
}
/// Événement pour rafraîchir les demandes
class RafraichirDemandesAideEvent extends DemandesAideEvent {
const RafraichirDemandesAideEvent();
}
/// Événement pour réinitialiser l'état
class ReinitialiserDemandesAideEvent extends DemandesAideEvent {
const ReinitialiserDemandesAideEvent();
}
/// Événement pour sélectionner/désélectionner une demande
class SelectionnerDemandeAideEvent extends DemandesAideEvent {
final String demandeId;
final bool selectionne;
const SelectionnerDemandeAideEvent({
required this.demandeId,
required this.selectionne,
});
@override
List<Object> get props => [demandeId, selectionne];
}
/// Événement pour sélectionner/désélectionner toutes les demandes
class SelectionnerToutesDemandesAideEvent extends DemandesAideEvent {
final bool selectionne;
const SelectionnerToutesDemandesAideEvent({required this.selectionne});
@override
List<Object> get props => [selectionne];
}
/// Événement pour supprimer des demandes sélectionnées
class SupprimerDemandesSelectionnees extends DemandesAideEvent {
final List<String> demandeIds;
const SupprimerDemandesSelectionnees({required this.demandeIds});
@override
List<Object> get props => [demandeIds];
}
/// Événement pour exporter des demandes
class ExporterDemandesAideEvent extends DemandesAideEvent {
final List<String> demandeIds;
final FormatExport format;
const ExporterDemandesAideEvent({
required this.demandeIds,
required this.format,
});
@override
List<Object> get props => [demandeIds, format];
}
/// Énumération pour les critères de tri
enum TriDemandes {
dateCreation,
dateModification,
titre,
statut,
priorite,
montant,
demandeur,
}
/// Énumération pour les formats d'export
enum FormatExport {
pdf,
excel,
csv,
json,
}
/// Extension pour obtenir le libellé des critères de tri
extension TriDemandesExtension on TriDemandes {
String get libelle {
switch (this) {
case TriDemandes.dateCreation:
return 'Date de création';
case TriDemandes.dateModification:
return 'Date de modification';
case TriDemandes.titre:
return 'Titre';
case TriDemandes.statut:
return 'Statut';
case TriDemandes.priorite:
return 'Priorité';
case TriDemandes.montant:
return 'Montant';
case TriDemandes.demandeur:
return 'Demandeur';
}
}
String get icone {
switch (this) {
case TriDemandes.dateCreation:
return 'calendar_today';
case TriDemandes.dateModification:
return 'update';
case TriDemandes.titre:
return 'title';
case TriDemandes.statut:
return 'flag';
case TriDemandes.priorite:
return 'priority_high';
case TriDemandes.montant:
return 'attach_money';
case TriDemandes.demandeur:
return 'person';
}
}
}
/// Extension pour obtenir le libellé des formats d'export
extension FormatExportExtension on FormatExport {
String get libelle {
switch (this) {
case FormatExport.pdf:
return 'PDF';
case FormatExport.excel:
return 'Excel';
case FormatExport.csv:
return 'CSV';
case FormatExport.json:
return 'JSON';
}
}
String get extension {
switch (this) {
case FormatExport.pdf:
return '.pdf';
case FormatExport.excel:
return '.xlsx';
case FormatExport.csv:
return '.csv';
case FormatExport.json:
return '.json';
}
}
String get mimeType {
switch (this) {
case FormatExport.pdf:
return 'application/pdf';
case FormatExport.excel:
return 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
case FormatExport.csv:
return 'text/csv';
case FormatExport.json:
return 'application/json';
}
}
}

View File

@@ -0,0 +1,434 @@
import 'package:equatable/equatable.dart';
import '../../../domain/entities/demande_aide.dart';
import 'demandes_aide_event.dart';
/// États pour la gestion des demandes d'aide
///
/// Ces états représentent tous les états possibles
/// de l'interface utilisateur pour les demandes d'aide.
abstract class DemandesAideState extends Equatable {
const DemandesAideState();
@override
List<Object?> get props => [];
}
/// État initial
class DemandesAideInitial extends DemandesAideState {
const DemandesAideInitial();
}
/// État de chargement
class DemandesAideLoading extends DemandesAideState {
final bool isRefreshing;
final bool isLoadingMore;
const DemandesAideLoading({
this.isRefreshing = false,
this.isLoadingMore = false,
});
@override
List<Object> get props => [isRefreshing, isLoadingMore];
}
/// État de succès avec données chargées
class DemandesAideLoaded extends DemandesAideState {
final List<DemandeAide> demandes;
final List<DemandeAide> demandesFiltrees;
final bool hasReachedMax;
final int currentPage;
final int totalElements;
final Map<String, bool> demandesSelectionnees;
final TriDemandes? criterieTri;
final bool triCroissant;
final FiltresDemandesAide filtres;
final bool isRefreshing;
final bool isLoadingMore;
final DateTime lastUpdated;
const DemandesAideLoaded({
required this.demandes,
required this.demandesFiltrees,
this.hasReachedMax = false,
this.currentPage = 0,
this.totalElements = 0,
this.demandesSelectionnees = const {},
this.criterieTri,
this.triCroissant = true,
this.filtres = const FiltresDemandesAide(),
this.isRefreshing = false,
this.isLoadingMore = false,
required this.lastUpdated,
});
@override
List<Object?> get props => [
demandes,
demandesFiltrees,
hasReachedMax,
currentPage,
totalElements,
demandesSelectionnees,
criterieTri,
triCroissant,
filtres,
isRefreshing,
isLoadingMore,
lastUpdated,
];
/// Copie l'état avec de nouvelles valeurs
DemandesAideLoaded copyWith({
List<DemandeAide>? demandes,
List<DemandeAide>? demandesFiltrees,
bool? hasReachedMax,
int? currentPage,
int? totalElements,
Map<String, bool>? demandesSelectionnees,
TriDemandes? criterieTri,
bool? triCroissant,
FiltresDemandesAide? filtres,
bool? isRefreshing,
bool? isLoadingMore,
DateTime? lastUpdated,
}) {
return DemandesAideLoaded(
demandes: demandes ?? this.demandes,
demandesFiltrees: demandesFiltrees ?? this.demandesFiltrees,
hasReachedMax: hasReachedMax ?? this.hasReachedMax,
currentPage: currentPage ?? this.currentPage,
totalElements: totalElements ?? this.totalElements,
demandesSelectionnees: demandesSelectionnees ?? this.demandesSelectionnees,
criterieTri: criterieTri ?? this.criterieTri,
triCroissant: triCroissant ?? this.triCroissant,
filtres: filtres ?? this.filtres,
isRefreshing: isRefreshing ?? this.isRefreshing,
isLoadingMore: isLoadingMore ?? this.isLoadingMore,
lastUpdated: lastUpdated ?? this.lastUpdated,
);
}
/// Obtient le nombre de demandes sélectionnées
int get nombreDemandesSelectionnees {
return demandesSelectionnees.values.where((selected) => selected).length;
}
/// Vérifie si toutes les demandes sont sélectionnées
bool get toutesDemandesSelectionnees {
if (demandesFiltrees.isEmpty) return false;
return demandesFiltrees.every((demande) =>
demandesSelectionnees[demande.id] == true
);
}
/// Obtient les IDs des demandes sélectionnées
List<String> get demandesSelectionneesIds {
return demandesSelectionnees.entries
.where((entry) => entry.value)
.map((entry) => entry.key)
.toList();
}
/// Obtient les demandes sélectionnées
List<DemandeAide> get demandesSelectionneesEntities {
return demandes.where((demande) =>
demandesSelectionnees[demande.id] == true
).toList();
}
/// Vérifie si des données sont disponibles
bool get hasData => demandes.isNotEmpty;
/// Vérifie si des filtres sont appliqués
bool get hasFiltres => !filtres.isEmpty;
/// Obtient le texte de statut
String get statusText {
if (isRefreshing) return 'Actualisation...';
if (isLoadingMore) return 'Chargement...';
if (demandesFiltrees.isEmpty && hasData) return 'Aucun résultat pour les filtres appliqués';
if (demandesFiltrees.isEmpty) return 'Aucune demande d\'aide';
return '${demandesFiltrees.length} demande${demandesFiltrees.length > 1 ? 's' : ''}';
}
}
/// État d'erreur
class DemandesAideError extends DemandesAideState {
final String message;
final String? code;
final bool isNetworkError;
final bool canRetry;
final List<DemandeAide>? cachedData;
const DemandesAideError({
required this.message,
this.code,
this.isNetworkError = false,
this.canRetry = true,
this.cachedData,
});
@override
List<Object?> get props => [
message,
code,
isNetworkError,
canRetry,
cachedData,
];
/// Vérifie si des données en cache sont disponibles
bool get hasCachedData => cachedData != null && cachedData!.isNotEmpty;
}
/// État de succès pour une opération spécifique
class DemandesAideOperationSuccess extends DemandesAideState {
final String message;
final DemandeAide? demande;
final TypeOperationDemande operation;
const DemandesAideOperationSuccess({
required this.message,
this.demande,
required this.operation,
});
@override
List<Object?> get props => [message, demande, operation];
}
/// État de validation
class DemandesAideValidation extends DemandesAideState {
final Map<String, String> erreurs;
final bool isValid;
final DemandeAide? demande;
const DemandesAideValidation({
required this.erreurs,
required this.isValid,
this.demande,
});
@override
List<Object?> get props => [erreurs, isValid, demande];
/// Obtient la première erreur
String? get premiereErreur {
return erreurs.values.isNotEmpty ? erreurs.values.first : null;
}
/// Obtient les erreurs pour un champ spécifique
String? getErreurPourChamp(String champ) {
return erreurs[champ];
}
}
/// État d'export
class DemandesAideExporting extends DemandesAideState {
final double progress;
final String? currentStep;
const DemandesAideExporting({
required this.progress,
this.currentStep,
});
@override
List<Object?> get props => [progress, currentStep];
}
/// État d'export terminé
class DemandesAideExported extends DemandesAideState {
final String filePath;
final FormatExport format;
final int nombreDemandes;
const DemandesAideExported({
required this.filePath,
required this.format,
required this.nombreDemandes,
});
@override
List<Object> get props => [filePath, format, nombreDemandes];
}
/// Classe pour les filtres des demandes d'aide
class FiltresDemandesAide extends Equatable {
final TypeAide? typeAide;
final StatutAide? statut;
final PrioriteAide? priorite;
final bool? urgente;
final String? motCle;
final String? organisationId;
final String? demandeurId;
final DateTime? dateDebutCreation;
final DateTime? dateFinCreation;
final double? montantMin;
final double? montantMax;
const FiltresDemandesAide({
this.typeAide,
this.statut,
this.priorite,
this.urgente,
this.motCle,
this.organisationId,
this.demandeurId,
this.dateDebutCreation,
this.dateFinCreation,
this.montantMin,
this.montantMax,
});
@override
List<Object?> get props => [
typeAide,
statut,
priorite,
urgente,
motCle,
organisationId,
demandeurId,
dateDebutCreation,
dateFinCreation,
montantMin,
montantMax,
];
/// Copie les filtres avec de nouvelles valeurs
FiltresDemandesAide copyWith({
TypeAide? typeAide,
StatutAide? statut,
PrioriteAide? priorite,
bool? urgente,
String? motCle,
String? organisationId,
String? demandeurId,
DateTime? dateDebutCreation,
DateTime? dateFinCreation,
double? montantMin,
double? montantMax,
}) {
return FiltresDemandesAide(
typeAide: typeAide ?? this.typeAide,
statut: statut ?? this.statut,
priorite: priorite ?? this.priorite,
urgente: urgente ?? this.urgente,
motCle: motCle ?? this.motCle,
organisationId: organisationId ?? this.organisationId,
demandeurId: demandeurId ?? this.demandeurId,
dateDebutCreation: dateDebutCreation ?? this.dateDebutCreation,
dateFinCreation: dateFinCreation ?? this.dateFinCreation,
montantMin: montantMin ?? this.montantMin,
montantMax: montantMax ?? this.montantMax,
);
}
/// Réinitialise tous les filtres
FiltresDemandesAide clear() {
return const FiltresDemandesAide();
}
/// Vérifie si les filtres sont vides
bool get isEmpty {
return typeAide == null &&
statut == null &&
priorite == null &&
urgente == null &&
(motCle == null || motCle!.isEmpty) &&
organisationId == null &&
demandeurId == null &&
dateDebutCreation == null &&
dateFinCreation == null &&
montantMin == null &&
montantMax == null;
}
/// Obtient le nombre de filtres actifs
int get nombreFiltresActifs {
int count = 0;
if (typeAide != null) count++;
if (statut != null) count++;
if (priorite != null) count++;
if (urgente != null) count++;
if (motCle != null && motCle!.isNotEmpty) count++;
if (organisationId != null) count++;
if (demandeurId != null) count++;
if (dateDebutCreation != null) count++;
if (dateFinCreation != null) count++;
if (montantMin != null) count++;
if (montantMax != null) count++;
return count;
}
/// Obtient une description textuelle des filtres
String get description {
final parts = <String>[];
if (typeAide != null) parts.add('Type: ${typeAide!.libelle}');
if (statut != null) parts.add('Statut: ${statut!.libelle}');
if (priorite != null) parts.add('Priorité: ${priorite!.libelle}');
if (urgente == true) parts.add('Urgente uniquement');
if (motCle != null && motCle!.isNotEmpty) parts.add('Recherche: "$motCle"');
if (montantMin != null || montantMax != null) {
if (montantMin != null && montantMax != null) {
parts.add('Montant: ${montantMin!.toInt()} - ${montantMax!.toInt()} FCFA');
} else if (montantMin != null) {
parts.add('Montant min: ${montantMin!.toInt()} FCFA');
} else {
parts.add('Montant max: ${montantMax!.toInt()} FCFA');
}
}
return parts.join(', ');
}
}
/// Énumération pour les types d'opération
enum TypeOperationDemande {
creation,
modification,
soumission,
evaluation,
suppression,
export,
}
/// Extension pour obtenir le libellé des opérations
extension TypeOperationDemandeExtension on TypeOperationDemande {
String get libelle {
switch (this) {
case TypeOperationDemande.creation:
return 'Création';
case TypeOperationDemande.modification:
return 'Modification';
case TypeOperationDemande.soumission:
return 'Soumission';
case TypeOperationDemande.evaluation:
return 'Évaluation';
case TypeOperationDemande.suppression:
return 'Suppression';
case TypeOperationDemande.export:
return 'Export';
}
}
String get messageSucces {
switch (this) {
case TypeOperationDemande.creation:
return 'Demande d\'aide créée avec succès';
case TypeOperationDemande.modification:
return 'Demande d\'aide modifiée avec succès';
case TypeOperationDemande.soumission:
return 'Demande d\'aide soumise avec succès';
case TypeOperationDemande.evaluation:
return 'Demande d\'aide évaluée avec succès';
case TypeOperationDemande.suppression:
return 'Demande d\'aide supprimée avec succès';
case TypeOperationDemande.export:
return 'Export réalisé avec succès';
}
}
}

View File

@@ -0,0 +1,438 @@
import 'package:equatable/equatable.dart';
import '../../../domain/entities/evaluation_aide.dart';
/// Événements pour la gestion des évaluations d'aide
///
/// Ces événements représentent toutes les actions possibles
/// que l'utilisateur peut effectuer sur les évaluations d'aide.
abstract class EvaluationsEvent extends Equatable {
const EvaluationsEvent();
@override
List<Object?> get props => [];
}
/// Événement pour charger les évaluations
class ChargerEvaluationsEvent extends EvaluationsEvent {
final String? demandeId;
final String? evaluateurId;
final TypeEvaluateur? typeEvaluateur;
final StatutAide? decision;
final bool forceRefresh;
const ChargerEvaluationsEvent({
this.demandeId,
this.evaluateurId,
this.typeEvaluateur,
this.decision,
this.forceRefresh = false,
});
@override
List<Object?> get props => [
demandeId,
evaluateurId,
typeEvaluateur,
decision,
forceRefresh,
];
}
/// Événement pour charger plus d'évaluations (pagination)
class ChargerPlusEvaluationsEvent extends EvaluationsEvent {
const ChargerPlusEvaluationsEvent();
}
/// Événement pour créer une nouvelle évaluation
class CreerEvaluationEvent extends EvaluationsEvent {
final EvaluationAide evaluation;
const CreerEvaluationEvent({required this.evaluation});
@override
List<Object> get props => [evaluation];
}
/// Événement pour mettre à jour une évaluation
class MettreAJourEvaluationEvent extends EvaluationsEvent {
final EvaluationAide evaluation;
const MettreAJourEvaluationEvent({required this.evaluation});
@override
List<Object> get props => [evaluation];
}
/// Événement pour obtenir une évaluation spécifique
class ObtenirEvaluationEvent extends EvaluationsEvent {
final String evaluationId;
const ObtenirEvaluationEvent({required this.evaluationId});
@override
List<Object> get props => [evaluationId];
}
/// Événement pour soumettre une évaluation
class SoumettreEvaluationEvent extends EvaluationsEvent {
final String evaluationId;
const SoumettreEvaluationEvent({required this.evaluationId});
@override
List<Object> get props => [evaluationId];
}
/// Événement pour approuver une évaluation
class ApprouverEvaluationEvent extends EvaluationsEvent {
final String evaluationId;
final String? commentaire;
const ApprouverEvaluationEvent({
required this.evaluationId,
this.commentaire,
});
@override
List<Object?> get props => [evaluationId, commentaire];
}
/// Événement pour rejeter une évaluation
class RejeterEvaluationEvent extends EvaluationsEvent {
final String evaluationId;
final String motifRejet;
const RejeterEvaluationEvent({
required this.evaluationId,
required this.motifRejet,
});
@override
List<Object> get props => [evaluationId, motifRejet];
}
/// Événement pour rechercher des évaluations
class RechercherEvaluationsEvent extends EvaluationsEvent {
final String? demandeId;
final String? evaluateurId;
final TypeEvaluateur? typeEvaluateur;
final StatutAide? decision;
final DateTime? dateDebut;
final DateTime? dateFin;
final double? noteMin;
final double? noteMax;
final String? motCle;
final int page;
final int taille;
const RechercherEvaluationsEvent({
this.demandeId,
this.evaluateurId,
this.typeEvaluateur,
this.decision,
this.dateDebut,
this.dateFin,
this.noteMin,
this.noteMax,
this.motCle,
this.page = 0,
this.taille = 20,
});
@override
List<Object?> get props => [
demandeId,
evaluateurId,
typeEvaluateur,
decision,
dateDebut,
dateFin,
noteMin,
noteMax,
motCle,
page,
taille,
];
}
/// Événement pour charger mes évaluations
class ChargerMesEvaluationsEvent extends EvaluationsEvent {
final String evaluateurId;
const ChargerMesEvaluationsEvent({required this.evaluateurId});
@override
List<Object> get props => [evaluateurId];
}
/// Événement pour charger les évaluations en attente
class ChargerEvaluationsEnAttenteEvent extends EvaluationsEvent {
final String? evaluateurId;
final TypeEvaluateur? typeEvaluateur;
const ChargerEvaluationsEnAttenteEvent({
this.evaluateurId,
this.typeEvaluateur,
});
@override
List<Object?> get props => [evaluateurId, typeEvaluateur];
}
/// Événement pour valider une évaluation
class ValiderEvaluationEvent extends EvaluationsEvent {
final EvaluationAide evaluation;
const ValiderEvaluationEvent({required this.evaluation});
@override
List<Object> get props => [evaluation];
}
/// Événement pour calculer la note globale
class CalculerNoteGlobaleEvent extends EvaluationsEvent {
final Map<String, double> criteres;
const CalculerNoteGlobaleEvent({required this.criteres});
@override
List<Object> get props => [criteres];
}
/// Événement pour filtrer les évaluations localement
class FiltrerEvaluationsEvent extends EvaluationsEvent {
final TypeEvaluateur? typeEvaluateur;
final StatutAide? decision;
final double? noteMin;
final double? noteMax;
final String? motCle;
final DateTime? dateDebut;
final DateTime? dateFin;
const FiltrerEvaluationsEvent({
this.typeEvaluateur,
this.decision,
this.noteMin,
this.noteMax,
this.motCle,
this.dateDebut,
this.dateFin,
});
@override
List<Object?> get props => [
typeEvaluateur,
decision,
noteMin,
noteMax,
motCle,
dateDebut,
dateFin,
];
}
/// Événement pour trier les évaluations
class TrierEvaluationsEvent extends EvaluationsEvent {
final TriEvaluations critere;
final bool croissant;
const TrierEvaluationsEvent({
required this.critere,
this.croissant = true,
});
@override
List<Object> get props => [critere, croissant];
}
/// Événement pour rafraîchir les évaluations
class RafraichirEvaluationsEvent extends EvaluationsEvent {
const RafraichirEvaluationsEvent();
}
/// Événement pour réinitialiser l'état
class ReinitialiserEvaluationsEvent extends EvaluationsEvent {
const ReinitialiserEvaluationsEvent();
}
/// Événement pour sélectionner/désélectionner une évaluation
class SelectionnerEvaluationEvent extends EvaluationsEvent {
final String evaluationId;
final bool selectionne;
const SelectionnerEvaluationEvent({
required this.evaluationId,
required this.selectionne,
});
@override
List<Object> get props => [evaluationId, selectionne];
}
/// Événement pour sélectionner/désélectionner toutes les évaluations
class SelectionnerToutesEvaluationsEvent extends EvaluationsEvent {
final bool selectionne;
const SelectionnerToutesEvaluationsEvent({required this.selectionne});
@override
List<Object> get props => [selectionne];
}
/// Événement pour supprimer des évaluations sélectionnées
class SupprimerEvaluationsSelectionnees extends EvaluationsEvent {
final List<String> evaluationIds;
const SupprimerEvaluationsSelectionnees({required this.evaluationIds});
@override
List<Object> get props => [evaluationIds];
}
/// Événement pour exporter des évaluations
class ExporterEvaluationsEvent extends EvaluationsEvent {
final List<String> evaluationIds;
final FormatExport format;
const ExporterEvaluationsEvent({
required this.evaluationIds,
required this.format,
});
@override
List<Object> get props => [evaluationIds, format];
}
/// Événement pour obtenir les statistiques d'évaluation
class ObtenirStatistiquesEvaluationEvent extends EvaluationsEvent {
final String? evaluateurId;
final DateTime? dateDebut;
final DateTime? dateFin;
const ObtenirStatistiquesEvaluationEvent({
this.evaluateurId,
this.dateDebut,
this.dateFin,
});
@override
List<Object?> get props => [evaluateurId, dateDebut, dateFin];
}
/// Événement pour signaler une évaluation
class SignalerEvaluationEvent extends EvaluationsEvent {
final String evaluationId;
final String motifSignalement;
final String? description;
const SignalerEvaluationEvent({
required this.evaluationId,
required this.motifSignalement,
this.description,
});
@override
List<Object?> get props => [evaluationId, motifSignalement, description];
}
/// Énumération pour les critères de tri
enum TriEvaluations {
dateEvaluation,
dateCreation,
noteGlobale,
decision,
evaluateur,
typeEvaluateur,
demandeId,
}
/// Énumération pour les formats d'export
enum FormatExport {
pdf,
excel,
csv,
json,
}
/// Extension pour obtenir le libellé des critères de tri
extension TriEvaluationsExtension on TriEvaluations {
String get libelle {
switch (this) {
case TriEvaluations.dateEvaluation:
return 'Date d\'évaluation';
case TriEvaluations.dateCreation:
return 'Date de création';
case TriEvaluations.noteGlobale:
return 'Note globale';
case TriEvaluations.decision:
return 'Décision';
case TriEvaluations.evaluateur:
return 'Évaluateur';
case TriEvaluations.typeEvaluateur:
return 'Type d\'évaluateur';
case TriEvaluations.demandeId:
return 'Demande';
}
}
String get icone {
switch (this) {
case TriEvaluations.dateEvaluation:
return 'calendar_today';
case TriEvaluations.dateCreation:
return 'schedule';
case TriEvaluations.noteGlobale:
return 'star';
case TriEvaluations.decision:
return 'gavel';
case TriEvaluations.evaluateur:
return 'person';
case TriEvaluations.typeEvaluateur:
return 'badge';
case TriEvaluations.demandeId:
return 'description';
}
}
}
/// Extension pour obtenir le libellé des formats d'export
extension FormatExportExtension on FormatExport {
String get libelle {
switch (this) {
case FormatExport.pdf:
return 'PDF';
case FormatExport.excel:
return 'Excel';
case FormatExport.csv:
return 'CSV';
case FormatExport.json:
return 'JSON';
}
}
String get extension {
switch (this) {
case FormatExport.pdf:
return '.pdf';
case FormatExport.excel:
return '.xlsx';
case FormatExport.csv:
return '.csv';
case FormatExport.json:
return '.json';
}
}
String get mimeType {
switch (this) {
case FormatExport.pdf:
return 'application/pdf';
case FormatExport.excel:
return 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
case FormatExport.csv:
return 'text/csv';
case FormatExport.json:
return 'application/json';
}
}
}

View File

@@ -0,0 +1,478 @@
import 'package:equatable/equatable.dart';
import '../../../domain/entities/evaluation_aide.dart';
import 'evaluations_event.dart';
/// États pour la gestion des évaluations d'aide
///
/// Ces états représentent tous les états possibles
/// de l'interface utilisateur pour les évaluations d'aide.
abstract class EvaluationsState extends Equatable {
const EvaluationsState();
@override
List<Object?> get props => [];
}
/// État initial
class EvaluationsInitial extends EvaluationsState {
const EvaluationsInitial();
}
/// État de chargement
class EvaluationsLoading extends EvaluationsState {
final bool isRefreshing;
final bool isLoadingMore;
const EvaluationsLoading({
this.isRefreshing = false,
this.isLoadingMore = false,
});
@override
List<Object> get props => [isRefreshing, isLoadingMore];
}
/// État de succès avec données chargées
class EvaluationsLoaded extends EvaluationsState {
final List<EvaluationAide> evaluations;
final List<EvaluationAide> evaluationsFiltrees;
final bool hasReachedMax;
final int currentPage;
final int totalElements;
final Map<String, bool> evaluationsSelectionnees;
final TriEvaluations? criterieTri;
final bool triCroissant;
final FiltresEvaluations filtres;
final bool isRefreshing;
final bool isLoadingMore;
final DateTime lastUpdated;
const EvaluationsLoaded({
required this.evaluations,
required this.evaluationsFiltrees,
this.hasReachedMax = false,
this.currentPage = 0,
this.totalElements = 0,
this.evaluationsSelectionnees = const {},
this.criterieTri,
this.triCroissant = true,
this.filtres = const FiltresEvaluations(),
this.isRefreshing = false,
this.isLoadingMore = false,
required this.lastUpdated,
});
@override
List<Object?> get props => [
evaluations,
evaluationsFiltrees,
hasReachedMax,
currentPage,
totalElements,
evaluationsSelectionnees,
criterieTri,
triCroissant,
filtres,
isRefreshing,
isLoadingMore,
lastUpdated,
];
/// Copie l'état avec de nouvelles valeurs
EvaluationsLoaded copyWith({
List<EvaluationAide>? evaluations,
List<EvaluationAide>? evaluationsFiltrees,
bool? hasReachedMax,
int? currentPage,
int? totalElements,
Map<String, bool>? evaluationsSelectionnees,
TriEvaluations? criterieTri,
bool? triCroissant,
FiltresEvaluations? filtres,
bool? isRefreshing,
bool? isLoadingMore,
DateTime? lastUpdated,
}) {
return EvaluationsLoaded(
evaluations: evaluations ?? this.evaluations,
evaluationsFiltrees: evaluationsFiltrees ?? this.evaluationsFiltrees,
hasReachedMax: hasReachedMax ?? this.hasReachedMax,
currentPage: currentPage ?? this.currentPage,
totalElements: totalElements ?? this.totalElements,
evaluationsSelectionnees: evaluationsSelectionnees ?? this.evaluationsSelectionnees,
criterieTri: criterieTri ?? this.criterieTri,
triCroissant: triCroissant ?? this.triCroissant,
filtres: filtres ?? this.filtres,
isRefreshing: isRefreshing ?? this.isRefreshing,
isLoadingMore: isLoadingMore ?? this.isLoadingMore,
lastUpdated: lastUpdated ?? this.lastUpdated,
);
}
/// Obtient le nombre d'évaluations sélectionnées
int get nombreEvaluationsSelectionnees {
return evaluationsSelectionnees.values.where((selected) => selected).length;
}
/// Vérifie si toutes les évaluations sont sélectionnées
bool get toutesEvaluationsSelectionnees {
if (evaluationsFiltrees.isEmpty) return false;
return evaluationsFiltrees.every((evaluation) =>
evaluationsSelectionnees[evaluation.id] == true
);
}
/// Obtient les IDs des évaluations sélectionnées
List<String> get evaluationsSelectionneesIds {
return evaluationsSelectionnees.entries
.where((entry) => entry.value)
.map((entry) => entry.key)
.toList();
}
/// Obtient les évaluations sélectionnées
List<EvaluationAide> get evaluationsSelectionneesEntities {
return evaluations.where((evaluation) =>
evaluationsSelectionnees[evaluation.id] == true
).toList();
}
/// Vérifie si des données sont disponibles
bool get hasData => evaluations.isNotEmpty;
/// Vérifie si des filtres sont appliqués
bool get hasFiltres => !filtres.isEmpty;
/// Obtient le texte de statut
String get statusText {
if (isRefreshing) return 'Actualisation...';
if (isLoadingMore) return 'Chargement...';
if (evaluationsFiltrees.isEmpty && hasData) return 'Aucun résultat pour les filtres appliqués';
if (evaluationsFiltrees.isEmpty) return 'Aucune évaluation';
return '${evaluationsFiltrees.length} évaluation${evaluationsFiltrees.length > 1 ? 's' : ''}';
}
/// Obtient la note moyenne
double get noteMoyenne {
if (evaluationsFiltrees.isEmpty) return 0.0;
final notesValides = evaluationsFiltrees
.where((e) => e.noteGlobale != null)
.map((e) => e.noteGlobale!)
.toList();
if (notesValides.isEmpty) return 0.0;
return notesValides.reduce((a, b) => a + b) / notesValides.length;
}
/// Obtient le nombre d'évaluations par décision
Map<StatutAide, int> get repartitionDecisions {
final repartition = <StatutAide, int>{};
for (final evaluation in evaluationsFiltrees) {
repartition[evaluation.decision] = (repartition[evaluation.decision] ?? 0) + 1;
}
return repartition;
}
}
/// État d'erreur
class EvaluationsError extends EvaluationsState {
final String message;
final String? code;
final bool isNetworkError;
final bool canRetry;
final List<EvaluationAide>? cachedData;
const EvaluationsError({
required this.message,
this.code,
this.isNetworkError = false,
this.canRetry = true,
this.cachedData,
});
@override
List<Object?> get props => [
message,
code,
isNetworkError,
canRetry,
cachedData,
];
/// Vérifie si des données en cache sont disponibles
bool get hasCachedData => cachedData != null && cachedData!.isNotEmpty;
}
/// État de succès pour une opération spécifique
class EvaluationsOperationSuccess extends EvaluationsState {
final String message;
final EvaluationAide? evaluation;
final TypeOperationEvaluation operation;
const EvaluationsOperationSuccess({
required this.message,
this.evaluation,
required this.operation,
});
@override
List<Object?> get props => [message, evaluation, operation];
}
/// État de validation
class EvaluationsValidation extends EvaluationsState {
final Map<String, String> erreurs;
final bool isValid;
final EvaluationAide? evaluation;
const EvaluationsValidation({
required this.erreurs,
required this.isValid,
this.evaluation,
});
@override
List<Object?> get props => [erreurs, isValid, evaluation];
/// Obtient la première erreur
String? get premiereErreur {
return erreurs.values.isNotEmpty ? erreurs.values.first : null;
}
/// Obtient les erreurs pour un champ spécifique
String? getErreurPourChamp(String champ) {
return erreurs[champ];
}
}
/// État de calcul de note globale
class EvaluationsNoteCalculee extends EvaluationsState {
final double noteGlobale;
final Map<String, double> criteres;
const EvaluationsNoteCalculee({
required this.noteGlobale,
required this.criteres,
});
@override
List<Object> get props => [noteGlobale, criteres];
}
/// État des statistiques d'évaluation
class EvaluationsStatistiques extends EvaluationsState {
final Map<String, dynamic> statistiques;
final DateTime? dateDebut;
final DateTime? dateFin;
const EvaluationsStatistiques({
required this.statistiques,
this.dateDebut,
this.dateFin,
});
@override
List<Object?> get props => [statistiques, dateDebut, dateFin];
}
/// État d'export
class EvaluationsExporting extends EvaluationsState {
final double progress;
final String? currentStep;
const EvaluationsExporting({
required this.progress,
this.currentStep,
});
@override
List<Object?> get props => [progress, currentStep];
}
/// État d'export terminé
class EvaluationsExported extends EvaluationsState {
final String filePath;
final FormatExport format;
final int nombreEvaluations;
const EvaluationsExported({
required this.filePath,
required this.format,
required this.nombreEvaluations,
});
@override
List<Object> get props => [filePath, format, nombreEvaluations];
}
/// Classe pour les filtres des évaluations
class FiltresEvaluations extends Equatable {
final TypeEvaluateur? typeEvaluateur;
final StatutAide? decision;
final double? noteMin;
final double? noteMax;
final String? motCle;
final String? evaluateurId;
final String? demandeId;
final DateTime? dateDebutEvaluation;
final DateTime? dateFinEvaluation;
const FiltresEvaluations({
this.typeEvaluateur,
this.decision,
this.noteMin,
this.noteMax,
this.motCle,
this.evaluateurId,
this.demandeId,
this.dateDebutEvaluation,
this.dateFinEvaluation,
});
@override
List<Object?> get props => [
typeEvaluateur,
decision,
noteMin,
noteMax,
motCle,
evaluateurId,
demandeId,
dateDebutEvaluation,
dateFinEvaluation,
];
/// Copie les filtres avec de nouvelles valeurs
FiltresEvaluations copyWith({
TypeEvaluateur? typeEvaluateur,
StatutAide? decision,
double? noteMin,
double? noteMax,
String? motCle,
String? evaluateurId,
String? demandeId,
DateTime? dateDebutEvaluation,
DateTime? dateFinEvaluation,
}) {
return FiltresEvaluations(
typeEvaluateur: typeEvaluateur ?? this.typeEvaluateur,
decision: decision ?? this.decision,
noteMin: noteMin ?? this.noteMin,
noteMax: noteMax ?? this.noteMax,
motCle: motCle ?? this.motCle,
evaluateurId: evaluateurId ?? this.evaluateurId,
demandeId: demandeId ?? this.demandeId,
dateDebutEvaluation: dateDebutEvaluation ?? this.dateDebutEvaluation,
dateFinEvaluation: dateFinEvaluation ?? this.dateFinEvaluation,
);
}
/// Réinitialise tous les filtres
FiltresEvaluations clear() {
return const FiltresEvaluations();
}
/// Vérifie si les filtres sont vides
bool get isEmpty {
return typeEvaluateur == null &&
decision == null &&
noteMin == null &&
noteMax == null &&
(motCle == null || motCle!.isEmpty) &&
evaluateurId == null &&
demandeId == null &&
dateDebutEvaluation == null &&
dateFinEvaluation == null;
}
/// Obtient le nombre de filtres actifs
int get nombreFiltresActifs {
int count = 0;
if (typeEvaluateur != null) count++;
if (decision != null) count++;
if (noteMin != null) count++;
if (noteMax != null) count++;
if (motCle != null && motCle!.isNotEmpty) count++;
if (evaluateurId != null) count++;
if (demandeId != null) count++;
if (dateDebutEvaluation != null) count++;
if (dateFinEvaluation != null) count++;
return count;
}
/// Obtient une description textuelle des filtres
String get description {
final parts = <String>[];
if (typeEvaluateur != null) parts.add('Type: ${typeEvaluateur!.libelle}');
if (decision != null) parts.add('Décision: ${decision!.libelle}');
if (motCle != null && motCle!.isNotEmpty) parts.add('Recherche: "$motCle"');
if (noteMin != null || noteMax != null) {
if (noteMin != null && noteMax != null) {
parts.add('Note: ${noteMin!.toStringAsFixed(1)} - ${noteMax!.toStringAsFixed(1)}');
} else if (noteMin != null) {
parts.add('Note min: ${noteMin!.toStringAsFixed(1)}');
} else {
parts.add('Note max: ${noteMax!.toStringAsFixed(1)}');
}
}
return parts.join(', ');
}
}
/// Énumération pour les types d'opération
enum TypeOperationEvaluation {
creation,
modification,
soumission,
approbation,
rejet,
suppression,
export,
signalement,
}
/// Extension pour obtenir le libellé des opérations
extension TypeOperationEvaluationExtension on TypeOperationEvaluation {
String get libelle {
switch (this) {
case TypeOperationEvaluation.creation:
return 'Création';
case TypeOperationEvaluation.modification:
return 'Modification';
case TypeOperationEvaluation.soumission:
return 'Soumission';
case TypeOperationEvaluation.approbation:
return 'Approbation';
case TypeOperationEvaluation.rejet:
return 'Rejet';
case TypeOperationEvaluation.suppression:
return 'Suppression';
case TypeOperationEvaluation.export:
return 'Export';
case TypeOperationEvaluation.signalement:
return 'Signalement';
}
}
String get messageSucces {
switch (this) {
case TypeOperationEvaluation.creation:
return 'Évaluation créée avec succès';
case TypeOperationEvaluation.modification:
return 'Évaluation modifiée avec succès';
case TypeOperationEvaluation.soumission:
return 'Évaluation soumise avec succès';
case TypeOperationEvaluation.approbation:
return 'Évaluation approuvée avec succès';
case TypeOperationEvaluation.rejet:
return 'Évaluation rejetée avec succès';
case TypeOperationEvaluation.suppression:
return 'Évaluation supprimée avec succès';
case TypeOperationEvaluation.export:
return 'Export réalisé avec succès';
case TypeOperationEvaluation.signalement:
return 'Évaluation signalée avec succès';
}
}
}

View File

@@ -0,0 +1,382 @@
import 'package:equatable/equatable.dart';
import '../../../domain/entities/proposition_aide.dart';
/// Événements pour la gestion des propositions d'aide
///
/// Ces événements représentent toutes les actions possibles
/// que l'utilisateur peut effectuer sur les propositions d'aide.
abstract class PropositionsAideEvent extends Equatable {
const PropositionsAideEvent();
@override
List<Object?> get props => [];
}
/// Événement pour charger les propositions d'aide
class ChargerPropositionsAideEvent extends PropositionsAideEvent {
final String? organisationId;
final TypeAide? typeAide;
final StatutProposition? statut;
final String? proposantId;
final bool? disponible;
final bool forceRefresh;
const ChargerPropositionsAideEvent({
this.organisationId,
this.typeAide,
this.statut,
this.proposantId,
this.disponible,
this.forceRefresh = false,
});
@override
List<Object?> get props => [
organisationId,
typeAide,
statut,
proposantId,
disponible,
forceRefresh,
];
}
/// Événement pour charger plus de propositions (pagination)
class ChargerPlusPropositionsAideEvent extends PropositionsAideEvent {
const ChargerPlusPropositionsAideEvent();
}
/// Événement pour créer une nouvelle proposition d'aide
class CreerPropositionAideEvent extends PropositionsAideEvent {
final PropositionAide proposition;
const CreerPropositionAideEvent({required this.proposition});
@override
List<Object> get props => [proposition];
}
/// Événement pour mettre à jour une proposition d'aide
class MettreAJourPropositionAideEvent extends PropositionsAideEvent {
final PropositionAide proposition;
const MettreAJourPropositionAideEvent({required this.proposition});
@override
List<Object> get props => [proposition];
}
/// Événement pour obtenir une proposition d'aide spécifique
class ObtenirPropositionAideEvent extends PropositionsAideEvent {
final String propositionId;
const ObtenirPropositionAideEvent({required this.propositionId});
@override
List<Object> get props => [propositionId];
}
/// Événement pour activer/désactiver une proposition
class ToggleDisponibilitePropositionEvent extends PropositionsAideEvent {
final String propositionId;
final bool disponible;
const ToggleDisponibilitePropositionEvent({
required this.propositionId,
required this.disponible,
});
@override
List<Object> get props => [propositionId, disponible];
}
/// Événement pour rechercher des propositions d'aide
class RechercherPropositionsAideEvent extends PropositionsAideEvent {
final String? organisationId;
final TypeAide? typeAide;
final StatutProposition? statut;
final String? proposantId;
final bool? disponible;
final String? motCle;
final int page;
final int taille;
const RechercherPropositionsAideEvent({
this.organisationId,
this.typeAide,
this.statut,
this.proposantId,
this.disponible,
this.motCle,
this.page = 0,
this.taille = 20,
});
@override
List<Object?> get props => [
organisationId,
typeAide,
statut,
proposantId,
disponible,
motCle,
page,
taille,
];
}
/// Événement pour charger mes propositions
class ChargerMesPropositionsEvent extends PropositionsAideEvent {
final String utilisateurId;
const ChargerMesPropositionsEvent({required this.utilisateurId});
@override
List<Object> get props => [utilisateurId];
}
/// Événement pour charger les propositions disponibles
class ChargerPropositionsDisponiblesEvent extends PropositionsAideEvent {
final String organisationId;
final TypeAide? typeAide;
const ChargerPropositionsDisponiblesEvent({
required this.organisationId,
this.typeAide,
});
@override
List<Object?> get props => [organisationId, typeAide];
}
/// Événement pour filtrer les propositions localement
class FiltrerPropositionsAideEvent extends PropositionsAideEvent {
final TypeAide? typeAide;
final StatutProposition? statut;
final bool? disponible;
final String? motCle;
final double? capaciteMin;
final double? capaciteMax;
const FiltrerPropositionsAideEvent({
this.typeAide,
this.statut,
this.disponible,
this.motCle,
this.capaciteMin,
this.capaciteMax,
});
@override
List<Object?> get props => [
typeAide,
statut,
disponible,
motCle,
capaciteMin,
capaciteMax,
];
}
/// Événement pour trier les propositions
class TrierPropositionsAideEvent extends PropositionsAideEvent {
final TriPropositions critere;
final bool croissant;
const TrierPropositionsAideEvent({
required this.critere,
this.croissant = true,
});
@override
List<Object> get props => [critere, croissant];
}
/// Événement pour rafraîchir les propositions
class RafraichirPropositionsAideEvent extends PropositionsAideEvent {
const RafraichirPropositionsAideEvent();
}
/// Événement pour réinitialiser l'état
class ReinitialiserPropositionsAideEvent extends PropositionsAideEvent {
const ReinitialiserPropositionsAideEvent();
}
/// Événement pour sélectionner/désélectionner une proposition
class SelectionnerPropositionAideEvent extends PropositionsAideEvent {
final String propositionId;
final bool selectionne;
const SelectionnerPropositionAideEvent({
required this.propositionId,
required this.selectionne,
});
@override
List<Object> get props => [propositionId, selectionne];
}
/// Événement pour sélectionner/désélectionner toutes les propositions
class SelectionnerToutesPropositionsAideEvent extends PropositionsAideEvent {
final bool selectionne;
const SelectionnerToutesPropositionsAideEvent({required this.selectionne});
@override
List<Object> get props => [selectionne];
}
/// Événement pour supprimer des propositions sélectionnées
class SupprimerPropositionsSelectionnees extends PropositionsAideEvent {
final List<String> propositionIds;
const SupprimerPropositionsSelectionnees({required this.propositionIds});
@override
List<Object> get props => [propositionIds];
}
/// Événement pour exporter des propositions
class ExporterPropositionsAideEvent extends PropositionsAideEvent {
final List<String> propositionIds;
final FormatExport format;
const ExporterPropositionsAideEvent({
required this.propositionIds,
required this.format,
});
@override
List<Object> get props => [propositionIds, format];
}
/// Événement pour calculer la compatibilité avec une demande
class CalculerCompatibiliteEvent extends PropositionsAideEvent {
final String propositionId;
final String demandeId;
const CalculerCompatibiliteEvent({
required this.propositionId,
required this.demandeId,
});
@override
List<Object> get props => [propositionId, demandeId];
}
/// Événement pour obtenir les statistiques d'une proposition
class ObtenirStatistiquesPropositionEvent extends PropositionsAideEvent {
final String propositionId;
const ObtenirStatistiquesPropositionEvent({required this.propositionId});
@override
List<Object> get props => [propositionId];
}
/// Énumération pour les critères de tri
enum TriPropositions {
dateCreation,
dateModification,
titre,
statut,
capacite,
proposant,
scoreCompatibilite,
nombreMatches,
}
/// Énumération pour les formats d'export
enum FormatExport {
pdf,
excel,
csv,
json,
}
/// Extension pour obtenir le libellé des critères de tri
extension TriPropositionsExtension on TriPropositions {
String get libelle {
switch (this) {
case TriPropositions.dateCreation:
return 'Date de création';
case TriPropositions.dateModification:
return 'Date de modification';
case TriPropositions.titre:
return 'Titre';
case TriPropositions.statut:
return 'Statut';
case TriPropositions.capacite:
return 'Capacité';
case TriPropositions.proposant:
return 'Proposant';
case TriPropositions.scoreCompatibilite:
return 'Score de compatibilité';
case TriPropositions.nombreMatches:
return 'Nombre de matches';
}
}
String get icone {
switch (this) {
case TriPropositions.dateCreation:
return 'calendar_today';
case TriPropositions.dateModification:
return 'update';
case TriPropositions.titre:
return 'title';
case TriPropositions.statut:
return 'flag';
case TriPropositions.capacite:
return 'trending_up';
case TriPropositions.proposant:
return 'person';
case TriPropositions.scoreCompatibilite:
return 'star';
case TriPropositions.nombreMatches:
return 'link';
}
}
}
/// Extension pour obtenir le libellé des formats d'export
extension FormatExportExtension on FormatExport {
String get libelle {
switch (this) {
case FormatExport.pdf:
return 'PDF';
case FormatExport.excel:
return 'Excel';
case FormatExport.csv:
return 'CSV';
case FormatExport.json:
return 'JSON';
}
}
String get extension {
switch (this) {
case FormatExport.pdf:
return '.pdf';
case FormatExport.excel:
return '.xlsx';
case FormatExport.csv:
return '.csv';
case FormatExport.json:
return '.json';
}
}
String get mimeType {
switch (this) {
case FormatExport.pdf:
return 'application/pdf';
case FormatExport.excel:
return 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
case FormatExport.csv:
return 'text/csv';
case FormatExport.json:
return 'application/json';
}
}
}

View File

@@ -0,0 +1,445 @@
import 'package:equatable/equatable.dart';
import '../../../domain/entities/proposition_aide.dart';
import 'propositions_aide_event.dart';
/// États pour la gestion des propositions d'aide
///
/// Ces états représentent tous les états possibles
/// de l'interface utilisateur pour les propositions d'aide.
abstract class PropositionsAideState extends Equatable {
const PropositionsAideState();
@override
List<Object?> get props => [];
}
/// État initial
class PropositionsAideInitial extends PropositionsAideState {
const PropositionsAideInitial();
}
/// État de chargement
class PropositionsAideLoading extends PropositionsAideState {
final bool isRefreshing;
final bool isLoadingMore;
const PropositionsAideLoading({
this.isRefreshing = false,
this.isLoadingMore = false,
});
@override
List<Object> get props => [isRefreshing, isLoadingMore];
}
/// État de succès avec données chargées
class PropositionsAideLoaded extends PropositionsAideState {
final List<PropositionAide> propositions;
final List<PropositionAide> propositionsFiltrees;
final bool hasReachedMax;
final int currentPage;
final int totalElements;
final Map<String, bool> propositionsSelectionnees;
final TriPropositions? criterieTri;
final bool triCroissant;
final FiltresPropositionsAide filtres;
final bool isRefreshing;
final bool isLoadingMore;
final DateTime lastUpdated;
const PropositionsAideLoaded({
required this.propositions,
required this.propositionsFiltrees,
this.hasReachedMax = false,
this.currentPage = 0,
this.totalElements = 0,
this.propositionsSelectionnees = const {},
this.criterieTri,
this.triCroissant = true,
this.filtres = const FiltresPropositionsAide(),
this.isRefreshing = false,
this.isLoadingMore = false,
required this.lastUpdated,
});
@override
List<Object?> get props => [
propositions,
propositionsFiltrees,
hasReachedMax,
currentPage,
totalElements,
propositionsSelectionnees,
criterieTri,
triCroissant,
filtres,
isRefreshing,
isLoadingMore,
lastUpdated,
];
/// Copie l'état avec de nouvelles valeurs
PropositionsAideLoaded copyWith({
List<PropositionAide>? propositions,
List<PropositionAide>? propositionsFiltrees,
bool? hasReachedMax,
int? currentPage,
int? totalElements,
Map<String, bool>? propositionsSelectionnees,
TriPropositions? criterieTri,
bool? triCroissant,
FiltresPropositionsAide? filtres,
bool? isRefreshing,
bool? isLoadingMore,
DateTime? lastUpdated,
}) {
return PropositionsAideLoaded(
propositions: propositions ?? this.propositions,
propositionsFiltrees: propositionsFiltrees ?? this.propositionsFiltrees,
hasReachedMax: hasReachedMax ?? this.hasReachedMax,
currentPage: currentPage ?? this.currentPage,
totalElements: totalElements ?? this.totalElements,
propositionsSelectionnees: propositionsSelectionnees ?? this.propositionsSelectionnees,
criterieTri: criterieTri ?? this.criterieTri,
triCroissant: triCroissant ?? this.triCroissant,
filtres: filtres ?? this.filtres,
isRefreshing: isRefreshing ?? this.isRefreshing,
isLoadingMore: isLoadingMore ?? this.isLoadingMore,
lastUpdated: lastUpdated ?? this.lastUpdated,
);
}
/// Obtient le nombre de propositions sélectionnées
int get nombrePropositionsSelectionnees {
return propositionsSelectionnees.values.where((selected) => selected).length;
}
/// Vérifie si toutes les propositions sont sélectionnées
bool get toutesPropositionsSelectionnees {
if (propositionsFiltrees.isEmpty) return false;
return propositionsFiltrees.every((proposition) =>
propositionsSelectionnees[proposition.id] == true
);
}
/// Obtient les IDs des propositions sélectionnées
List<String> get propositionsSelectionneesIds {
return propositionsSelectionnees.entries
.where((entry) => entry.value)
.map((entry) => entry.key)
.toList();
}
/// Obtient les propositions sélectionnées
List<PropositionAide> get propositionsSelectionneesEntities {
return propositions.where((proposition) =>
propositionsSelectionnees[proposition.id] == true
).toList();
}
/// Vérifie si des données sont disponibles
bool get hasData => propositions.isNotEmpty;
/// Vérifie si des filtres sont appliqués
bool get hasFiltres => !filtres.isEmpty;
/// Obtient le texte de statut
String get statusText {
if (isRefreshing) return 'Actualisation...';
if (isLoadingMore) return 'Chargement...';
if (propositionsFiltrees.isEmpty && hasData) return 'Aucun résultat pour les filtres appliqués';
if (propositionsFiltrees.isEmpty) return 'Aucune proposition d\'aide';
return '${propositionsFiltrees.length} proposition${propositionsFiltrees.length > 1 ? 's' : ''}';
}
/// Obtient le nombre de propositions disponibles
int get nombrePropositionsDisponibles {
return propositionsFiltrees.where((p) => p.estDisponible).length;
}
/// Obtient la capacité totale disponible
double get capaciteTotaleDisponible {
return propositionsFiltrees
.where((p) => p.estDisponible)
.fold(0.0, (sum, p) => sum + (p.capaciteMaximale ?? 0.0));
}
}
/// État d'erreur
class PropositionsAideError extends PropositionsAideState {
final String message;
final String? code;
final bool isNetworkError;
final bool canRetry;
final List<PropositionAide>? cachedData;
const PropositionsAideError({
required this.message,
this.code,
this.isNetworkError = false,
this.canRetry = true,
this.cachedData,
});
@override
List<Object?> get props => [
message,
code,
isNetworkError,
canRetry,
cachedData,
];
/// Vérifie si des données en cache sont disponibles
bool get hasCachedData => cachedData != null && cachedData!.isNotEmpty;
}
/// État de succès pour une opération spécifique
class PropositionsAideOperationSuccess extends PropositionsAideState {
final String message;
final PropositionAide? proposition;
final TypeOperationProposition operation;
const PropositionsAideOperationSuccess({
required this.message,
this.proposition,
required this.operation,
});
@override
List<Object?> get props => [message, proposition, operation];
}
/// État de compatibilité calculée
class PropositionsAideCompatibilite extends PropositionsAideState {
final String propositionId;
final String demandeId;
final double scoreCompatibilite;
final Map<String, dynamic> detailsCompatibilite;
const PropositionsAideCompatibilite({
required this.propositionId,
required this.demandeId,
required this.scoreCompatibilite,
required this.detailsCompatibilite,
});
@override
List<Object> get props => [propositionId, demandeId, scoreCompatibilite, detailsCompatibilite];
}
/// État des statistiques d'une proposition
class PropositionsAideStatistiques extends PropositionsAideState {
final String propositionId;
final Map<String, dynamic> statistiques;
const PropositionsAideStatistiques({
required this.propositionId,
required this.statistiques,
});
@override
List<Object> get props => [propositionId, statistiques];
}
/// État d'export
class PropositionsAideExporting extends PropositionsAideState {
final double progress;
final String? currentStep;
const PropositionsAideExporting({
required this.progress,
this.currentStep,
});
@override
List<Object?> get props => [progress, currentStep];
}
/// État d'export terminé
class PropositionsAideExported extends PropositionsAideState {
final String filePath;
final FormatExport format;
final int nombrePropositions;
const PropositionsAideExported({
required this.filePath,
required this.format,
required this.nombrePropositions,
});
@override
List<Object> get props => [filePath, format, nombrePropositions];
}
/// Classe pour les filtres des propositions d'aide
class FiltresPropositionsAide extends Equatable {
final TypeAide? typeAide;
final StatutProposition? statut;
final bool? disponible;
final String? motCle;
final String? organisationId;
final String? proposantId;
final DateTime? dateDebutCreation;
final DateTime? dateFinCreation;
final double? capaciteMin;
final double? capaciteMax;
const FiltresPropositionsAide({
this.typeAide,
this.statut,
this.disponible,
this.motCle,
this.organisationId,
this.proposantId,
this.dateDebutCreation,
this.dateFinCreation,
this.capaciteMin,
this.capaciteMax,
});
@override
List<Object?> get props => [
typeAide,
statut,
disponible,
motCle,
organisationId,
proposantId,
dateDebutCreation,
dateFinCreation,
capaciteMin,
capaciteMax,
];
/// Copie les filtres avec de nouvelles valeurs
FiltresPropositionsAide copyWith({
TypeAide? typeAide,
StatutProposition? statut,
bool? disponible,
String? motCle,
String? organisationId,
String? proposantId,
DateTime? dateDebutCreation,
DateTime? dateFinCreation,
double? capaciteMin,
double? capaciteMax,
}) {
return FiltresPropositionsAide(
typeAide: typeAide ?? this.typeAide,
statut: statut ?? this.statut,
disponible: disponible ?? this.disponible,
motCle: motCle ?? this.motCle,
organisationId: organisationId ?? this.organisationId,
proposantId: proposantId ?? this.proposantId,
dateDebutCreation: dateDebutCreation ?? this.dateDebutCreation,
dateFinCreation: dateFinCreation ?? this.dateFinCreation,
capaciteMin: capaciteMin ?? this.capaciteMin,
capaciteMax: capaciteMax ?? this.capaciteMax,
);
}
/// Réinitialise tous les filtres
FiltresPropositionsAide clear() {
return const FiltresPropositionsAide();
}
/// Vérifie si les filtres sont vides
bool get isEmpty {
return typeAide == null &&
statut == null &&
disponible == null &&
(motCle == null || motCle!.isEmpty) &&
organisationId == null &&
proposantId == null &&
dateDebutCreation == null &&
dateFinCreation == null &&
capaciteMin == null &&
capaciteMax == null;
}
/// Obtient le nombre de filtres actifs
int get nombreFiltresActifs {
int count = 0;
if (typeAide != null) count++;
if (statut != null) count++;
if (disponible != null) count++;
if (motCle != null && motCle!.isNotEmpty) count++;
if (organisationId != null) count++;
if (proposantId != null) count++;
if (dateDebutCreation != null) count++;
if (dateFinCreation != null) count++;
if (capaciteMin != null) count++;
if (capaciteMax != null) count++;
return count;
}
/// Obtient une description textuelle des filtres
String get description {
final parts = <String>[];
if (typeAide != null) parts.add('Type: ${typeAide!.libelle}');
if (statut != null) parts.add('Statut: ${statut!.libelle}');
if (disponible == true) parts.add('Disponible uniquement');
if (disponible == false) parts.add('Non disponible uniquement');
if (motCle != null && motCle!.isNotEmpty) parts.add('Recherche: "$motCle"');
if (capaciteMin != null || capaciteMax != null) {
if (capaciteMin != null && capaciteMax != null) {
parts.add('Capacité: ${capaciteMin!.toInt()} - ${capaciteMax!.toInt()}');
} else if (capaciteMin != null) {
parts.add('Capacité min: ${capaciteMin!.toInt()}');
} else {
parts.add('Capacité max: ${capaciteMax!.toInt()}');
}
}
return parts.join(', ');
}
}
/// Énumération pour les types d'opération
enum TypeOperationProposition {
creation,
modification,
activation,
desactivation,
suppression,
export,
}
/// Extension pour obtenir le libellé des opérations
extension TypeOperationPropositionExtension on TypeOperationProposition {
String get libelle {
switch (this) {
case TypeOperationProposition.creation:
return 'Création';
case TypeOperationProposition.modification:
return 'Modification';
case TypeOperationProposition.activation:
return 'Activation';
case TypeOperationProposition.desactivation:
return 'Désactivation';
case TypeOperationProposition.suppression:
return 'Suppression';
case TypeOperationProposition.export:
return 'Export';
}
}
String get messageSucces {
switch (this) {
case TypeOperationProposition.creation:
return 'Proposition d\'aide créée avec succès';
case TypeOperationProposition.modification:
return 'Proposition d\'aide modifiée avec succès';
case TypeOperationProposition.activation:
return 'Proposition d\'aide activée avec succès';
case TypeOperationProposition.desactivation:
return 'Proposition d\'aide désactivée avec succès';
case TypeOperationProposition.suppression:
return 'Proposition d\'aide supprimée avec succès';
case TypeOperationProposition.export:
return 'Export réalisé avec succès';
}
}
}