Clean project: remove test files, debug logs, and add documentation

This commit is contained in:
dahoud
2025-10-05 13:41:33 +00:00
parent 96a17eadbd
commit 291847924c
438 changed files with 65754 additions and 32713 deletions

View File

@@ -0,0 +1,488 @@
/// 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<OrganisationsEvent, OrganisationsState> {
final OrganisationService _organisationService;
OrganisationsBloc(this._organisationService) : super(const OrganisationsInitial()) {
// Enregistrement des handlers d'événements
on<LoadOrganisations>(_onLoadOrganisations);
on<LoadMoreOrganisations>(_onLoadMoreOrganisations);
on<SearchOrganisations>(_onSearchOrganisations);
on<AdvancedSearchOrganisations>(_onAdvancedSearchOrganisations);
on<LoadOrganisationById>(_onLoadOrganisationById);
on<CreateOrganisation>(_onCreateOrganisation);
on<UpdateOrganisation>(_onUpdateOrganisation);
on<DeleteOrganisation>(_onDeleteOrganisation);
on<ActivateOrganisation>(_onActivateOrganisation);
on<FilterOrganisationsByStatus>(_onFilterOrganisationsByStatus);
on<FilterOrganisationsByType>(_onFilterOrganisationsByType);
on<SortOrganisations>(_onSortOrganisations);
on<LoadOrganisationsStats>(_onLoadOrganisationsStats);
on<ClearOrganisationsFilters>(_onClearOrganisationsFilters);
on<RefreshOrganisations>(_onRefreshOrganisations);
on<ResetOrganisationsState>(_onResetOrganisationsState);
}
/// Charge la liste des organisations
Future<void> _onLoadOrganisations(
LoadOrganisations event,
Emitter<OrganisationsState> 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<void> _onLoadMoreOrganisations(
LoadMoreOrganisations event,
Emitter<OrganisationsState> 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<void> _onSearchOrganisations(
SearchOrganisations event,
Emitter<OrganisationsState> 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<void> _onAdvancedSearchOrganisations(
AdvancedSearchOrganisations event,
Emitter<OrganisationsState> 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<void> _onLoadOrganisationById(
LoadOrganisationById event,
Emitter<OrganisationsState> 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<void> _onCreateOrganisation(
CreateOrganisation event,
Emitter<OrganisationsState> 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<void> _onUpdateOrganisation(
UpdateOrganisation event,
Emitter<OrganisationsState> 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<void> _onDeleteOrganisation(
DeleteOrganisation event,
Emitter<OrganisationsState> 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<void> _onActivateOrganisation(
ActivateOrganisation event,
Emitter<OrganisationsState> 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<OrganisationsState> 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<OrganisationsState> 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<OrganisationsState> emit,
) {
final currentState = state;
if (currentState is! OrganisationsLoaded) return;
List<OrganisationModel> 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<void> _onLoadOrganisationsStats(
LoadOrganisationsStats event,
Emitter<OrganisationsState> 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<OrganisationsState> 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<OrganisationsState> emit,
) {
add(const LoadOrganisations(refresh: true));
}
/// Remet à zéro l'état
void _onResetOrganisationsState(
ResetOrganisationsState event,
Emitter<OrganisationsState> emit,
) {
emit(const OrganisationsInitial());
}
/// Applique les filtres actuels à une liste d'organisations
List<OrganisationModel> _applyCurrentFilters(
List<OrganisationModel> 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;
}
}

View File

@@ -0,0 +1,216 @@
/// Événements pour le BLoC des organisations
library organisations_event;
import 'package:equatable/equatable.dart';
import '../data/models/organisation_model.dart';
/// Classe de base pour tous les événements des organisations
abstract class OrganisationsEvent extends Equatable {
const OrganisationsEvent();
@override
List<Object?> get props => [];
}
/// Événement pour charger la liste des organisations
class LoadOrganisations extends OrganisationsEvent {
final int page;
final int size;
final String? recherche;
final bool refresh;
const LoadOrganisations({
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 LoadMoreOrganisations extends OrganisationsEvent {
const LoadMoreOrganisations();
}
/// Événement pour rechercher des organisations
class SearchOrganisations extends OrganisationsEvent {
final String query;
const SearchOrganisations(this.query);
@override
List<Object?> get props => [query];
}
/// Événement pour recherche avancée
class AdvancedSearchOrganisations extends OrganisationsEvent {
final String? nom;
final TypeOrganisation? type;
final StatutOrganisation? statut;
final String? ville;
final String? region;
final String? pays;
final int page;
final int size;
const AdvancedSearchOrganisations({
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 LoadOrganisationById extends OrganisationsEvent {
final String id;
const LoadOrganisationById(this.id);
@override
List<Object?> get props => [id];
}
/// Événement pour créer une nouvelle organisation
class CreateOrganisation extends OrganisationsEvent {
final OrganisationModel organisation;
const CreateOrganisation(this.organisation);
@override
List<Object?> get props => [organisation];
}
/// Événement pour mettre à jour une organisation
class UpdateOrganisation extends OrganisationsEvent {
final String id;
final OrganisationModel organisation;
const UpdateOrganisation(this.id, this.organisation);
@override
List<Object?> get props => [id, organisation];
}
/// Événement pour supprimer une organisation
class DeleteOrganisation extends OrganisationsEvent {
final String id;
const DeleteOrganisation(this.id);
@override
List<Object?> get props => [id];
}
/// Événement pour activer une organisation
class ActivateOrganisation extends OrganisationsEvent {
final String id;
const ActivateOrganisation(this.id);
@override
List<Object?> get props => [id];
}
/// Événement pour filtrer les organisations par statut
class FilterOrganisationsByStatus extends OrganisationsEvent {
final StatutOrganisation? statut;
const FilterOrganisationsByStatus(this.statut);
@override
List<Object?> get props => [statut];
}
/// Événement pour filtrer les organisations par type
class FilterOrganisationsByType extends OrganisationsEvent {
final TypeOrganisation? type;
const FilterOrganisationsByType(this.type);
@override
List<Object?> get props => [type];
}
/// Événement pour trier les organisations
class SortOrganisations extends OrganisationsEvent {
final OrganisationSortType sortType;
final bool ascending;
const SortOrganisations(this.sortType, {this.ascending = true});
@override
List<Object?> get props => [sortType, ascending];
}
/// Événement pour charger les statistiques des organisations
class LoadOrganisationsStats extends OrganisationsEvent {
const LoadOrganisationsStats();
}
/// Événement pour effacer les filtres
class ClearOrganisationsFilters extends OrganisationsEvent {
const ClearOrganisationsFilters();
}
/// Événement pour rafraîchir les données
class RefreshOrganisations extends OrganisationsEvent {
const RefreshOrganisations();
}
/// Événement pour réinitialiser l'état
class ResetOrganisationsState extends OrganisationsEvent {
const ResetOrganisationsState();
}
/// Types de tri pour les organisations
enum OrganisationSortType {
nom,
dateCreation,
nombreMembres,
type,
statut,
}
/// Extension pour les types de tri
extension OrganisationSortTypeExtension on OrganisationSortType {
String get displayName {
switch (this) {
case OrganisationSortType.nom:
return 'Nom';
case OrganisationSortType.dateCreation:
return 'Date de création';
case OrganisationSortType.nombreMembres:
return 'Nombre de membres';
case OrganisationSortType.type:
return 'Type';
case OrganisationSortType.statut:
return 'Statut';
}
}
String get icon {
switch (this) {
case OrganisationSortType.nom:
return '📝';
case OrganisationSortType.dateCreation:
return '📅';
case OrganisationSortType.nombreMembres:
return '👥';
case OrganisationSortType.type:
return '🏷️';
case OrganisationSortType.statut:
return '📊';
}
}
}

View File

@@ -0,0 +1,282 @@
/// États pour le BLoC des organisations
library organisations_state;
import 'package:equatable/equatable.dart';
import '../data/models/organisation_model.dart';
import 'organisations_event.dart';
/// Classe de base pour tous les états des organisations
abstract class OrganisationsState extends Equatable {
const OrganisationsState();
@override
List<Object?> get props => [];
}
/// État initial
class OrganisationsInitial extends OrganisationsState {
const OrganisationsInitial();
}
/// État de chargement
class OrganisationsLoading extends OrganisationsState {
const OrganisationsLoading();
}
/// État de chargement de plus d'éléments (pagination)
class OrganisationsLoadingMore extends OrganisationsState {
final List<OrganisationModel> currentOrganisations;
const OrganisationsLoadingMore(this.currentOrganisations);
@override
List<Object?> get props => [currentOrganisations];
}
/// État de succès avec données
class OrganisationsLoaded extends OrganisationsState {
final List<OrganisationModel> organisations;
final List<OrganisationModel> filteredOrganisations;
final bool hasReachedMax;
final int currentPage;
final String? currentSearch;
final StatutOrganisation? statusFilter;
final TypeOrganisation? typeFilter;
final OrganisationSortType? sortType;
final bool sortAscending;
final Map<String, dynamic>? stats;
const OrganisationsLoaded({
required this.organisations,
required this.filteredOrganisations,
this.hasReachedMax = false,
this.currentPage = 0,
this.currentSearch,
this.statusFilter,
this.typeFilter,
this.sortType,
this.sortAscending = true,
this.stats,
});
/// Copie avec modifications
OrganisationsLoaded copyWith({
List<OrganisationModel>? organisations,
List<OrganisationModel>? filteredOrganisations,
bool? hasReachedMax,
int? currentPage,
String? currentSearch,
StatutOrganisation? statusFilter,
TypeOrganisation? typeFilter,
OrganisationSortType? sortType,
bool? sortAscending,
Map<String, dynamic>? stats,
bool clearSearch = false,
bool clearStatusFilter = false,
bool clearTypeFilter = false,
bool clearSort = false,
}) {
return OrganisationsLoaded(
organisations: organisations ?? this.organisations,
filteredOrganisations: filteredOrganisations ?? this.filteredOrganisations,
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 => organisations.length;
/// Nombre d'organisations filtrées
int get filteredCount => filteredOrganisations.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 = organisations.where((org) => org.statut == StatutOrganisation.active).length;
final inactives = organisations.length - actives;
final totalMembres = organisations.fold<int>(0, (sum, org) => sum + org.nombreMembres);
return {
'total': organisations.length,
'actives': actives,
'inactives': inactives,
'totalMembres': totalMembres,
};
}
@override
List<Object?> get props => [
organisations,
filteredOrganisations,
hasReachedMax,
currentPage,
currentSearch,
statusFilter,
typeFilter,
sortType,
sortAscending,
stats,
];
}
/// État d'erreur
class OrganisationsError extends OrganisationsState {
final String message;
final String? details;
final List<OrganisationModel>? previousOrganisations;
const OrganisationsError(
this.message, {
this.details,
this.previousOrganisations,
});
@override
List<Object?> get props => [message, details, previousOrganisations];
}
/// État de chargement d'une organisation spécifique
class OrganisationLoading extends OrganisationsState {
final String id;
const OrganisationLoading(this.id);
@override
List<Object?> get props => [id];
}
/// État d'organisation chargée
class OrganisationLoaded extends OrganisationsState {
final OrganisationModel organisation;
const OrganisationLoaded(this.organisation);
@override
List<Object?> get props => [organisation];
}
/// État d'erreur pour une organisation spécifique
class OrganisationError extends OrganisationsState {
final String message;
final String? organisationId;
const OrganisationError(this.message, {this.organisationId});
@override
List<Object?> get props => [message, organisationId];
}
/// État de création d'organisation
class OrganisationCreating extends OrganisationsState {
const OrganisationCreating();
}
/// État de succès de création
class OrganisationCreated extends OrganisationsState {
final OrganisationModel organisation;
const OrganisationCreated(this.organisation);
@override
List<Object?> get props => [organisation];
}
/// État de mise à jour d'organisation
class OrganisationUpdating extends OrganisationsState {
final String id;
const OrganisationUpdating(this.id);
@override
List<Object?> get props => [id];
}
/// État de succès de mise à jour
class OrganisationUpdated extends OrganisationsState {
final OrganisationModel organisation;
const OrganisationUpdated(this.organisation);
@override
List<Object?> get props => [organisation];
}
/// État de suppression d'organisation
class OrganisationDeleting extends OrganisationsState {
final String id;
const OrganisationDeleting(this.id);
@override
List<Object?> get props => [id];
}
/// État de succès de suppression
class OrganisationDeleted extends OrganisationsState {
final String id;
const OrganisationDeleted(this.id);
@override
List<Object?> get props => [id];
}
/// État d'activation d'organisation
class OrganisationActivating extends OrganisationsState {
final String id;
const OrganisationActivating(this.id);
@override
List<Object?> get props => [id];
}
/// État de succès d'activation
class OrganisationActivated extends OrganisationsState {
final OrganisationModel organisation;
const OrganisationActivated(this.organisation);
@override
List<Object?> get props => [organisation];
}
/// État de chargement des statistiques
class OrganisationsStatsLoading extends OrganisationsState {
const OrganisationsStatsLoading();
}
/// État des statistiques chargées
class OrganisationsStatsLoaded extends OrganisationsState {
final Map<String, dynamic> stats;
const OrganisationsStatsLoaded(this.stats);
@override
List<Object?> get props => [stats];
}
/// État d'erreur des statistiques
class OrganisationsStatsError extends OrganisationsState {
final String message;
const OrganisationsStatsError(this.message);
@override
List<Object?> get props => [message];
}