import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:intl/intl.dart'; import '../../../../core/models/cotisation_model.dart'; import '../../../../shared/theme/app_theme.dart'; /// Widget card pour afficher une cotisation class CotisationCard extends StatelessWidget { final CotisationModel cotisation; final VoidCallback? onTap; final VoidCallback? onPay; final VoidCallback? onEdit; final VoidCallback? onDelete; const CotisationCard({ super.key, required this.cotisation, this.onTap, this.onPay, this.onEdit, this.onDelete, }); @override Widget build(BuildContext context) { final currencyFormat = NumberFormat.currency( locale: 'fr_FR', symbol: 'FCFA', decimalDigits: 0, ); final dateFormat = DateFormat('dd/MM/yyyy', 'fr_FR'); return Card( elevation: 2, margin: EdgeInsets.zero, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), side: BorderSide( color: _getStatusColor().withOpacity(0.3), width: 1, ), ), child: InkWell( onTap: () { HapticFeedback.lightImpact(); onTap?.call(); }, borderRadius: BorderRadius.circular(12), child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Header avec statut et actions Row( children: [ // Statut badge Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: _getStatusColor().withOpacity(0.1), borderRadius: BorderRadius.circular(12), ), child: Text( cotisation.libelleStatut, style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, color: _getStatusColor(), ), ), ), const Spacer(), // Actions if (cotisation.statut == 'EN_ATTENTE' || cotisation.statut == 'EN_RETARD') IconButton( onPressed: () { HapticFeedback.lightImpact(); onPay?.call(); }, icon: const Icon(Icons.payment, size: 20), color: AppTheme.successColor, tooltip: 'Payer', ), if (onEdit != null) IconButton( onPressed: onEdit, icon: const Icon(Icons.edit, size: 20), color: AppTheme.primaryColor, tooltip: 'Modifier', ), if (onDelete != null) IconButton( onPressed: onDelete, icon: const Icon(Icons.delete, size: 20), color: AppTheme.errorColor, tooltip: 'Supprimer', ), ], ), const SizedBox(height: 12), // Informations principales Row( children: [ // Icône du type Container( width: 40, height: 40, decoration: BoxDecoration( color: AppTheme.primaryColor.withOpacity(0.1), borderRadius: BorderRadius.circular(20), ), child: Center( child: Text( cotisation.iconeTypeCotisation, style: const TextStyle(fontSize: 20), ), ), ), const SizedBox(width: 12), // Détails Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( cotisation.libelleTypeCotisation, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: AppTheme.textPrimary, ), ), if (cotisation.nomMembre != null) ...[ const SizedBox(height: 2), Text( cotisation.nomMembre!, style: const TextStyle( fontSize: 14, color: AppTheme.textSecondary, ), ), ], if (cotisation.periode != null) ...[ const SizedBox(height: 2), Text( cotisation.periode!, style: const TextStyle( fontSize: 12, color: AppTheme.textHint, ), ), ], ], ), ), // Montant Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( currencyFormat.format(cotisation.montantDu), style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: AppTheme.textPrimary, ), ), if (cotisation.montantPaye > 0) ...[ const SizedBox(height: 2), Text( 'Payé: ${currencyFormat.format(cotisation.montantPaye)}', style: const TextStyle( fontSize: 12, color: AppTheme.successColor, ), ), ], ], ), ], ), const SizedBox(height: 12), // Barre de progression du paiement if (cotisation.montantPaye > 0 && !cotisation.isEntierementPayee) ...[ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Progression', style: const TextStyle( fontSize: 12, color: AppTheme.textSecondary, ), ), Text( '${cotisation.pourcentagePaiement.toStringAsFixed(0)}%', style: const TextStyle( fontSize: 12, fontWeight: FontWeight.w600, color: AppTheme.textSecondary, ), ), ], ), const SizedBox(height: 4), LinearProgressIndicator( value: cotisation.pourcentagePaiement / 100, backgroundColor: AppTheme.borderColor, valueColor: AlwaysStoppedAnimation( cotisation.pourcentagePaiement >= 100 ? AppTheme.successColor : AppTheme.primaryColor, ), ), ], ), const SizedBox(height: 12), ], // Informations d'échéance Row( children: [ Icon( Icons.schedule, size: 16, color: cotisation.isEnRetard ? AppTheme.errorColor : cotisation.echeanceProche ? AppTheme.warningColor : AppTheme.textHint, ), const SizedBox(width: 4), Text( 'Échéance: ${dateFormat.format(cotisation.dateEcheance)}', style: TextStyle( fontSize: 12, color: cotisation.isEnRetard ? AppTheme.errorColor : cotisation.echeanceProche ? AppTheme.warningColor : AppTheme.textSecondary, ), ), if (cotisation.messageUrgence.isNotEmpty) ...[ const SizedBox(width: 8), Container( padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), decoration: BoxDecoration( color: cotisation.isEnRetard ? AppTheme.errorColor.withOpacity(0.1) : AppTheme.warningColor.withOpacity(0.1), borderRadius: BorderRadius.circular(8), ), child: Text( cotisation.messageUrgence, style: TextStyle( fontSize: 10, fontWeight: FontWeight.w600, color: cotisation.isEnRetard ? AppTheme.errorColor : AppTheme.warningColor, ), ), ), ], ], ), // Référence const SizedBox(height: 8), Row( children: [ const Icon( Icons.tag, size: 16, color: AppTheme.textHint, ), const SizedBox(width: 4), Text( 'Réf: ${cotisation.numeroReference}', style: const TextStyle( fontSize: 12, color: AppTheme.textHint, ), ), ], ), ], ), ), ), ); } Color _getStatusColor() { switch (cotisation.statut) { case 'PAYEE': return AppTheme.successColor; case 'EN_ATTENTE': return AppTheme.warningColor; case 'EN_RETARD': return AppTheme.errorColor; case 'PARTIELLEMENT_PAYEE': return AppTheme.infoColor; case 'ANNULEE': return AppTheme.textHint; default: return AppTheme.textSecondary; } } }