/// BLoC pour la gestion des organisations library organisations_bloc; import 'package:flutter_bloc/flutter_bloc.dart'; import '../data/models/organisation_model.dart'; import '../data/services/organisation_service.dart'; import 'organisations_event.dart'; import 'organisations_state.dart'; /// BLoC principal pour la gestion des organisations class OrganisationsBloc extends Bloc { final OrganisationService _organisationService; OrganisationsBloc(this._organisationService) : super(const OrganisationsInitial()) { // Enregistrement des handlers d'événements on(_onLoadOrganisations); on(_onLoadMoreOrganisations); on(_onSearchOrganisations); on(_onAdvancedSearchOrganisations); on(_onLoadOrganisationById); on(_onCreateOrganisation); on(_onUpdateOrganisation); on(_onDeleteOrganisation); on(_onActivateOrganisation); on(_onFilterOrganisationsByStatus); on(_onFilterOrganisationsByType); on(_onSortOrganisations); on(_onLoadOrganisationsStats); on(_onClearOrganisationsFilters); on(_onRefreshOrganisations); on(_onResetOrganisationsState); } /// Charge la liste des organisations Future _onLoadOrganisations( LoadOrganisations event, Emitter emit, ) async { try { if (event.refresh || state is! OrganisationsLoaded) { emit(const OrganisationsLoading()); } final organisations = await _organisationService.getOrganisations( page: event.page, size: event.size, recherche: event.recherche, ); emit(OrganisationsLoaded( organisations: organisations, filteredOrganisations: organisations, hasReachedMax: organisations.length < event.size, currentPage: event.page, currentSearch: event.recherche, )); } catch (e) { emit(OrganisationsError( 'Erreur lors du chargement des organisations', details: e.toString(), )); } } /// Charge plus d'organisations (pagination) Future _onLoadMoreOrganisations( LoadMoreOrganisations event, Emitter emit, ) async { final currentState = state; if (currentState is! OrganisationsLoaded || currentState.hasReachedMax) { return; } emit(OrganisationsLoadingMore(currentState.organisations)); try { final nextPage = currentState.currentPage + 1; final newOrganisations = await _organisationService.getOrganisations( page: nextPage, size: 20, recherche: currentState.currentSearch, ); final allOrganisations = [...currentState.organisations, ...newOrganisations]; final filteredOrganisations = _applyCurrentFilters(allOrganisations, currentState); emit(currentState.copyWith( organisations: allOrganisations, filteredOrganisations: filteredOrganisations, hasReachedMax: newOrganisations.length < 20, currentPage: nextPage, )); } catch (e) { emit(OrganisationsError( 'Erreur lors du chargement de plus d\'organisations', details: e.toString(), previousOrganisations: currentState.organisations, )); } } /// Recherche des organisations Future _onSearchOrganisations( SearchOrganisations event, Emitter emit, ) async { final currentState = state; if (currentState is! OrganisationsLoaded) { // Si pas encore chargé, charger avec recherche add(LoadOrganisations(recherche: event.query, refresh: true)); return; } try { if (event.query.isEmpty) { // Recherche vide, afficher toutes les organisations final filteredOrganisations = _applyCurrentFilters( currentState.organisations, currentState.copyWith(clearSearch: true), ); emit(currentState.copyWith( filteredOrganisations: filteredOrganisations, clearSearch: true, )); } else { // Recherche locale d'abord final localResults = _organisationService.searchLocal( currentState.organisations, event.query, ); emit(currentState.copyWith( filteredOrganisations: localResults, currentSearch: event.query, )); // Puis recherche serveur pour plus de résultats final serverResults = await _organisationService.getOrganisations( page: 0, size: 50, recherche: event.query, ); final filteredResults = _applyCurrentFilters(serverResults, currentState); emit(currentState.copyWith( organisations: serverResults, filteredOrganisations: filteredResults, currentSearch: event.query, currentPage: 0, hasReachedMax: true, )); } } catch (e) { emit(OrganisationsError( 'Erreur lors de la recherche', details: e.toString(), previousOrganisations: currentState.organisations, )); } } /// Recherche avancée Future _onAdvancedSearchOrganisations( AdvancedSearchOrganisations event, Emitter emit, ) async { emit(const OrganisationsLoading()); try { final organisations = await _organisationService.searchOrganisations( 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(OrganisationsLoaded( organisations: organisations, filteredOrganisations: organisations, hasReachedMax: organisations.length < event.size, currentPage: event.page, typeFilter: event.type, statusFilter: event.statut, )); } catch (e) { emit(OrganisationsError( 'Erreur lors de la recherche avancée', details: e.toString(), )); } } /// Charge une organisation par ID Future _onLoadOrganisationById( LoadOrganisationById event, Emitter emit, ) async { emit(OrganisationLoading(event.id)); try { final organisation = await _organisationService.getOrganisationById(event.id); if (organisation != null) { emit(OrganisationLoaded(organisation)); } else { emit(OrganisationError('Organisation non trouvée', organisationId: event.id)); } } catch (e) { emit(OrganisationError( 'Erreur lors du chargement de l\'organisation', organisationId: event.id, )); } } /// Crée une nouvelle organisation Future _onCreateOrganisation( CreateOrganisation event, Emitter emit, ) async { emit(const OrganisationCreating()); try { final createdOrganisation = await _organisationService.createOrganisation(event.organisation); emit(OrganisationCreated(createdOrganisation)); // Recharger la liste si elle était déjà chargée if (state is OrganisationsLoaded) { add(const RefreshOrganisations()); } } catch (e) { emit(OrganisationsError( 'Erreur lors de la création de l\'organisation', details: e.toString(), )); } } /// Met à jour une organisation Future _onUpdateOrganisation( UpdateOrganisation event, Emitter emit, ) async { emit(OrganisationUpdating(event.id)); try { final updatedOrganisation = await _organisationService.updateOrganisation( event.id, event.organisation, ); emit(OrganisationUpdated(updatedOrganisation)); // Mettre à jour la liste si elle était déjà chargée final currentState = state; if (currentState is OrganisationsLoaded) { final updatedList = currentState.organisations.map((org) { return org.id == event.id ? updatedOrganisation : org; }).toList(); final filteredList = _applyCurrentFilters(updatedList, currentState); emit(currentState.copyWith( organisations: updatedList, filteredOrganisations: filteredList, )); } } catch (e) { emit(OrganisationsError( 'Erreur lors de la mise à jour de l\'organisation', details: e.toString(), )); } } /// Supprime une organisation Future _onDeleteOrganisation( DeleteOrganisation event, Emitter emit, ) async { emit(OrganisationDeleting(event.id)); try { await _organisationService.deleteOrganisation(event.id); emit(OrganisationDeleted(event.id)); // Retirer de la liste si elle était déjà chargée final currentState = state; if (currentState is OrganisationsLoaded) { final updatedList = currentState.organisations.where((org) => org.id != event.id).toList(); final filteredList = _applyCurrentFilters(updatedList, currentState); emit(currentState.copyWith( organisations: updatedList, filteredOrganisations: filteredList, )); } } catch (e) { emit(OrganisationsError( 'Erreur lors de la suppression de l\'organisation', details: e.toString(), )); } } /// Active une organisation Future _onActivateOrganisation( ActivateOrganisation event, Emitter emit, ) async { emit(OrganisationActivating(event.id)); try { final activatedOrganisation = await _organisationService.activateOrganisation(event.id); emit(OrganisationActivated(activatedOrganisation)); // Mettre à jour la liste si elle était déjà chargée final currentState = state; if (currentState is OrganisationsLoaded) { final updatedList = currentState.organisations.map((org) { return org.id == event.id ? activatedOrganisation : org; }).toList(); final filteredList = _applyCurrentFilters(updatedList, currentState); emit(currentState.copyWith( organisations: updatedList, filteredOrganisations: filteredList, )); } } catch (e) { emit(OrganisationsError( 'Erreur lors de l\'activation de l\'organisation', details: e.toString(), )); } } /// Filtre par statut void _onFilterOrganisationsByStatus( FilterOrganisationsByStatus event, Emitter emit, ) { final currentState = state; if (currentState is! OrganisationsLoaded) return; final filteredOrganisations = _applyCurrentFilters( currentState.organisations, currentState.copyWith(statusFilter: event.statut), ); emit(currentState.copyWith( filteredOrganisations: filteredOrganisations, statusFilter: event.statut, )); } /// Filtre par type void _onFilterOrganisationsByType( FilterOrganisationsByType event, Emitter emit, ) { final currentState = state; if (currentState is! OrganisationsLoaded) return; final filteredOrganisations = _applyCurrentFilters( currentState.organisations, currentState.copyWith(typeFilter: event.type), ); emit(currentState.copyWith( filteredOrganisations: filteredOrganisations, typeFilter: event.type, )); } /// Trie les organisations void _onSortOrganisations( SortOrganisations event, Emitter emit, ) { final currentState = state; if (currentState is! OrganisationsLoaded) return; List sortedOrganisations; switch (event.sortType) { case OrganisationSortType.nom: sortedOrganisations = _organisationService.sortByName( currentState.filteredOrganisations, ascending: event.ascending, ); break; case OrganisationSortType.dateCreation: sortedOrganisations = _organisationService.sortByCreationDate( currentState.filteredOrganisations, ascending: event.ascending, ); break; case OrganisationSortType.nombreMembres: sortedOrganisations = _organisationService.sortByMemberCount( currentState.filteredOrganisations, ascending: event.ascending, ); break; default: sortedOrganisations = currentState.filteredOrganisations; } emit(currentState.copyWith( filteredOrganisations: sortedOrganisations, sortType: event.sortType, sortAscending: event.ascending, )); } /// Charge les statistiques Future _onLoadOrganisationsStats( LoadOrganisationsStats event, Emitter emit, ) async { emit(const OrganisationsStatsLoading()); try { final stats = await _organisationService.getOrganisationsStats(); emit(OrganisationsStatsLoaded(stats)); } catch (e) { emit(const OrganisationsStatsError('Erreur lors du chargement des statistiques')); } } /// Efface les filtres void _onClearOrganisationsFilters( ClearOrganisationsFilters event, Emitter emit, ) { final currentState = state; if (currentState is! OrganisationsLoaded) return; emit(currentState.copyWith( filteredOrganisations: currentState.organisations, clearSearch: true, clearStatusFilter: true, clearTypeFilter: true, clearSort: true, )); } /// Rafraîchit les données void _onRefreshOrganisations( RefreshOrganisations event, Emitter emit, ) { add(const LoadOrganisations(refresh: true)); } /// Remet à zéro l'état void _onResetOrganisationsState( ResetOrganisationsState event, Emitter emit, ) { emit(const OrganisationsInitial()); } /// Applique les filtres actuels à une liste d'organisations List _applyCurrentFilters( List organisations, OrganisationsLoaded state, ) { var filtered = organisations; // Filtre par recherche if (state.currentSearch?.isNotEmpty == true) { filtered = _organisationService.searchLocal(filtered, state.currentSearch!); } // Filtre par statut if (state.statusFilter != null) { filtered = _organisationService.filterByStatus(filtered, state.statusFilter!); } // Filtre par type if (state.typeFilter != null) { filtered = _organisationService.filterByType(filtered, state.typeFilter!); } return filtered; } }