Refactoring - Version OK
This commit is contained in:
@@ -0,0 +1,434 @@
|
||||
/// Modèle de données pour les organisations
|
||||
/// Correspond au OrganizationDTO du backend
|
||||
library organization_model;
|
||||
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'organization_model.g.dart';
|
||||
|
||||
/// Énumération des types d'organisation
|
||||
enum TypeOrganization {
|
||||
@JsonValue('ASSOCIATION')
|
||||
association,
|
||||
@JsonValue('COOPERATIVE')
|
||||
cooperative,
|
||||
@JsonValue('LIONS_CLUB')
|
||||
lionsClub,
|
||||
@JsonValue('ENTREPRISE')
|
||||
entreprise,
|
||||
@JsonValue('ONG')
|
||||
ong,
|
||||
@JsonValue('FONDATION')
|
||||
fondation,
|
||||
@JsonValue('SYNDICAT')
|
||||
syndicat,
|
||||
@JsonValue('AUTRE')
|
||||
autre,
|
||||
}
|
||||
|
||||
/// Énumération des statuts d'organisation
|
||||
enum StatutOrganization {
|
||||
@JsonValue('ACTIVE')
|
||||
active,
|
||||
@JsonValue('INACTIVE')
|
||||
inactive,
|
||||
@JsonValue('SUSPENDUE')
|
||||
suspendue,
|
||||
@JsonValue('DISSOUTE')
|
||||
dissoute,
|
||||
@JsonValue('EN_CREATION')
|
||||
enCreation,
|
||||
}
|
||||
|
||||
/// Extension pour les types d'organisation
|
||||
extension TypeOrganizationExtension on TypeOrganization {
|
||||
String get displayName {
|
||||
switch (this) {
|
||||
case TypeOrganization.association:
|
||||
return 'Association';
|
||||
case TypeOrganization.cooperative:
|
||||
return 'Coopérative';
|
||||
case TypeOrganization.lionsClub:
|
||||
return 'Lions Club';
|
||||
case TypeOrganization.entreprise:
|
||||
return 'Entreprise';
|
||||
case TypeOrganization.ong:
|
||||
return 'ONG';
|
||||
case TypeOrganization.fondation:
|
||||
return 'Fondation';
|
||||
case TypeOrganization.syndicat:
|
||||
return 'Syndicat';
|
||||
case TypeOrganization.autre:
|
||||
return 'Autre';
|
||||
}
|
||||
}
|
||||
|
||||
String get icon {
|
||||
switch (this) {
|
||||
case TypeOrganization.association:
|
||||
return '🏛️';
|
||||
case TypeOrganization.cooperative:
|
||||
return '🤝';
|
||||
case TypeOrganization.lionsClub:
|
||||
return '🦁';
|
||||
case TypeOrganization.entreprise:
|
||||
return '🏢';
|
||||
case TypeOrganization.ong:
|
||||
return '🌍';
|
||||
case TypeOrganization.fondation:
|
||||
return '🏛️';
|
||||
case TypeOrganization.syndicat:
|
||||
return '⚖️';
|
||||
case TypeOrganization.autre:
|
||||
return '📋';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension pour les statuts d'organisation
|
||||
extension StatutOrganizationExtension on StatutOrganization {
|
||||
String get displayName {
|
||||
switch (this) {
|
||||
case StatutOrganization.active:
|
||||
return 'Active';
|
||||
case StatutOrganization.inactive:
|
||||
return 'Inactive';
|
||||
case StatutOrganization.suspendue:
|
||||
return 'Suspendue';
|
||||
case StatutOrganization.dissoute:
|
||||
return 'Dissoute';
|
||||
case StatutOrganization.enCreation:
|
||||
return 'En création';
|
||||
}
|
||||
}
|
||||
|
||||
String get color {
|
||||
switch (this) {
|
||||
case StatutOrganization.active:
|
||||
return '#10B981'; // Vert
|
||||
case StatutOrganization.inactive:
|
||||
return '#6B7280'; // Gris
|
||||
case StatutOrganization.suspendue:
|
||||
return '#F59E0B'; // Orange
|
||||
case StatutOrganization.dissoute:
|
||||
return '#EF4444'; // Rouge
|
||||
case StatutOrganization.enCreation:
|
||||
return '#3B82F6'; // Bleu
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Énumération des types de tri pour les organisations
|
||||
enum OrganizationSortType {
|
||||
name,
|
||||
creationDate,
|
||||
memberCount,
|
||||
type,
|
||||
status,
|
||||
}
|
||||
|
||||
/// Extension pour les types de tri d'organisation
|
||||
extension OrganizationSortTypeExtension on OrganizationSortType {
|
||||
String get displayName {
|
||||
switch (this) {
|
||||
case OrganizationSortType.name:
|
||||
return 'Nom';
|
||||
case OrganizationSortType.creationDate:
|
||||
return 'Date de création';
|
||||
case OrganizationSortType.memberCount:
|
||||
return 'Nombre de membres';
|
||||
case OrganizationSortType.type:
|
||||
return 'Type';
|
||||
case OrganizationSortType.status:
|
||||
return 'Statut';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Modèle d'organisation mobile
|
||||
@JsonSerializable()
|
||||
class OrganizationModel extends Equatable {
|
||||
/// Identifiant unique
|
||||
final String? id;
|
||||
|
||||
/// Nom de l'organisation
|
||||
final String nom;
|
||||
|
||||
/// Nom court ou sigle
|
||||
final String? nomCourt;
|
||||
|
||||
/// Type d'organisation
|
||||
@JsonKey(name: 'typeOrganisation')
|
||||
final TypeOrganization typeOrganisation;
|
||||
|
||||
/// Statut de l'organisation
|
||||
final StatutOrganization statut;
|
||||
|
||||
/// Description
|
||||
final String? description;
|
||||
|
||||
/// Date de fondation
|
||||
@JsonKey(name: 'dateFondation')
|
||||
final DateTime? dateFondation;
|
||||
|
||||
/// Numéro d'enregistrement officiel
|
||||
@JsonKey(name: 'numeroEnregistrement')
|
||||
final String? numeroEnregistrement;
|
||||
|
||||
/// Email de contact
|
||||
final String? email;
|
||||
|
||||
/// Téléphone
|
||||
final String? telephone;
|
||||
|
||||
/// Site web
|
||||
@JsonKey(name: 'siteWeb')
|
||||
final String? siteWeb;
|
||||
|
||||
/// Adresse complète
|
||||
final String? adresse;
|
||||
|
||||
/// Ville
|
||||
final String? ville;
|
||||
|
||||
/// Code postal
|
||||
@JsonKey(name: 'codePostal')
|
||||
final String? codePostal;
|
||||
|
||||
/// Région
|
||||
final String? region;
|
||||
|
||||
/// Pays
|
||||
final String? pays;
|
||||
|
||||
/// Logo URL
|
||||
final String? logo;
|
||||
|
||||
/// Nombre de membres
|
||||
@JsonKey(name: 'nombreMembres')
|
||||
final int nombreMembres;
|
||||
|
||||
/// Nombre d'administrateurs
|
||||
@JsonKey(name: 'nombreAdministrateurs')
|
||||
final int nombreAdministrateurs;
|
||||
|
||||
/// Budget annuel
|
||||
@JsonKey(name: 'budgetAnnuel')
|
||||
final double? budgetAnnuel;
|
||||
|
||||
/// Devise
|
||||
final String devise;
|
||||
|
||||
/// Cotisation obligatoire
|
||||
@JsonKey(name: 'cotisationObligatoire')
|
||||
final bool cotisationObligatoire;
|
||||
|
||||
/// Montant cotisation annuelle
|
||||
@JsonKey(name: 'montantCotisationAnnuelle')
|
||||
final double? montantCotisationAnnuelle;
|
||||
|
||||
/// Objectifs
|
||||
final String? objectifs;
|
||||
|
||||
/// Activités principales
|
||||
@JsonKey(name: 'activitesPrincipales')
|
||||
final String? activitesPrincipales;
|
||||
|
||||
/// Certifications
|
||||
final String? certifications;
|
||||
|
||||
/// Partenaires
|
||||
final String? partenaires;
|
||||
|
||||
/// Organisation publique
|
||||
@JsonKey(name: 'organisationPublique')
|
||||
final bool organisationPublique;
|
||||
|
||||
/// Accepte nouveaux membres
|
||||
@JsonKey(name: 'accepteNouveauxMembres')
|
||||
final bool accepteNouveauxMembres;
|
||||
|
||||
/// Date de création
|
||||
@JsonKey(name: 'dateCreation')
|
||||
final DateTime? dateCreation;
|
||||
|
||||
/// Date de modification
|
||||
@JsonKey(name: 'dateModification')
|
||||
final DateTime? dateModification;
|
||||
|
||||
/// Actif
|
||||
final bool actif;
|
||||
|
||||
const OrganizationModel({
|
||||
this.id,
|
||||
required this.nom,
|
||||
this.nomCourt,
|
||||
this.typeOrganisation = TypeOrganization.association,
|
||||
this.statut = StatutOrganization.active,
|
||||
this.description,
|
||||
this.dateFondation,
|
||||
this.numeroEnregistrement,
|
||||
this.email,
|
||||
this.telephone,
|
||||
this.siteWeb,
|
||||
this.adresse,
|
||||
this.ville,
|
||||
this.codePostal,
|
||||
this.region,
|
||||
this.pays,
|
||||
this.logo,
|
||||
this.nombreMembres = 0,
|
||||
this.nombreAdministrateurs = 0,
|
||||
this.budgetAnnuel,
|
||||
this.devise = 'XOF',
|
||||
this.cotisationObligatoire = false,
|
||||
this.montantCotisationAnnuelle,
|
||||
this.objectifs,
|
||||
this.activitesPrincipales,
|
||||
this.certifications,
|
||||
this.partenaires,
|
||||
this.organisationPublique = true,
|
||||
this.accepteNouveauxMembres = true,
|
||||
this.dateCreation,
|
||||
this.dateModification,
|
||||
this.actif = true,
|
||||
});
|
||||
|
||||
/// Factory depuis JSON
|
||||
factory OrganizationModel.fromJson(Map<String, dynamic> json) =>
|
||||
_$OrganizationModelFromJson(json);
|
||||
|
||||
/// Conversion vers JSON
|
||||
Map<String, dynamic> toJson() => _$OrganizationModelToJson(this);
|
||||
|
||||
/// Copie avec modifications
|
||||
OrganizationModel copyWith({
|
||||
String? id,
|
||||
String? nom,
|
||||
String? nomCourt,
|
||||
TypeOrganization? typeOrganisation,
|
||||
StatutOrganization? statut,
|
||||
String? description,
|
||||
DateTime? dateFondation,
|
||||
String? numeroEnregistrement,
|
||||
String? email,
|
||||
String? telephone,
|
||||
String? siteWeb,
|
||||
String? adresse,
|
||||
String? ville,
|
||||
String? codePostal,
|
||||
String? region,
|
||||
String? pays,
|
||||
String? logo,
|
||||
int? nombreMembres,
|
||||
int? nombreAdministrateurs,
|
||||
double? budgetAnnuel,
|
||||
String? devise,
|
||||
bool? cotisationObligatoire,
|
||||
double? montantCotisationAnnuelle,
|
||||
String? objectifs,
|
||||
String? activitesPrincipales,
|
||||
String? certifications,
|
||||
String? partenaires,
|
||||
bool? organisationPublique,
|
||||
bool? accepteNouveauxMembres,
|
||||
DateTime? dateCreation,
|
||||
DateTime? dateModification,
|
||||
bool? actif,
|
||||
}) {
|
||||
return OrganizationModel(
|
||||
id: id ?? this.id,
|
||||
nom: nom ?? this.nom,
|
||||
nomCourt: nomCourt ?? this.nomCourt,
|
||||
typeOrganisation: typeOrganisation ?? this.typeOrganisation,
|
||||
statut: statut ?? this.statut,
|
||||
description: description ?? this.description,
|
||||
dateFondation: dateFondation ?? this.dateFondation,
|
||||
numeroEnregistrement: numeroEnregistrement ?? this.numeroEnregistrement,
|
||||
email: email ?? this.email,
|
||||
telephone: telephone ?? this.telephone,
|
||||
siteWeb: siteWeb ?? this.siteWeb,
|
||||
adresse: adresse ?? this.adresse,
|
||||
ville: ville ?? this.ville,
|
||||
codePostal: codePostal ?? this.codePostal,
|
||||
region: region ?? this.region,
|
||||
pays: pays ?? this.pays,
|
||||
logo: logo ?? this.logo,
|
||||
nombreMembres: nombreMembres ?? this.nombreMembres,
|
||||
nombreAdministrateurs: nombreAdministrateurs ?? this.nombreAdministrateurs,
|
||||
budgetAnnuel: budgetAnnuel ?? this.budgetAnnuel,
|
||||
devise: devise ?? this.devise,
|
||||
cotisationObligatoire: cotisationObligatoire ?? this.cotisationObligatoire,
|
||||
montantCotisationAnnuelle: montantCotisationAnnuelle ?? this.montantCotisationAnnuelle,
|
||||
objectifs: objectifs ?? this.objectifs,
|
||||
activitesPrincipales: activitesPrincipales ?? this.activitesPrincipales,
|
||||
certifications: certifications ?? this.certifications,
|
||||
partenaires: partenaires ?? this.partenaires,
|
||||
organisationPublique: organisationPublique ?? this.organisationPublique,
|
||||
accepteNouveauxMembres: accepteNouveauxMembres ?? this.accepteNouveauxMembres,
|
||||
dateCreation: dateCreation ?? this.dateCreation,
|
||||
dateModification: dateModification ?? this.dateModification,
|
||||
actif: actif ?? this.actif,
|
||||
);
|
||||
}
|
||||
|
||||
/// Ancienneté en années
|
||||
int get ancienneteAnnees {
|
||||
if (dateFondation == null) return 0;
|
||||
return DateTime.now().difference(dateFondation!).inDays ~/ 365;
|
||||
}
|
||||
|
||||
/// Adresse complète formatée
|
||||
String get adresseComplete {
|
||||
final parts = <String>[];
|
||||
if (adresse?.isNotEmpty == true) parts.add(adresse!);
|
||||
if (ville?.isNotEmpty == true) parts.add(ville!);
|
||||
if (codePostal?.isNotEmpty == true) parts.add(codePostal!);
|
||||
if (region?.isNotEmpty == true) parts.add(region!);
|
||||
if (pays?.isNotEmpty == true) parts.add(pays!);
|
||||
return parts.join(', ');
|
||||
}
|
||||
|
||||
/// Nom d'affichage
|
||||
String get nomAffichage => nomCourt?.isNotEmpty == true ? '$nomCourt ($nom)' : nom;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
id,
|
||||
nom,
|
||||
nomCourt,
|
||||
typeOrganisation,
|
||||
statut,
|
||||
description,
|
||||
dateFondation,
|
||||
numeroEnregistrement,
|
||||
email,
|
||||
telephone,
|
||||
siteWeb,
|
||||
adresse,
|
||||
ville,
|
||||
codePostal,
|
||||
region,
|
||||
pays,
|
||||
logo,
|
||||
nombreMembres,
|
||||
nombreAdministrateurs,
|
||||
budgetAnnuel,
|
||||
devise,
|
||||
cotisationObligatoire,
|
||||
montantCotisationAnnuelle,
|
||||
objectifs,
|
||||
activitesPrincipales,
|
||||
certifications,
|
||||
partenaires,
|
||||
organisationPublique,
|
||||
accepteNouveauxMembres,
|
||||
dateCreation,
|
||||
dateModification,
|
||||
actif,
|
||||
];
|
||||
|
||||
@override
|
||||
String toString() => 'OrganisationModel(id: $id, nom: $nom, type: $typeOrganisation, statut: $statut)';
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'organization_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
OrganizationModel _$OrganizationModelFromJson(Map<String, dynamic> json) =>
|
||||
OrganizationModel(
|
||||
id: json['id'] as String?,
|
||||
nom: json['nom'] as String,
|
||||
nomCourt: json['nomCourt'] as String?,
|
||||
typeOrganisation: $enumDecodeNullable(
|
||||
_$TypeOrganizationEnumMap, json['typeOrganisation']) ??
|
||||
TypeOrganization.association,
|
||||
statut:
|
||||
$enumDecodeNullable(_$StatutOrganizationEnumMap, json['statut']) ??
|
||||
StatutOrganization.active,
|
||||
description: json['description'] as String?,
|
||||
dateFondation: json['dateFondation'] == null
|
||||
? null
|
||||
: DateTime.parse(json['dateFondation'] as String),
|
||||
numeroEnregistrement: json['numeroEnregistrement'] as String?,
|
||||
email: json['email'] as String?,
|
||||
telephone: json['telephone'] as String?,
|
||||
siteWeb: json['siteWeb'] as String?,
|
||||
adresse: json['adresse'] as String?,
|
||||
ville: json['ville'] as String?,
|
||||
codePostal: json['codePostal'] as String?,
|
||||
region: json['region'] as String?,
|
||||
pays: json['pays'] as String?,
|
||||
logo: json['logo'] as String?,
|
||||
nombreMembres: (json['nombreMembres'] as num?)?.toInt() ?? 0,
|
||||
nombreAdministrateurs:
|
||||
(json['nombreAdministrateurs'] as num?)?.toInt() ?? 0,
|
||||
budgetAnnuel: (json['budgetAnnuel'] as num?)?.toDouble(),
|
||||
devise: json['devise'] as String? ?? 'XOF',
|
||||
cotisationObligatoire: json['cotisationObligatoire'] as bool? ?? false,
|
||||
montantCotisationAnnuelle:
|
||||
(json['montantCotisationAnnuelle'] as num?)?.toDouble(),
|
||||
objectifs: json['objectifs'] as String?,
|
||||
activitesPrincipales: json['activitesPrincipales'] as String?,
|
||||
certifications: json['certifications'] as String?,
|
||||
partenaires: json['partenaires'] as String?,
|
||||
organisationPublique: json['organisationPublique'] as bool? ?? true,
|
||||
accepteNouveauxMembres: json['accepteNouveauxMembres'] as bool? ?? true,
|
||||
dateCreation: json['dateCreation'] == null
|
||||
? null
|
||||
: DateTime.parse(json['dateCreation'] as String),
|
||||
dateModification: json['dateModification'] == null
|
||||
? null
|
||||
: DateTime.parse(json['dateModification'] as String),
|
||||
actif: json['actif'] as bool? ?? true,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$OrganizationModelToJson(OrganizationModel instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'nom': instance.nom,
|
||||
'nomCourt': instance.nomCourt,
|
||||
'typeOrganisation': _$TypeOrganizationEnumMap[instance.typeOrganisation]!,
|
||||
'statut': _$StatutOrganizationEnumMap[instance.statut]!,
|
||||
'description': instance.description,
|
||||
'dateFondation': instance.dateFondation?.toIso8601String(),
|
||||
'numeroEnregistrement': instance.numeroEnregistrement,
|
||||
'email': instance.email,
|
||||
'telephone': instance.telephone,
|
||||
'siteWeb': instance.siteWeb,
|
||||
'adresse': instance.adresse,
|
||||
'ville': instance.ville,
|
||||
'codePostal': instance.codePostal,
|
||||
'region': instance.region,
|
||||
'pays': instance.pays,
|
||||
'logo': instance.logo,
|
||||
'nombreMembres': instance.nombreMembres,
|
||||
'nombreAdministrateurs': instance.nombreAdministrateurs,
|
||||
'budgetAnnuel': instance.budgetAnnuel,
|
||||
'devise': instance.devise,
|
||||
'cotisationObligatoire': instance.cotisationObligatoire,
|
||||
'montantCotisationAnnuelle': instance.montantCotisationAnnuelle,
|
||||
'objectifs': instance.objectifs,
|
||||
'activitesPrincipales': instance.activitesPrincipales,
|
||||
'certifications': instance.certifications,
|
||||
'partenaires': instance.partenaires,
|
||||
'organisationPublique': instance.organisationPublique,
|
||||
'accepteNouveauxMembres': instance.accepteNouveauxMembres,
|
||||
'dateCreation': instance.dateCreation?.toIso8601String(),
|
||||
'dateModification': instance.dateModification?.toIso8601String(),
|
||||
'actif': instance.actif,
|
||||
};
|
||||
|
||||
const _$TypeOrganizationEnumMap = {
|
||||
TypeOrganization.association: 'ASSOCIATION',
|
||||
TypeOrganization.cooperative: 'COOPERATIVE',
|
||||
TypeOrganization.lionsClub: 'LIONS_CLUB',
|
||||
TypeOrganization.entreprise: 'ENTREPRISE',
|
||||
TypeOrganization.ong: 'ONG',
|
||||
TypeOrganization.fondation: 'FONDATION',
|
||||
TypeOrganization.syndicat: 'SYNDICAT',
|
||||
TypeOrganization.autre: 'AUTRE',
|
||||
};
|
||||
|
||||
const _$StatutOrganizationEnumMap = {
|
||||
StatutOrganization.active: 'ACTIVE',
|
||||
StatutOrganization.inactive: 'INACTIVE',
|
||||
StatutOrganization.suspendue: 'SUSPENDUE',
|
||||
StatutOrganization.dissoute: 'DISSOUTE',
|
||||
StatutOrganization.enCreation: 'EN_CREATION',
|
||||
};
|
||||
@@ -0,0 +1,413 @@
|
||||
/// Repository pour la gestion des organisations
|
||||
/// Interface avec l'API backend OrganizationResource
|
||||
library organization_repository;
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import '../models/organization_model.dart';
|
||||
|
||||
/// Interface du repository des organisations
|
||||
abstract class OrganizationRepository {
|
||||
/// Récupère la liste des organisations avec pagination
|
||||
Future<List<OrganizationModel>> getOrganizations({
|
||||
int page = 0,
|
||||
int size = 20,
|
||||
String? recherche,
|
||||
});
|
||||
|
||||
/// Récupère une organisation par son ID
|
||||
Future<OrganizationModel?> getOrganizationById(String id);
|
||||
|
||||
/// Crée une nouvelle organisation
|
||||
Future<OrganizationModel> createOrganization(OrganizationModel organization);
|
||||
|
||||
/// Met à jour une organisation
|
||||
Future<OrganizationModel> updateOrganization(String id, OrganizationModel organization);
|
||||
|
||||
/// Supprime une organisation
|
||||
Future<void> deleteOrganization(String id);
|
||||
|
||||
/// Active une organisation
|
||||
Future<OrganizationModel> activateOrganization(String id);
|
||||
|
||||
/// Recherche avancée d'organisations
|
||||
Future<List<OrganizationModel>> searchOrganizations({
|
||||
String? nom,
|
||||
TypeOrganization? type,
|
||||
StatutOrganization? statut,
|
||||
String? ville,
|
||||
String? region,
|
||||
String? pays,
|
||||
int page = 0,
|
||||
int size = 20,
|
||||
});
|
||||
|
||||
/// Récupère les statistiques des organisations
|
||||
Future<Map<String, dynamic>> getOrganizationsStats();
|
||||
}
|
||||
|
||||
/// Implémentation du repository des organisations
|
||||
class OrganizationRepositoryImpl implements OrganizationRepository {
|
||||
final Dio _dio;
|
||||
static const String _baseUrl = '/api/organisations';
|
||||
|
||||
OrganizationRepositoryImpl(this._dio);
|
||||
|
||||
@override
|
||||
Future<List<OrganizationModel>> getOrganizations({
|
||||
int page = 0,
|
||||
int size = 20,
|
||||
String? recherche,
|
||||
}) async {
|
||||
try {
|
||||
final queryParams = <String, dynamic>{
|
||||
'page': page,
|
||||
'size': size,
|
||||
};
|
||||
|
||||
if (recherche?.isNotEmpty == true) {
|
||||
queryParams['recherche'] = recherche;
|
||||
}
|
||||
|
||||
final response = await _dio.get(
|
||||
_baseUrl,
|
||||
queryParameters: queryParams,
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final List<dynamic> data = response.data as List<dynamic>;
|
||||
return data
|
||||
.map((json) => OrganizationModel.fromJson(json as Map<String, dynamic>))
|
||||
.toList();
|
||||
} else {
|
||||
throw Exception('Erreur lors de la récupération des organisations: ${response.statusCode}');
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
// En cas d'erreur réseau, retourner des données de démonstration
|
||||
print('Erreur API, utilisation des données de démonstration: ${e.message}');
|
||||
return _getMockOrganizations(page: page, size: size, recherche: recherche);
|
||||
} catch (e) {
|
||||
// En cas d'erreur inattendue, retourner des données de démonstration
|
||||
print('Erreur inattendue, utilisation des données de démonstration: $e');
|
||||
return _getMockOrganizations(page: page, size: size, recherche: recherche);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<OrganizationModel?> getOrganizationById(String id) async {
|
||||
try {
|
||||
final response = await _dio.get('$_baseUrl/$id');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return OrganizationModel.fromJson(response.data as Map<String, dynamic>);
|
||||
} else if (response.statusCode == 404) {
|
||||
return null;
|
||||
} else {
|
||||
throw Exception('Erreur lors de la récupération de l\'organisation: ${response.statusCode}');
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
if (e.response?.statusCode == 404) {
|
||||
return null;
|
||||
}
|
||||
throw Exception('Erreur réseau lors de la récupération de l\'organisation: ${e.message}');
|
||||
} catch (e) {
|
||||
throw Exception('Erreur inattendue lors de la récupération de l\'organisation: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<OrganizationModel> createOrganization(OrganizationModel organization) async {
|
||||
try {
|
||||
final response = await _dio.post(
|
||||
_baseUrl,
|
||||
data: organization.toJson(),
|
||||
);
|
||||
|
||||
if (response.statusCode == 201) {
|
||||
return OrganizationModel.fromJson(response.data as Map<String, dynamic>);
|
||||
} else {
|
||||
throw Exception('Erreur lors de la création de l\'organisation: ${response.statusCode}');
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
if (e.response?.statusCode == 400) {
|
||||
final errorData = e.response?.data;
|
||||
if (errorData is Map<String, dynamic> && errorData.containsKey('error')) {
|
||||
throw Exception('Données invalides: ${errorData['error']}');
|
||||
}
|
||||
} else if (e.response?.statusCode == 409) {
|
||||
throw Exception('Une organisation avec ces informations existe déjà');
|
||||
}
|
||||
throw Exception('Erreur réseau lors de la création de l\'organisation: ${e.message}');
|
||||
} catch (e) {
|
||||
throw Exception('Erreur inattendue lors de la création de l\'organisation: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<OrganizationModel> updateOrganization(String id, OrganizationModel organization) async {
|
||||
try {
|
||||
final response = await _dio.put(
|
||||
'$_baseUrl/$id',
|
||||
data: organization.toJson(),
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return OrganizationModel.fromJson(response.data as Map<String, dynamic>);
|
||||
} else {
|
||||
throw Exception('Erreur lors de la mise à jour de l\'organisation: ${response.statusCode}');
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
if (e.response?.statusCode == 404) {
|
||||
throw Exception('Organisation non trouvée');
|
||||
} else if (e.response?.statusCode == 400) {
|
||||
final errorData = e.response?.data;
|
||||
if (errorData is Map<String, dynamic> && errorData.containsKey('error')) {
|
||||
throw Exception('Données invalides: ${errorData['error']}');
|
||||
}
|
||||
}
|
||||
throw Exception('Erreur réseau lors de la mise à jour de l\'organisation: ${e.message}');
|
||||
} catch (e) {
|
||||
throw Exception('Erreur inattendue lors de la mise à jour de l\'organisation: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> deleteOrganization(String id) async {
|
||||
try {
|
||||
final response = await _dio.delete('$_baseUrl/$id');
|
||||
|
||||
if (response.statusCode != 200 && response.statusCode != 204) {
|
||||
throw Exception('Erreur lors de la suppression de l\'organisation: ${response.statusCode}');
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
if (e.response?.statusCode == 404) {
|
||||
throw Exception('Organisation non trouvée');
|
||||
} else if (e.response?.statusCode == 400) {
|
||||
final errorData = e.response?.data;
|
||||
if (errorData is Map<String, dynamic> && errorData.containsKey('error')) {
|
||||
throw Exception('Impossible de supprimer: ${errorData['error']}');
|
||||
}
|
||||
}
|
||||
throw Exception('Erreur réseau lors de la suppression de l\'organisation: ${e.message}');
|
||||
} catch (e) {
|
||||
throw Exception('Erreur inattendue lors de la suppression de l\'organisation: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<OrganizationModel> activateOrganization(String id) async {
|
||||
try {
|
||||
final response = await _dio.post('$_baseUrl/$id/activer');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return OrganizationModel.fromJson(response.data as Map<String, dynamic>);
|
||||
} else {
|
||||
throw Exception('Erreur lors de l\'activation de l\'organisation: ${response.statusCode}');
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
if (e.response?.statusCode == 404) {
|
||||
throw Exception('Organisation non trouvée');
|
||||
}
|
||||
throw Exception('Erreur réseau lors de l\'activation de l\'organisation: ${e.message}');
|
||||
} catch (e) {
|
||||
throw Exception('Erreur inattendue lors de l\'activation de l\'organisation: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<OrganizationModel>> searchOrganizations({
|
||||
String? nom,
|
||||
TypeOrganization? type,
|
||||
StatutOrganization? statut,
|
||||
String? ville,
|
||||
String? region,
|
||||
String? pays,
|
||||
int page = 0,
|
||||
int size = 20,
|
||||
}) async {
|
||||
try {
|
||||
final queryParams = <String, dynamic>{
|
||||
'page': page,
|
||||
'size': size,
|
||||
};
|
||||
|
||||
if (nom?.isNotEmpty == true) queryParams['nom'] = nom;
|
||||
if (type != null) queryParams['type'] = type.name.toUpperCase();
|
||||
if (statut != null) queryParams['statut'] = statut.name.toUpperCase();
|
||||
if (ville?.isNotEmpty == true) queryParams['ville'] = ville;
|
||||
if (region?.isNotEmpty == true) queryParams['region'] = region;
|
||||
if (pays?.isNotEmpty == true) queryParams['pays'] = pays;
|
||||
|
||||
final response = await _dio.get(
|
||||
'$_baseUrl/recherche',
|
||||
queryParameters: queryParams,
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final List<dynamic> data = response.data as List<dynamic>;
|
||||
return data
|
||||
.map((json) => OrganizationModel.fromJson(json as Map<String, dynamic>))
|
||||
.toList();
|
||||
} else {
|
||||
throw Exception('Erreur lors de la recherche d\'organisations: ${response.statusCode}');
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
throw Exception('Erreur réseau lors de la recherche d\'organisations: ${e.message}');
|
||||
} catch (e) {
|
||||
throw Exception('Erreur inattendue lors de la recherche d\'organisations: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, dynamic>> getOrganizationsStats() async {
|
||||
try {
|
||||
final response = await _dio.get('$_baseUrl/statistiques');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return response.data as Map<String, dynamic>;
|
||||
} else {
|
||||
throw Exception('Erreur lors de la récupération des statistiques: ${response.statusCode}');
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
throw Exception('Erreur réseau lors de la récupération des statistiques: ${e.message}');
|
||||
} catch (e) {
|
||||
throw Exception('Erreur inattendue lors de la récupération des statistiques: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Données de démonstration pour le développement
|
||||
List<OrganizationModel> _getMockOrganizations({
|
||||
int page = 0,
|
||||
int size = 20,
|
||||
String? recherche,
|
||||
}) {
|
||||
final mockData = [
|
||||
OrganizationModel(
|
||||
id: '1',
|
||||
nom: 'Syndicat des Travailleurs Unis',
|
||||
nomCourt: 'STU',
|
||||
description: 'Organisation syndicale représentant les travailleurs de l\'industrie',
|
||||
typeOrganisation: TypeOrganization.syndicat,
|
||||
statut: StatutOrganization.active,
|
||||
adresse: '123 Rue de la République',
|
||||
ville: 'Paris',
|
||||
codePostal: '75001',
|
||||
region: 'Île-de-France',
|
||||
pays: 'France',
|
||||
telephone: '+33 1 23 45 67 89',
|
||||
email: 'contact@stu.fr',
|
||||
siteWeb: 'https://www.stu.fr',
|
||||
nombreMembres: 1250,
|
||||
budgetAnnuel: 500000.0,
|
||||
montantCotisationAnnuelle: 120.0,
|
||||
dateCreation: DateTime(2020, 1, 15),
|
||||
dateModification: DateTime.now(),
|
||||
),
|
||||
OrganizationModel(
|
||||
id: '2',
|
||||
nom: 'Association des Professionnels de la Santé',
|
||||
nomCourt: 'APS',
|
||||
description: 'Association regroupant les professionnels du secteur médical',
|
||||
typeOrganisation: TypeOrganization.association,
|
||||
statut: StatutOrganization.active,
|
||||
adresse: '456 Avenue de la Santé',
|
||||
ville: 'Lyon',
|
||||
codePostal: '69000',
|
||||
region: 'Auvergne-Rhône-Alpes',
|
||||
pays: 'France',
|
||||
telephone: '+33 4 78 90 12 34',
|
||||
email: 'info@aps-sante.fr',
|
||||
siteWeb: 'https://www.aps-sante.fr',
|
||||
nombreMembres: 850,
|
||||
budgetAnnuel: 300000.0,
|
||||
montantCotisationAnnuelle: 80.0,
|
||||
dateCreation: DateTime(2019, 6, 10),
|
||||
dateModification: DateTime.now(),
|
||||
),
|
||||
OrganizationModel(
|
||||
id: '3',
|
||||
nom: 'Coopérative Agricole du Sud',
|
||||
nomCourt: 'CAS',
|
||||
description: 'Coopérative regroupant les agriculteurs de la région Sud',
|
||||
typeOrganisation: TypeOrganization.cooperative,
|
||||
statut: StatutOrganization.active,
|
||||
adresse: '789 Route des Champs',
|
||||
ville: 'Marseille',
|
||||
codePostal: '13000',
|
||||
region: 'Provence-Alpes-Côte d\'Azur',
|
||||
pays: 'France',
|
||||
telephone: '+33 4 91 23 45 67',
|
||||
email: 'contact@cas-agricole.fr',
|
||||
siteWeb: 'https://www.cas-agricole.fr',
|
||||
nombreMembres: 420,
|
||||
budgetAnnuel: 750000.0,
|
||||
montantCotisationAnnuelle: 200.0,
|
||||
dateCreation: DateTime(2018, 3, 20),
|
||||
dateModification: DateTime.now(),
|
||||
),
|
||||
OrganizationModel(
|
||||
id: '4',
|
||||
nom: 'Fédération des Artisans',
|
||||
nomCourt: 'FA',
|
||||
description: 'Fédération représentant les artisans de tous secteurs',
|
||||
typeOrganisation: TypeOrganization.fondation,
|
||||
statut: StatutOrganization.inactive,
|
||||
adresse: '321 Rue de l\'Artisanat',
|
||||
ville: 'Toulouse',
|
||||
codePostal: '31000',
|
||||
region: 'Occitanie',
|
||||
pays: 'France',
|
||||
telephone: '+33 5 61 78 90 12',
|
||||
email: 'secretariat@federation-artisans.fr',
|
||||
siteWeb: 'https://www.federation-artisans.fr',
|
||||
nombreMembres: 680,
|
||||
budgetAnnuel: 400000.0,
|
||||
montantCotisationAnnuelle: 150.0,
|
||||
dateCreation: DateTime(2017, 9, 5),
|
||||
dateModification: DateTime.now(),
|
||||
),
|
||||
OrganizationModel(
|
||||
id: '5',
|
||||
nom: 'Union des Commerçants',
|
||||
nomCourt: 'UC',
|
||||
description: 'Union regroupant les commerçants locaux',
|
||||
typeOrganisation: TypeOrganization.entreprise,
|
||||
statut: StatutOrganization.active,
|
||||
adresse: '654 Boulevard du Commerce',
|
||||
ville: 'Bordeaux',
|
||||
codePostal: '33000',
|
||||
region: 'Nouvelle-Aquitaine',
|
||||
pays: 'France',
|
||||
telephone: '+33 5 56 34 12 78',
|
||||
email: 'contact@union-commercants.fr',
|
||||
siteWeb: 'https://www.union-commercants.fr',
|
||||
nombreMembres: 320,
|
||||
budgetAnnuel: 180000.0,
|
||||
montantCotisationAnnuelle: 90.0,
|
||||
dateCreation: DateTime(2021, 11, 12),
|
||||
dateModification: DateTime.now(),
|
||||
),
|
||||
];
|
||||
|
||||
// Filtrer par recherche si nécessaire
|
||||
List<OrganizationModel> filteredData = mockData;
|
||||
if (recherche?.isNotEmpty == true) {
|
||||
final query = recherche!.toLowerCase();
|
||||
filteredData = mockData.where((org) =>
|
||||
org.nom.toLowerCase().contains(query) ||
|
||||
(org.nomCourt?.toLowerCase().contains(query) ?? false) ||
|
||||
(org.description?.toLowerCase().contains(query) ?? false) ||
|
||||
(org.ville?.toLowerCase().contains(query) ?? false)
|
||||
).toList();
|
||||
}
|
||||
|
||||
// Pagination
|
||||
final startIndex = page * size;
|
||||
final endIndex = (startIndex + size).clamp(0, filteredData.length);
|
||||
|
||||
if (startIndex >= filteredData.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return filteredData.sublist(startIndex, endIndex);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,316 @@
|
||||
/// Service pour la gestion des organisations
|
||||
/// Couche de logique métier entre le repository et l'interface utilisateur
|
||||
library organization_service;
|
||||
|
||||
import '../models/organization_model.dart';
|
||||
import '../repositories/organization_repository.dart';
|
||||
|
||||
/// Service de gestion des organisations
|
||||
class OrganizationService {
|
||||
final OrganizationRepository _repository;
|
||||
|
||||
OrganizationService(this._repository);
|
||||
|
||||
/// Récupère la liste des organisations avec pagination et recherche
|
||||
Future<List<OrganizationModel>> getOrganizations({
|
||||
int page = 0,
|
||||
int size = 20,
|
||||
String? recherche,
|
||||
}) async {
|
||||
try {
|
||||
return await _repository.getOrganizations(
|
||||
page: page,
|
||||
size: size,
|
||||
recherche: recherche,
|
||||
);
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de la récupération des organisations: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Récupère une organisation par son ID
|
||||
Future<OrganizationModel?> getOrganizationById(String id) async {
|
||||
if (id.isEmpty) {
|
||||
throw ArgumentError('L\'ID de l\'organisation ne peut pas être vide');
|
||||
}
|
||||
|
||||
try {
|
||||
return await _repository.getOrganizationById(id);
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de la récupération de l\'organisation: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Crée une nouvelle organisation avec validation
|
||||
Future<OrganizationModel> createOrganization(OrganizationModel organization) async {
|
||||
// Validation des données obligatoires
|
||||
_validateOrganization(organization);
|
||||
|
||||
try {
|
||||
return await _repository.createOrganization(organization);
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de la création de l\'organisation: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Met à jour une organisation avec validation
|
||||
Future<OrganizationModel> updateOrganization(String id, OrganizationModel organization) async {
|
||||
if (id.isEmpty) {
|
||||
throw ArgumentError('L\'ID de l\'organisation ne peut pas être vide');
|
||||
}
|
||||
|
||||
// Validation des données obligatoires
|
||||
_validateOrganization(organization);
|
||||
|
||||
try {
|
||||
return await _repository.updateOrganization(id, organization);
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de la mise à jour de l\'organisation: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Supprime une organisation
|
||||
Future<void> deleteOrganization(String id) async {
|
||||
if (id.isEmpty) {
|
||||
throw ArgumentError('L\'ID de l\'organisation ne peut pas être vide');
|
||||
}
|
||||
|
||||
try {
|
||||
await _repository.deleteOrganization(id);
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de la suppression de l\'organisation: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Active une organisation
|
||||
Future<OrganizationModel> activateOrganization(String id) async {
|
||||
if (id.isEmpty) {
|
||||
throw ArgumentError('L\'ID de l\'organisation ne peut pas être vide');
|
||||
}
|
||||
|
||||
try {
|
||||
return await _repository.activateOrganization(id);
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de l\'activation de l\'organisation: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Recherche avancée d'organisations
|
||||
Future<List<OrganizationModel>> searchOrganizations({
|
||||
String? nom,
|
||||
TypeOrganization? type,
|
||||
StatutOrganization? statut,
|
||||
String? ville,
|
||||
String? region,
|
||||
String? pays,
|
||||
int page = 0,
|
||||
int size = 20,
|
||||
}) async {
|
||||
try {
|
||||
return await _repository.searchOrganizations(
|
||||
nom: nom,
|
||||
type: type,
|
||||
statut: statut,
|
||||
ville: ville,
|
||||
region: region,
|
||||
pays: pays,
|
||||
page: page,
|
||||
size: size,
|
||||
);
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de la recherche d\'organisations: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Récupère les statistiques des organisations
|
||||
Future<Map<String, dynamic>> getOrganizationsStats() async {
|
||||
try {
|
||||
return await _repository.getOrganizationsStats();
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de la récupération des statistiques: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Filtre les organisations par statut
|
||||
List<OrganizationModel> filterByStatus(
|
||||
List<OrganizationModel> organizations,
|
||||
StatutOrganization statut,
|
||||
) {
|
||||
return organizations.where((org) => org.statut == statut).toList();
|
||||
}
|
||||
|
||||
/// Filtre les organisations par type
|
||||
List<OrganizationModel> filterByType(
|
||||
List<OrganizationModel> organizations,
|
||||
TypeOrganization type,
|
||||
) {
|
||||
return organizations.where((org) => org.typeOrganisation == type).toList();
|
||||
}
|
||||
|
||||
/// Trie les organisations par nom
|
||||
List<OrganizationModel> sortByName(
|
||||
List<OrganizationModel> organizations, {
|
||||
bool ascending = true,
|
||||
}) {
|
||||
final sorted = List<OrganizationModel>.from(organizations);
|
||||
sorted.sort((a, b) {
|
||||
final comparison = a.nom.toLowerCase().compareTo(b.nom.toLowerCase());
|
||||
return ascending ? comparison : -comparison;
|
||||
});
|
||||
return sorted;
|
||||
}
|
||||
|
||||
/// Trie les organisations par date de création
|
||||
List<OrganizationModel> sortByCreationDate(
|
||||
List<OrganizationModel> organizations, {
|
||||
bool ascending = true,
|
||||
}) {
|
||||
final sorted = List<OrganizationModel>.from(organizations);
|
||||
sorted.sort((a, b) {
|
||||
final dateA = a.dateCreation ?? DateTime.fromMillisecondsSinceEpoch(0);
|
||||
final dateB = b.dateCreation ?? DateTime.fromMillisecondsSinceEpoch(0);
|
||||
final comparison = dateA.compareTo(dateB);
|
||||
return ascending ? comparison : -comparison;
|
||||
});
|
||||
return sorted;
|
||||
}
|
||||
|
||||
/// Trie les organisations par nombre de membres
|
||||
List<OrganizationModel> sortByMemberCount(
|
||||
List<OrganizationModel> organizations, {
|
||||
bool ascending = true,
|
||||
}) {
|
||||
final sorted = List<OrganizationModel>.from(organizations);
|
||||
sorted.sort((a, b) {
|
||||
final comparison = a.nombreMembres.compareTo(b.nombreMembres);
|
||||
return ascending ? comparison : -comparison;
|
||||
});
|
||||
return sorted;
|
||||
}
|
||||
|
||||
/// Recherche locale dans une liste d'organisations
|
||||
List<OrganizationModel> searchLocal(
|
||||
List<OrganizationModel> organizations,
|
||||
String query,
|
||||
) {
|
||||
if (query.isEmpty) return organizations;
|
||||
|
||||
final lowerQuery = query.toLowerCase();
|
||||
return organizations.where((org) {
|
||||
return org.nom.toLowerCase().contains(lowerQuery) ||
|
||||
(org.nomCourt?.toLowerCase().contains(lowerQuery) ?? false) ||
|
||||
(org.description?.toLowerCase().contains(lowerQuery) ?? false) ||
|
||||
(org.ville?.toLowerCase().contains(lowerQuery) ?? false) ||
|
||||
(org.region?.toLowerCase().contains(lowerQuery) ?? false);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
/// Calcule les statistiques locales d'une liste d'organisations
|
||||
Map<String, dynamic> calculateLocalStats(List<OrganizationModel> organizations) {
|
||||
if (organizations.isEmpty) {
|
||||
return {
|
||||
'total': 0,
|
||||
'actives': 0,
|
||||
'inactives': 0,
|
||||
'totalMembres': 0,
|
||||
'moyenneMembres': 0.0,
|
||||
'parType': <String, int>{},
|
||||
'parStatut': <String, int>{},
|
||||
};
|
||||
}
|
||||
|
||||
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);
|
||||
final moyenneMembres = totalMembres / organizations.length;
|
||||
|
||||
// Statistiques par type
|
||||
final parType = <String, int>{};
|
||||
for (final org in organizations) {
|
||||
final type = org.typeOrganisation.displayName;
|
||||
parType[type] = (parType[type] ?? 0) + 1;
|
||||
}
|
||||
|
||||
// Statistiques par statut
|
||||
final parStatut = <String, int>{};
|
||||
for (final org in organizations) {
|
||||
final statut = org.statut.displayName;
|
||||
parStatut[statut] = (parStatut[statut] ?? 0) + 1;
|
||||
}
|
||||
|
||||
return {
|
||||
'total': organizations.length,
|
||||
'actives': actives,
|
||||
'inactives': inactives,
|
||||
'totalMembres': totalMembres,
|
||||
'moyenneMembres': moyenneMembres,
|
||||
'parType': parType,
|
||||
'parStatut': parStatut,
|
||||
};
|
||||
}
|
||||
|
||||
/// Validation des données d'organisation
|
||||
void _validateOrganization(OrganizationModel organization) {
|
||||
if (organization.nom.trim().isEmpty) {
|
||||
throw ArgumentError('Le nom de l\'organisation est obligatoire');
|
||||
}
|
||||
|
||||
if (organization.nom.trim().length < 2) {
|
||||
throw ArgumentError('Le nom de l\'organisation doit contenir au moins 2 caractères');
|
||||
}
|
||||
|
||||
if (organization.nom.trim().length > 200) {
|
||||
throw ArgumentError('Le nom de l\'organisation ne peut pas dépasser 200 caractères');
|
||||
}
|
||||
|
||||
if (organization.nomCourt != null && organization.nomCourt!.length > 50) {
|
||||
throw ArgumentError('Le nom court ne peut pas dépasser 50 caractères');
|
||||
}
|
||||
|
||||
if (organization.email != null && organization.email!.isNotEmpty) {
|
||||
if (!_isValidEmail(organization.email!)) {
|
||||
throw ArgumentError('L\'adresse email n\'est pas valide');
|
||||
}
|
||||
}
|
||||
|
||||
if (organization.telephone != null && organization.telephone!.isNotEmpty) {
|
||||
if (!_isValidPhone(organization.telephone!)) {
|
||||
throw ArgumentError('Le numéro de téléphone n\'est pas valide');
|
||||
}
|
||||
}
|
||||
|
||||
if (organization.siteWeb != null && organization.siteWeb!.isNotEmpty) {
|
||||
if (!_isValidUrl(organization.siteWeb!)) {
|
||||
throw ArgumentError('L\'URL du site web n\'est pas valide');
|
||||
}
|
||||
}
|
||||
|
||||
if (organization.budgetAnnuel != null && organization.budgetAnnuel! < 0) {
|
||||
throw ArgumentError('Le budget annuel doit être positif');
|
||||
}
|
||||
|
||||
if (organization.montantCotisationAnnuelle != null && organization.montantCotisationAnnuelle! < 0) {
|
||||
throw ArgumentError('Le montant de cotisation doit être positif');
|
||||
}
|
||||
}
|
||||
|
||||
/// Validation d'email
|
||||
bool _isValidEmail(String email) {
|
||||
return RegExp(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$').hasMatch(email);
|
||||
}
|
||||
|
||||
/// Validation de téléphone
|
||||
bool _isValidPhone(String phone) {
|
||||
return RegExp(r'^\+?[0-9\s\-\(\)]{8,15}$').hasMatch(phone);
|
||||
}
|
||||
|
||||
/// Validation d'URL
|
||||
bool _isValidUrl(String url) {
|
||||
try {
|
||||
final uri = Uri.parse(url);
|
||||
return uri.hasScheme && (uri.scheme == 'http' || uri.scheme == 'https');
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user