489 lines
15 KiB
Dart
489 lines
15 KiB
Dart
/// 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;
|
|
}
|
|
}
|