Refactoring - Version OK

This commit is contained in:
dahoud
2025-11-17 16:02:04 +00:00
parent 3f00a26308
commit 3b9ffac8cd
198 changed files with 18010 additions and 11383 deletions

View File

@@ -0,0 +1,488 @@
/// BLoC pour la gestion des organisations
library organizations_bloc;
import 'package:flutter_bloc/flutter_bloc.dart';
import '../data/models/organization_model.dart';
import '../data/services/organization_service.dart';
import 'organizations_event.dart';
import 'organizations_state.dart';
/// BLoC principal pour la gestion des organisations
class OrganizationsBloc extends Bloc<OrganizationsEvent, OrganizationsState> {
final OrganizationService _organizationService;
OrganizationsBloc(this._organizationService) : super(const OrganizationsInitial()) {
// Enregistrement des handlers d'événements
on<LoadOrganizations>(_onLoadOrganizations);
on<LoadMoreOrganizations>(_onLoadMoreOrganizations);
on<SearchOrganizations>(_onSearchOrganizations);
on<AdvancedSearchOrganizations>(_onAdvancedSearchOrganizations);
on<LoadOrganizationById>(_onLoadOrganizationById);
on<CreateOrganization>(_onCreateOrganization);
on<UpdateOrganization>(_onUpdateOrganization);
on<DeleteOrganization>(_onDeleteOrganization);
on<ActivateOrganization>(_onActivateOrganization);
on<FilterOrganizationsByStatus>(_onFilterOrganizationsByStatus);
on<FilterOrganizationsByType>(_onFilterOrganizationsByType);
on<SortOrganizations>(_onSortOrganizations);
on<LoadOrganizationsStats>(_onLoadOrganizationsStats);
on<ClearOrganizationsFilters>(_onClearOrganizationsFilters);
on<RefreshOrganizations>(_onRefreshOrganizations);
on<ResetOrganizationsState>(_onResetOrganizationsState);
}
/// Charge la liste des organisations
Future<void> _onLoadOrganizations(
LoadOrganizations event,
Emitter<OrganizationsState> emit,
) async {
try {
if (event.refresh || state is! OrganizationsLoaded) {
emit(const OrganizationsLoading());
}
final organizations = await _organizationService.getOrganizations(
page: event.page,
size: event.size,
recherche: event.recherche,
);
emit(OrganizationsLoaded(
organizations: organizations,
filteredOrganizations: organizations,
hasReachedMax: organizations.length < event.size,
currentPage: event.page,
currentSearch: event.recherche,
));
} catch (e) {
emit(OrganizationsError(
'Erreur lors du chargement des organisations',
details: e.toString(),
));
}
}
/// Charge plus d'organisations (pagination)
Future<void> _onLoadMoreOrganizations(
LoadMoreOrganizations event,
Emitter<OrganizationsState> emit,
) async {
final currentState = state;
if (currentState is! OrganizationsLoaded || currentState.hasReachedMax) {
return;
}
emit(OrganizationsLoadingMore(currentState.organizations));
try {
final nextPage = currentState.currentPage + 1;
final newOrganizations = await _organizationService.getOrganizations(
page: nextPage,
size: 20,
recherche: currentState.currentSearch,
);
final allOrganizations = [...currentState.organizations, ...newOrganizations];
final filteredOrganizations = _applyCurrentFilters(allOrganizations, currentState);
emit(currentState.copyWith(
organizations: allOrganizations,
filteredOrganizations: filteredOrganizations,
hasReachedMax: newOrganizations.length < 20,
currentPage: nextPage,
));
} catch (e) {
emit(OrganizationsError(
'Erreur lors du chargement de plus d\'organisations',
details: e.toString(),
previousOrganizations: currentState.organizations,
));
}
}
/// Recherche des organisations
Future<void> _onSearchOrganizations(
SearchOrganizations event,
Emitter<OrganizationsState> emit,
) async {
final currentState = state;
if (currentState is! OrganizationsLoaded) {
// Si pas encore chargé, charger avec recherche
add(LoadOrganizations(recherche: event.query, refresh: true));
return;
}
try {
if (event.query.isEmpty) {
// Recherche vide, afficher toutes les organisations
final filteredOrganizations = _applyCurrentFilters(
currentState.organizations,
currentState.copyWith(clearSearch: true),
);
emit(currentState.copyWith(
filteredOrganizations: filteredOrganizations,
clearSearch: true,
));
} else {
// Recherche locale d'abord
final localResults = _organizationService.searchLocal(
currentState.organizations,
event.query,
);
emit(currentState.copyWith(
filteredOrganizations: localResults,
currentSearch: event.query,
));
// Puis recherche serveur pour plus de résultats
final serverResults = await _organizationService.getOrganizations(
page: 0,
size: 50,
recherche: event.query,
);
final filteredResults = _applyCurrentFilters(serverResults, currentState);
emit(currentState.copyWith(
organizations: serverResults,
filteredOrganizations: filteredResults,
currentSearch: event.query,
currentPage: 0,
hasReachedMax: true,
));
}
} catch (e) {
emit(OrganizationsError(
'Erreur lors de la recherche',
details: e.toString(),
previousOrganizations: currentState.organizations,
));
}
}
/// Recherche avancée
Future<void> _onAdvancedSearchOrganizations(
AdvancedSearchOrganizations event,
Emitter<OrganizationsState> emit,
) async {
emit(const OrganizationsLoading());
try {
final organizations = await _organizationService.searchOrganizations(
nom: event.nom,
type: event.type,
statut: event.statut,
ville: event.ville,
region: event.region,
pays: event.pays,
page: event.page,
size: event.size,
);
emit(OrganizationsLoaded(
organizations: organizations,
filteredOrganizations: organizations,
hasReachedMax: organizations.length < event.size,
currentPage: event.page,
typeFilter: event.type,
statusFilter: event.statut,
));
} catch (e) {
emit(OrganizationsError(
'Erreur lors de la recherche avancée',
details: e.toString(),
));
}
}
/// Charge une organisation par ID
Future<void> _onLoadOrganizationById(
LoadOrganizationById event,
Emitter<OrganizationsState> emit,
) async {
emit(OrganizationLoading(event.id));
try {
final organization = await _organizationService.getOrganizationById(event.id);
if (organization != null) {
emit(OrganizationLoaded(organization));
} else {
emit(OrganizationError('Organisation non trouvée', organizationId: event.id));
}
} catch (e) {
emit(OrganizationError(
'Erreur lors du chargement de l\'organisation',
organizationId: event.id,
));
}
}
/// Crée une nouvelle organisation
Future<void> _onCreateOrganization(
CreateOrganization event,
Emitter<OrganizationsState> emit,
) async {
emit(const OrganizationCreating());
try {
final createdOrganization = await _organizationService.createOrganization(event.organization);
emit(OrganizationCreated(createdOrganization));
// Recharger la liste si elle était déjà chargée
if (state is OrganizationsLoaded) {
add(const RefreshOrganizations());
}
} catch (e) {
emit(OrganizationsError(
'Erreur lors de la création de l\'organisation',
details: e.toString(),
));
}
}
/// Met à jour une organisation
Future<void> _onUpdateOrganization(
UpdateOrganization event,
Emitter<OrganizationsState> emit,
) async {
emit(OrganizationUpdating(event.id));
try {
final updatedOrganization = await _organizationService.updateOrganization(
event.id,
event.organization,
);
emit(OrganizationUpdated(updatedOrganization));
// Mettre à jour la liste si elle était déjà chargée
final currentState = state;
if (currentState is OrganizationsLoaded) {
final updatedList = currentState.organizations.map((org) {
return org.id == event.id ? updatedOrganization : org;
}).toList();
final filteredList = _applyCurrentFilters(updatedList, currentState);
emit(currentState.copyWith(
organizations: updatedList,
filteredOrganizations: filteredList,
));
}
} catch (e) {
emit(OrganizationsError(
'Erreur lors de la mise à jour de l\'organisation',
details: e.toString(),
));
}
}
/// Supprime une organisation
Future<void> _onDeleteOrganization(
DeleteOrganization event,
Emitter<OrganizationsState> emit,
) async {
emit(OrganizationDeleting(event.id));
try {
await _organizationService.deleteOrganization(event.id);
emit(OrganizationDeleted(event.id));
// Retirer de la liste si elle était déjà chargée
final currentState = state;
if (currentState is OrganizationsLoaded) {
final updatedList = currentState.organizations.where((org) => org.id != event.id).toList();
final filteredList = _applyCurrentFilters(updatedList, currentState);
emit(currentState.copyWith(
organizations: updatedList,
filteredOrganizations: filteredList,
));
}
} catch (e) {
emit(OrganizationsError(
'Erreur lors de la suppression de l\'organisation',
details: e.toString(),
));
}
}
/// Active une organisation
Future<void> _onActivateOrganization(
ActivateOrganization event,
Emitter<OrganizationsState> emit,
) async {
emit(OrganizationActivating(event.id));
try {
final activatedOrganization = await _organizationService.activateOrganization(event.id);
emit(OrganizationActivated(activatedOrganization));
// Mettre à jour la liste si elle était déjà chargée
final currentState = state;
if (currentState is OrganizationsLoaded) {
final updatedList = currentState.organizations.map((org) {
return org.id == event.id ? activatedOrganization : org;
}).toList();
final filteredList = _applyCurrentFilters(updatedList, currentState);
emit(currentState.copyWith(
organizations: updatedList,
filteredOrganizations: filteredList,
));
}
} catch (e) {
emit(OrganizationsError(
'Erreur lors de l\'activation de l\'organisation',
details: e.toString(),
));
}
}
/// Filtre par statut
void _onFilterOrganizationsByStatus(
FilterOrganizationsByStatus event,
Emitter<OrganizationsState> emit,
) {
final currentState = state;
if (currentState is! OrganizationsLoaded) return;
final filteredOrganizations = _applyCurrentFilters(
currentState.organizations,
currentState.copyWith(statusFilter: event.statut),
);
emit(currentState.copyWith(
filteredOrganizations: filteredOrganizations,
statusFilter: event.statut,
));
}
/// Filtre par type
void _onFilterOrganizationsByType(
FilterOrganizationsByType event,
Emitter<OrganizationsState> emit,
) {
final currentState = state;
if (currentState is! OrganizationsLoaded) return;
final filteredOrganizations = _applyCurrentFilters(
currentState.organizations,
currentState.copyWith(typeFilter: event.type),
);
emit(currentState.copyWith(
filteredOrganizations: filteredOrganizations,
typeFilter: event.type,
));
}
/// Trie les organisations
void _onSortOrganizations(
SortOrganizations event,
Emitter<OrganizationsState> emit,
) {
final currentState = state;
if (currentState is! OrganizationsLoaded) return;
List<OrganizationModel> sortedOrganizations;
switch (event.sortType) {
case OrganizationSortType.name:
sortedOrganizations = _organizationService.sortByName(
currentState.filteredOrganizations,
ascending: event.ascending,
);
break;
case OrganizationSortType.creationDate:
sortedOrganizations = _organizationService.sortByCreationDate(
currentState.filteredOrganizations,
ascending: event.ascending,
);
break;
case OrganizationSortType.memberCount:
sortedOrganizations = _organizationService.sortByMemberCount(
currentState.filteredOrganizations,
ascending: event.ascending,
);
break;
default:
sortedOrganizations = currentState.filteredOrganizations;
}
emit(currentState.copyWith(
filteredOrganizations: sortedOrganizations,
sortType: event.sortType,
sortAscending: event.ascending,
));
}
/// Charge les statistiques
Future<void> _onLoadOrganizationsStats(
LoadOrganizationsStats event,
Emitter<OrganizationsState> emit,
) async {
emit(const OrganizationsStatsLoading());
try {
final stats = await _organizationService.getOrganizationsStats();
emit(OrganizationsStatsLoaded(stats));
} catch (e) {
emit(const OrganizationsStatsError('Erreur lors du chargement des statistiques'));
}
}
/// Efface les filtres
void _onClearOrganizationsFilters(
ClearOrganizationsFilters event,
Emitter<OrganizationsState> emit,
) {
final currentState = state;
if (currentState is! OrganizationsLoaded) return;
emit(currentState.copyWith(
filteredOrganizations: currentState.organizations,
clearSearch: true,
clearStatusFilter: true,
clearTypeFilter: true,
clearSort: true,
));
}
/// Rafraîchit les données
void _onRefreshOrganizations(
RefreshOrganizations event,
Emitter<OrganizationsState> emit,
) {
add(const LoadOrganizations(refresh: true));
}
/// Remet à zéro l'état
void _onResetOrganizationsState(
ResetOrganizationsState event,
Emitter<OrganizationsState> emit,
) {
emit(const OrganizationsInitial());
}
/// Applique les filtres actuels à une liste d'organisations
List<OrganizationModel> _applyCurrentFilters(
List<OrganizationModel> organizations,
OrganizationsLoaded state,
) {
var filtered = organizations;
// Filtre par recherche
if (state.currentSearch?.isNotEmpty == true) {
filtered = _organizationService.searchLocal(filtered, state.currentSearch!);
}
// Filtre par statut
if (state.statusFilter != null) {
filtered = _organizationService.filterByStatus(filtered, state.statusFilter!);
}
// Filtre par type
if (state.typeFilter != null) {
filtered = _organizationService.filterByType(filtered, state.typeFilter!);
}
return filtered;
}
}

View File

@@ -0,0 +1,176 @@
/// Événements pour le BLoC des organisations
library organizations_event;
import 'package:equatable/equatable.dart';
import '../data/models/organization_model.dart';
/// Classe de base pour tous les événements des organisations
abstract class OrganizationsEvent extends Equatable {
const OrganizationsEvent();
@override
List<Object?> get props => [];
}
/// Événement pour charger la liste des organisations
class LoadOrganizations extends OrganizationsEvent {
final int page;
final int size;
final String? recherche;
final bool refresh;
const LoadOrganizations({
this.page = 0,
this.size = 20,
this.recherche,
this.refresh = false,
});
@override
List<Object?> get props => [page, size, recherche, refresh];
}
/// Événement pour charger plus d'organisations (pagination)
class LoadMoreOrganizations extends OrganizationsEvent {
const LoadMoreOrganizations();
}
/// Événement pour rechercher des organisations
class SearchOrganizations extends OrganizationsEvent {
final String query;
const SearchOrganizations(this.query);
@override
List<Object?> get props => [query];
}
/// Événement pour recherche avancée
class AdvancedSearchOrganizations extends OrganizationsEvent {
final String? nom;
final TypeOrganization? type;
final StatutOrganization? statut;
final String? ville;
final String? region;
final String? pays;
final int page;
final int size;
const AdvancedSearchOrganizations({
this.nom,
this.type,
this.statut,
this.ville,
this.region,
this.pays,
this.page = 0,
this.size = 20,
});
@override
List<Object?> get props => [nom, type, statut, ville, region, pays, page, size];
}
/// Événement pour charger une organisation spécifique
class LoadOrganizationById extends OrganizationsEvent {
final String id;
const LoadOrganizationById(this.id);
@override
List<Object?> get props => [id];
}
/// Événement pour créer une nouvelle organisation
class CreateOrganization extends OrganizationsEvent {
final OrganizationModel organization;
const CreateOrganization(this.organization);
@override
List<Object?> get props => [organization];
}
/// Événement pour mettre à jour une organisation
class UpdateOrganization extends OrganizationsEvent {
final String id;
final OrganizationModel organization;
const UpdateOrganization(this.id, this.organization);
@override
List<Object?> get props => [id, organization];
}
/// Événement pour supprimer une organisation
class DeleteOrganization extends OrganizationsEvent {
final String id;
const DeleteOrganization(this.id);
@override
List<Object?> get props => [id];
}
/// Événement pour activer une organisation
class ActivateOrganization extends OrganizationsEvent {
final String id;
const ActivateOrganization(this.id);
@override
List<Object?> get props => [id];
}
/// Événement pour filtrer les organisations par statut
class FilterOrganizationsByStatus extends OrganizationsEvent {
final StatutOrganization? statut;
const FilterOrganizationsByStatus(this.statut);
@override
List<Object?> get props => [statut];
}
/// Événement pour filtrer les organisations par type
class FilterOrganizationsByType extends OrganizationsEvent {
final TypeOrganization? type;
const FilterOrganizationsByType(this.type);
@override
List<Object?> get props => [type];
}
/// Événement pour trier les organisations
class SortOrganizations extends OrganizationsEvent {
final OrganizationSortType sortType;
final bool ascending;
const SortOrganizations(this.sortType, {this.ascending = true});
@override
List<Object?> get props => [sortType, ascending];
}
/// Événement pour charger les statistiques des organisations
class LoadOrganizationsStats extends OrganizationsEvent {
const LoadOrganizationsStats();
}
/// Événement pour effacer les filtres
class ClearOrganizationsFilters extends OrganizationsEvent {
const ClearOrganizationsFilters();
}
/// Événement pour rafraîchir les données
class RefreshOrganizations extends OrganizationsEvent {
const RefreshOrganizations();
}
/// Événement pour réinitialiser l'état
class ResetOrganizationsState extends OrganizationsEvent {
const ResetOrganizationsState();
}

View File

@@ -0,0 +1,281 @@
/// États pour le BLoC des organisations
library organizations_state;
import 'package:equatable/equatable.dart';
import '../data/models/organization_model.dart';
/// Classe de base pour tous les états des organisations
abstract class OrganizationsState extends Equatable {
const OrganizationsState();
@override
List<Object?> get props => [];
}
/// État initial
class OrganizationsInitial extends OrganizationsState {
const OrganizationsInitial();
}
/// État de chargement
class OrganizationsLoading extends OrganizationsState {
const OrganizationsLoading();
}
/// État de chargement de plus d'éléments (pagination)
class OrganizationsLoadingMore extends OrganizationsState {
final List<OrganizationModel> currentOrganizations;
const OrganizationsLoadingMore(this.currentOrganizations);
@override
List<Object?> get props => [currentOrganizations];
}
/// État de succès avec données
class OrganizationsLoaded extends OrganizationsState {
final List<OrganizationModel> organizations;
final List<OrganizationModel> filteredOrganizations;
final bool hasReachedMax;
final int currentPage;
final String? currentSearch;
final StatutOrganization? statusFilter;
final TypeOrganization? typeFilter;
final OrganizationSortType? sortType;
final bool sortAscending;
final Map<String, dynamic>? stats;
const OrganizationsLoaded({
required this.organizations,
required this.filteredOrganizations,
this.hasReachedMax = false,
this.currentPage = 0,
this.currentSearch,
this.statusFilter,
this.typeFilter,
this.sortType,
this.sortAscending = true,
this.stats,
});
/// Copie avec modifications
OrganizationsLoaded copyWith({
List<OrganizationModel>? organizations,
List<OrganizationModel>? filteredOrganizations,
bool? hasReachedMax,
int? currentPage,
String? currentSearch,
StatutOrganization? statusFilter,
TypeOrganization? typeFilter,
OrganizationSortType? sortType,
bool? sortAscending,
Map<String, dynamic>? stats,
bool clearSearch = false,
bool clearStatusFilter = false,
bool clearTypeFilter = false,
bool clearSort = false,
}) {
return OrganizationsLoaded(
organizations: organizations ?? this.organizations,
filteredOrganizations: filteredOrganizations ?? this.filteredOrganizations,
hasReachedMax: hasReachedMax ?? this.hasReachedMax,
currentPage: currentPage ?? this.currentPage,
currentSearch: clearSearch ? null : (currentSearch ?? this.currentSearch),
statusFilter: clearStatusFilter ? null : (statusFilter ?? this.statusFilter),
typeFilter: clearTypeFilter ? null : (typeFilter ?? this.typeFilter),
sortType: clearSort ? null : (sortType ?? this.sortType),
sortAscending: sortAscending ?? this.sortAscending,
stats: stats ?? this.stats,
);
}
/// Nombre total d'organisations
int get totalCount => organizations.length;
/// Nombre d'organisations filtrées
int get filteredCount => filteredOrganizations.length;
/// Indique si des filtres sont appliqués
bool get hasFilters =>
currentSearch?.isNotEmpty == true ||
statusFilter != null ||
typeFilter != null;
/// Indique si un tri est appliqué
bool get hasSorting => sortType != null;
/// Statistiques rapides
Map<String, int> get quickStats {
final actives = organizations.where((org) => org.statut == StatutOrganization.active).length;
final inactives = organizations.length - actives;
final totalMembres = organizations.fold<int>(0, (sum, org) => sum + org.nombreMembres);
return {
'total': organizations.length,
'actives': actives,
'inactives': inactives,
'totalMembres': totalMembres,
};
}
@override
List<Object?> get props => [
organizations,
filteredOrganizations,
hasReachedMax,
currentPage,
currentSearch,
statusFilter,
typeFilter,
sortType,
sortAscending,
stats,
];
}
/// État d'erreur
class OrganizationsError extends OrganizationsState {
final String message;
final String? details;
final List<OrganizationModel>? previousOrganizations;
const OrganizationsError(
this.message, {
this.details,
this.previousOrganizations,
});
@override
List<Object?> get props => [message, details, previousOrganizations];
}
/// État de chargement d'une organisation spécifique
class OrganizationLoading extends OrganizationsState {
final String id;
const OrganizationLoading(this.id);
@override
List<Object?> get props => [id];
}
/// État d'organisation chargée
class OrganizationLoaded extends OrganizationsState {
final OrganizationModel organization;
const OrganizationLoaded(this.organization);
@override
List<Object?> get props => [organization];
}
/// État d'erreur pour une organisation spécifique
class OrganizationError extends OrganizationsState {
final String message;
final String? organizationId;
const OrganizationError(this.message, {this.organizationId});
@override
List<Object?> get props => [message, organizationId];
}
/// État de création d'organisation
class OrganizationCreating extends OrganizationsState {
const OrganizationCreating();
}
/// État de succès de création
class OrganizationCreated extends OrganizationsState {
final OrganizationModel organization;
const OrganizationCreated(this.organization);
@override
List<Object?> get props => [organization];
}
/// État de mise à jour d'organisation
class OrganizationUpdating extends OrganizationsState {
final String id;
const OrganizationUpdating(this.id);
@override
List<Object?> get props => [id];
}
/// État de succès de mise à jour
class OrganizationUpdated extends OrganizationsState {
final OrganizationModel organization;
const OrganizationUpdated(this.organization);
@override
List<Object?> get props => [organization];
}
/// État de suppression d'organisation
class OrganizationDeleting extends OrganizationsState {
final String id;
const OrganizationDeleting(this.id);
@override
List<Object?> get props => [id];
}
/// État de succès de suppression
class OrganizationDeleted extends OrganizationsState {
final String id;
const OrganizationDeleted(this.id);
@override
List<Object?> get props => [id];
}
/// État d'activation d'organisation
class OrganizationActivating extends OrganizationsState {
final String id;
const OrganizationActivating(this.id);
@override
List<Object?> get props => [id];
}
/// État de succès d'activation
class OrganizationActivated extends OrganizationsState {
final OrganizationModel organization;
const OrganizationActivated(this.organization);
@override
List<Object?> get props => [organization];
}
/// État de chargement des statistiques
class OrganizationsStatsLoading extends OrganizationsState {
const OrganizationsStatsLoading();
}
/// État des statistiques chargées
class OrganizationsStatsLoaded extends OrganizationsState {
final Map<String, dynamic> stats;
const OrganizationsStatsLoaded(this.stats);
@override
List<Object?> get props => [stats];
}
/// État d'erreur des statistiques
class OrganizationsStatsError extends OrganizationsState {
final String message;
const OrganizationsStatsError(this.message);
@override
List<Object?> get props => [message];
}