/// 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 { final OrganizationService _organizationService; OrganizationsBloc(this._organizationService) : super(const OrganizationsInitial()) { // Enregistrement des handlers d'événements on(_onLoadOrganizations); on(_onLoadMoreOrganizations); on(_onSearchOrganizations); on(_onAdvancedSearchOrganizations); on(_onLoadOrganizationById); on(_onCreateOrganization); on(_onUpdateOrganization); on(_onDeleteOrganization); on(_onActivateOrganization); on(_onFilterOrganizationsByStatus); on(_onFilterOrganizationsByType); on(_onSortOrganizations); on(_onLoadOrganizationsStats); on(_onClearOrganizationsFilters); on(_onRefreshOrganizations); on(_onResetOrganizationsState); } /// Charge la liste des organisations Future _onLoadOrganizations( LoadOrganizations event, Emitter 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 _onLoadMoreOrganizations( LoadMoreOrganizations event, Emitter 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 _onSearchOrganizations( SearchOrganizations event, Emitter 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 _onAdvancedSearchOrganizations( AdvancedSearchOrganizations event, Emitter 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 _onLoadOrganizationById( LoadOrganizationById event, Emitter 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 _onCreateOrganization( CreateOrganization event, Emitter 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 _onUpdateOrganization( UpdateOrganization event, Emitter 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 _onDeleteOrganization( DeleteOrganization event, Emitter 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 _onActivateOrganization( ActivateOrganization event, Emitter 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 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 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 emit, ) { final currentState = state; if (currentState is! OrganizationsLoaded) return; List 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 _onLoadOrganizationsStats( LoadOrganizationsStats event, Emitter 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 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 emit, ) { add(const LoadOrganizations(refresh: true)); } /// Remet à zéro l'état void _onResetOrganizationsState( ResetOrganizationsState event, Emitter emit, ) { emit(const OrganizationsInitial()); } /// Applique les filtres actuels à une liste d'organisations List _applyCurrentFilters( List 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; } }