import 'package:equatable/equatable.dart'; /// Énumération des types de métriques disponibles enum TypeMetrique { // Métriques membres nombreMembresActifs('Nombre de membres actifs', 'membres', 'count'), nombreMembresInactifs('Nombre de membres inactifs', 'membres', 'count'), tauxCroissanceMembres('Taux de croissance des membres', 'membres', 'percentage'), moyenneAgeMembres('Âge moyen des membres', 'membres', 'average'), // Métriques financières totalCotisationsCollectees('Total des cotisations collectées', 'finance', 'amount'), cotisationsEnAttente('Cotisations en attente', 'finance', 'amount'), tauxRecouvrementCotisations('Taux de recouvrement', 'finance', 'percentage'), moyenneCotisationMembre('Cotisation moyenne par membre', 'finance', 'average'), // Métriques événements nombreEvenementsOrganises('Nombre d\'événements organisés', 'evenements', 'count'), tauxParticipationEvenements('Taux de participation aux événements', 'evenements', 'percentage'), moyenneParticipantsEvenement('Moyenne de participants par événement', 'evenements', 'average'), // Métriques solidarité nombreDemandesAide('Nombre de demandes d\'aide', 'solidarite', 'count'), montantAidesAccordees('Montant des aides accordées', 'solidarite', 'amount'), tauxApprobationAides('Taux d\'approbation des aides', 'solidarite', 'percentage'); const TypeMetrique(this.libelle, this.categorie, this.typeValeur); final String libelle; final String categorie; final String typeValeur; /// Retourne l'unité de mesure appropriée String get unite { switch (typeValeur) { case 'percentage': return '%'; case 'amount': return 'XOF'; case 'average': return typeValeur == 'moyenneAgeMembres' ? 'ans' : ''; default: return ''; } } /// Retourne l'icône Material Design appropriée String get icone { switch (categorie) { case 'membres': return 'people'; case 'finance': return 'attach_money'; case 'evenements': return 'event'; case 'solidarite': return 'favorite'; default: return 'analytics'; } } /// Retourne la couleur appropriée String get couleur { switch (categorie) { case 'membres': return '#2196F3'; case 'finance': return '#4CAF50'; case 'evenements': return '#FF9800'; case 'solidarite': return '#E91E63'; default: return '#757575'; } } } /// Énumération des périodes d'analyse enum PeriodeAnalyse { aujourdHui('Aujourd\'hui', 'today'), hier('Hier', 'yesterday'), cetteSemaine('Cette semaine', 'this_week'), semaineDerniere('Semaine dernière', 'last_week'), ceMois('Ce mois', 'this_month'), moisDernier('Mois dernier', 'last_month'), troisDerniersMois('3 derniers mois', 'last_3_months'), sixDerniersMois('6 derniers mois', 'last_6_months'), cetteAnnee('Cette année', 'this_year'), anneeDerniere('Année dernière', 'last_year'), septDerniersJours('7 derniers jours', 'last_7_days'), trenteDerniersJours('30 derniers jours', 'last_30_days'), periodePersonnalisee('Période personnalisée', 'custom'); const PeriodeAnalyse(this.libelle, this.code); final String libelle; final String code; /// Vérifie si la période est courte (moins d'un mois) bool get isPeriodeCourte { return [ aujourdHui, hier, cetteSemaine, semaineDerniere, septDerniersJours ].contains(this); } /// Vérifie si la période est longue (plus d'un an) bool get isPeriodeLongue { return [cetteAnnee, anneeDerniere].contains(this); } } /// Entité représentant une donnée analytics class AnalyticsData extends Equatable { const AnalyticsData({ required this.id, required this.typeMetrique, required this.periodeAnalyse, required this.valeur, this.valeurPrecedente, this.pourcentageEvolution, required this.dateDebut, required this.dateFin, required this.dateCalcul, this.organisationId, this.nomOrganisation, this.utilisateurId, this.nomUtilisateur, this.libellePersonnalise, this.description, this.donneesDetaillees, this.configurationGraphique, this.metadonnees, this.indicateurFiabilite = 95.0, this.nombreElementsAnalyses, this.tempsCalculMs, this.tempsReel = false, this.necessiteMiseAJour = false, this.niveauPriorite = 3, this.tags, }); final String id; final TypeMetrique typeMetrique; final PeriodeAnalyse periodeAnalyse; final double valeur; final double? valeurPrecedente; final double? pourcentageEvolution; final DateTime dateDebut; final DateTime dateFin; final DateTime dateCalcul; final String? organisationId; final String? nomOrganisation; final String? utilisateurId; final String? nomUtilisateur; final String? libellePersonnalise; final String? description; final String? donneesDetaillees; final String? configurationGraphique; final Map? metadonnees; final double indicateurFiabilite; final int? nombreElementsAnalyses; final int? tempsCalculMs; final bool tempsReel; final bool necessiteMiseAJour; final int niveauPriorite; final List? tags; /// Retourne le libellé à afficher String get libelleAffichage { return libellePersonnalise?.isNotEmpty == true ? libellePersonnalise! : typeMetrique.libelle; } /// Retourne l'unité de mesure String get unite => typeMetrique.unite; /// Retourne l'icône String get icone => typeMetrique.icone; /// Retourne la couleur String get couleur => typeMetrique.couleur; /// Vérifie si la métrique a évolué positivement bool get hasEvolutionPositive { return pourcentageEvolution != null && pourcentageEvolution! > 0; } /// Vérifie si la métrique a évolué négativement bool get hasEvolutionNegative { return pourcentageEvolution != null && pourcentageEvolution! < 0; } /// Vérifie si la métrique est stable bool get isStable { return pourcentageEvolution != null && pourcentageEvolution! == 0; } /// Retourne la tendance sous forme de texte String get tendance { if (hasEvolutionPositive) return 'hausse'; if (hasEvolutionNegative) return 'baisse'; return 'stable'; } /// Vérifie si les données sont fiables bool get isDonneesFiables => indicateurFiabilite >= 80.0; /// Vérifie si la métrique est critique bool get isCritique => niveauPriorite >= 4; /// Formate la valeur avec l'unité appropriée String get valeurFormatee { switch (typeMetrique.typeValeur) { case 'amount': return '${valeur.toStringAsFixed(0)} ${unite}'; case 'percentage': return '${valeur.toStringAsFixed(1)}${unite}'; case 'average': return valeur.toStringAsFixed(1); default: return valeur.toStringAsFixed(0); } } /// Formate le pourcentage d'évolution String get evolutionFormatee { if (pourcentageEvolution == null) return ''; final signe = pourcentageEvolution! >= 0 ? '+' : ''; return '$signe${pourcentageEvolution!.toStringAsFixed(1)}%'; } @override List get props => [ id, typeMetrique, periodeAnalyse, valeur, valeurPrecedente, pourcentageEvolution, dateDebut, dateFin, dateCalcul, organisationId, nomOrganisation, utilisateurId, nomUtilisateur, libellePersonnalise, description, donneesDetaillees, configurationGraphique, metadonnees, indicateurFiabilite, nombreElementsAnalyses, tempsCalculMs, tempsReel, necessiteMiseAJour, niveauPriorite, tags, ]; AnalyticsData copyWith({ String? id, TypeMetrique? typeMetrique, PeriodeAnalyse? periodeAnalyse, double? valeur, double? valeurPrecedente, double? pourcentageEvolution, DateTime? dateDebut, DateTime? dateFin, DateTime? dateCalcul, String? organisationId, String? nomOrganisation, String? utilisateurId, String? nomUtilisateur, String? libellePersonnalise, String? description, String? donneesDetaillees, String? configurationGraphique, Map? metadonnees, double? indicateurFiabilite, int? nombreElementsAnalyses, int? tempsCalculMs, bool? tempsReel, bool? necessiteMiseAJour, int? niveauPriorite, List? tags, }) { return AnalyticsData( id: id ?? this.id, typeMetrique: typeMetrique ?? this.typeMetrique, periodeAnalyse: periodeAnalyse ?? this.periodeAnalyse, valeur: valeur ?? this.valeur, valeurPrecedente: valeurPrecedente ?? this.valeurPrecedente, pourcentageEvolution: pourcentageEvolution ?? this.pourcentageEvolution, dateDebut: dateDebut ?? this.dateDebut, dateFin: dateFin ?? this.dateFin, dateCalcul: dateCalcul ?? this.dateCalcul, organisationId: organisationId ?? this.organisationId, nomOrganisation: nomOrganisation ?? this.nomOrganisation, utilisateurId: utilisateurId ?? this.utilisateurId, nomUtilisateur: nomUtilisateur ?? this.nomUtilisateur, libellePersonnalise: libellePersonnalise ?? this.libellePersonnalise, description: description ?? this.description, donneesDetaillees: donneesDetaillees ?? this.donneesDetaillees, configurationGraphique: configurationGraphique ?? this.configurationGraphique, metadonnees: metadonnees ?? this.metadonnees, indicateurFiabilite: indicateurFiabilite ?? this.indicateurFiabilite, nombreElementsAnalyses: nombreElementsAnalyses ?? this.nombreElementsAnalyses, tempsCalculMs: tempsCalculMs ?? this.tempsCalculMs, tempsReel: tempsReel ?? this.tempsReel, necessiteMiseAJour: necessiteMiseAJour ?? this.necessiteMiseAJour, niveauPriorite: niveauPriorite ?? this.niveauPriorite, tags: tags ?? this.tags, ); } }