import 'package:equatable/equatable.dart'; import 'demande_aide.dart'; /// Entité représentant une proposition d'aide dans le système de solidarité /// /// Cette entité encapsule toutes les informations relatives à une proposition d'aide, /// incluant les détails du proposant, les capacités et les conditions. class PropositionAide extends Equatable { /// Identifiant unique de la proposition final String id; /// Numéro de référence unique (format: PA-YYYY-NNNNNN) final String numeroReference; /// Titre de la proposition d'aide final String titre; /// Description détaillée de la proposition final String description; /// Type d'aide proposée final TypeAide typeAide; /// Statut actuel de la proposition final StatutProposition statut; /// Identifiant du proposant final String proposantId; /// Nom complet du proposant final String nomProposant; /// Identifiant de l'organisation final String organisationId; /// Montant maximum proposé (si applicable) final double? montantMaximum; /// Montant minimum proposé (si applicable) final double? montantMinimum; /// Nombre maximum de bénéficiaires final int nombreMaxBeneficiaires; /// Nombre de bénéficiaires déjà aidés final int nombreBeneficiairesAides; /// Nombre de demandes traitées final int nombreDemandesTraitees; /// Montant total versé final double montantTotalVerse; /// Date de création de la proposition final DateTime dateCreation; /// Date de modification final DateTime dateModification; /// Date d'expiration final DateTime? dateExpiration; /// Délai de réponse en heures final int delaiReponseHeures; /// Zones géographiques couvertes final List zonesGeographiques; /// Créneaux de disponibilité final List creneauxDisponibilite; /// Critères de sélection final List criteresSelection; /// Contact du proposant final ContactProposant contactProposant; /// Conditions particulières final String? conditionsParticulieres; /// Instructions spéciales final String? instructionsSpeciales; /// Note moyenne des évaluations final double? noteMoyenne; /// Nombre d'évaluations reçues final int nombreEvaluations; /// Nombre de vues de la proposition final int nombreVues; /// Nombre de candidatures reçues final int nombreCandidatures; /// Score de pertinence calculé final double scorePertinence; /// Données personnalisées final Map donneesPersonnalisees; /// Indique si la proposition est disponible final bool estDisponible; /// Indique si la proposition est vérifiée final bool estVerifiee; /// Indique si la proposition est expirée final bool estExpiree; const PropositionAide({ required this.id, required this.numeroReference, required this.titre, required this.description, required this.typeAide, required this.statut, required this.proposantId, required this.nomProposant, required this.organisationId, this.montantMaximum, this.montantMinimum, required this.nombreMaxBeneficiaires, this.nombreBeneficiairesAides = 0, this.nombreDemandesTraitees = 0, this.montantTotalVerse = 0.0, required this.dateCreation, required this.dateModification, this.dateExpiration, this.delaiReponseHeures = 48, this.zonesGeographiques = const [], this.creneauxDisponibilite = const [], this.criteresSelection = const [], required this.contactProposant, this.conditionsParticulieres, this.instructionsSpeciales, this.noteMoyenne, this.nombreEvaluations = 0, this.nombreVues = 0, this.nombreCandidatures = 0, this.scorePertinence = 50.0, this.donneesPersonnalisees = const {}, this.estDisponible = true, this.estVerifiee = false, this.estExpiree = false, }); /// Calcule le nombre de places restantes int get placesRestantes { return nombreMaxBeneficiaires - nombreBeneficiairesAides; } /// Calcule le pourcentage de capacité utilisée double get pourcentageCapaciteUtilisee { if (nombreMaxBeneficiaires == 0) return 0.0; return (nombreBeneficiairesAides / nombreMaxBeneficiaires) * 100; } /// Indique si la proposition peut accepter de nouveaux bénéficiaires bool get peutAccepterBeneficiaires { return estDisponible && !estExpiree && placesRestantes > 0; } /// Indique si la proposition est active et disponible bool get isActiveEtDisponible { return statut == StatutProposition.active && estDisponible && !estExpiree; } /// Calcule un score de compatibilité avec une demande double calculerScoreCompatibilite(DemandeAide demande) { double score = 0.0; // Correspondance du type d'aide (40 points max) if (demande.typeAide == typeAide) { score += 40.0; } else { // Bonus partiel pour les types similaires score += 20.0; } // Compatibilité financière (25 points max) if (demande.montantDemande != null && montantMaximum != null) { if (demande.montantDemande! <= montantMaximum!) { score += 25.0; } else { // Pénalité proportionnelle double ratio = montantMaximum! / demande.montantDemande!; score += 25.0 * ratio; } } else if (demande.montantDemande == null) { score += 25.0; // Pas de contrainte financière } // Expérience du proposant (15 points max) if (nombreBeneficiairesAides > 0) { score += (nombreBeneficiairesAides * 2.0).clamp(0.0, 15.0); } // Réputation (10 points max) if (noteMoyenne != null && nombreEvaluations >= 3) { score += (noteMoyenne! - 3.0) * 3.33; } // Disponibilité (10 points max) if (peutAccepterBeneficiaires) { double ratioCapacite = placesRestantes / nombreMaxBeneficiaires; score += 10.0 * ratioCapacite; } return score.clamp(0.0, 100.0); } /// Obtient la couleur associée au statut String get couleurStatut => statut.couleur; /// Obtient l'icône associée au type d'aide String get iconeTypeAide => typeAide.icone; @override List get props => [ id, numeroReference, titre, description, typeAide, statut, proposantId, nomProposant, organisationId, montantMaximum, montantMinimum, nombreMaxBeneficiaires, nombreBeneficiairesAides, nombreDemandesTraitees, montantTotalVerse, dateCreation, dateModification, dateExpiration, delaiReponseHeures, zonesGeographiques, creneauxDisponibilite, criteresSelection, contactProposant, conditionsParticulieres, instructionsSpeciales, noteMoyenne, nombreEvaluations, nombreVues, nombreCandidatures, scorePertinence, donneesPersonnalisees, estDisponible, estVerifiee, estExpiree, ]; PropositionAide copyWith({ String? id, String? numeroReference, String? titre, String? description, TypeAide? typeAide, StatutProposition? statut, String? proposantId, String? nomProposant, String? organisationId, double? montantMaximum, double? montantMinimum, int? nombreMaxBeneficiaires, int? nombreBeneficiairesAides, int? nombreDemandesTraitees, double? montantTotalVerse, DateTime? dateCreation, DateTime? dateModification, DateTime? dateExpiration, int? delaiReponseHeures, List? zonesGeographiques, List? creneauxDisponibilite, List? criteresSelection, ContactProposant? contactProposant, String? conditionsParticulieres, String? instructionsSpeciales, double? noteMoyenne, int? nombreEvaluations, int? nombreVues, int? nombreCandidatures, double? scorePertinence, Map? donneesPersonnalisees, bool? estDisponible, bool? estVerifiee, bool? estExpiree, }) { return PropositionAide( id: id ?? this.id, numeroReference: numeroReference ?? this.numeroReference, titre: titre ?? this.titre, description: description ?? this.description, typeAide: typeAide ?? this.typeAide, statut: statut ?? this.statut, proposantId: proposantId ?? this.proposantId, nomProposant: nomProposant ?? this.nomProposant, organisationId: organisationId ?? this.organisationId, montantMaximum: montantMaximum ?? this.montantMaximum, montantMinimum: montantMinimum ?? this.montantMinimum, nombreMaxBeneficiaires: nombreMaxBeneficiaires ?? this.nombreMaxBeneficiaires, nombreBeneficiairesAides: nombreBeneficiairesAides ?? this.nombreBeneficiairesAides, nombreDemandesTraitees: nombreDemandesTraitees ?? this.nombreDemandesTraitees, montantTotalVerse: montantTotalVerse ?? this.montantTotalVerse, dateCreation: dateCreation ?? this.dateCreation, dateModification: dateModification ?? this.dateModification, dateExpiration: dateExpiration ?? this.dateExpiration, delaiReponseHeures: delaiReponseHeures ?? this.delaiReponseHeures, zonesGeographiques: zonesGeographiques ?? this.zonesGeographiques, creneauxDisponibilite: creneauxDisponibilite ?? this.creneauxDisponibilite, criteresSelection: criteresSelection ?? this.criteresSelection, contactProposant: contactProposant ?? this.contactProposant, conditionsParticulieres: conditionsParticulieres ?? this.conditionsParticulieres, instructionsSpeciales: instructionsSpeciales ?? this.instructionsSpeciales, noteMoyenne: noteMoyenne ?? this.noteMoyenne, nombreEvaluations: nombreEvaluations ?? this.nombreEvaluations, nombreVues: nombreVues ?? this.nombreVues, nombreCandidatures: nombreCandidatures ?? this.nombreCandidatures, scorePertinence: scorePertinence ?? this.scorePertinence, donneesPersonnalisees: donneesPersonnalisees ?? this.donneesPersonnalisees, estDisponible: estDisponible ?? this.estDisponible, estVerifiee: estVerifiee ?? this.estVerifiee, estExpiree: estExpiree ?? this.estExpiree, ); } } /// Énumération des statuts de proposition d'aide enum StatutProposition { active('Active', 'check_circle', '#4CAF50'), suspendue('Suspendue', 'pause_circle', '#FF9800'), terminee('Terminée', 'done_all', '#607D8B'), expiree('Expirée', 'schedule', '#9E9E9E'), supprimee('Supprimée', 'delete', '#F44336'); const StatutProposition(this.libelle, this.icone, this.couleur); final String libelle; final String icone; final String couleur; } /// Classe représentant un créneau de disponibilité class CreneauDisponibilite extends Equatable { final String jourSemaine; final String heureDebut; final String heureFin; final String? commentaire; const CreneauDisponibilite({ required this.jourSemaine, required this.heureDebut, required this.heureFin, this.commentaire, }); @override List get props => [jourSemaine, heureDebut, heureFin, commentaire]; } /// Classe représentant un critère de sélection class CritereSelection extends Equatable { final String nom; final String valeur; final bool estObligatoire; final String? description; const CritereSelection({ required this.nom, required this.valeur, this.estObligatoire = false, this.description, }); @override List get props => [nom, valeur, estObligatoire, description]; } /// Classe représentant le contact d'un proposant class ContactProposant extends Equatable { final String nom; final String telephone; final String? email; final String? adresse; final String? methodePrefereee; const ContactProposant({ required this.nom, required this.telephone, this.email, this.adresse, this.methodePrefereee, }); @override List get props => [nom, telephone, email, adresse, methodePrefereee]; }