import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import '../../../../core/models/membre_model.dart'; import '../../../../core/models/cotisation_model.dart'; import '../../../../shared/theme/app_theme.dart'; /// Section des cotisations d'un membre class MembreCotisationsSection extends StatelessWidget { const MembreCotisationsSection({ super.key, required this.membre, required this.cotisations, required this.isLoading, this.onRefresh, }); final MembreModel membre; final List cotisations; final bool isLoading; final VoidCallback? onRefresh; @override Widget build(BuildContext context) { if (isLoading) { return const Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ CircularProgressIndicator(), SizedBox(height: 16), Text('Chargement des cotisations...'), ], ), ); } return RefreshIndicator( onRefresh: () async { onRefresh?.call(); }, child: SingleChildScrollView( physics: const AlwaysScrollableScrollPhysics(), padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildSummaryCard(), const SizedBox(height: 16), _buildCotisationsList(), ], ), ), ); } Widget _buildSummaryCard() { final totalDu = cotisations.fold( 0, (sum, cotisation) => sum + cotisation.montantDu, ); final totalPaye = cotisations.fold( 0, (sum, cotisation) => sum + cotisation.montantPaye, ); final totalRestant = totalDu - totalPaye; final cotisationsPayees = cotisations.where((c) => c.statut == 'PAYEE').length; final cotisationsEnRetard = cotisations.where((c) => c.isEnRetard).length; return Card( elevation: 2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), child: Padding( padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Row( children: [ Icon( Icons.account_balance_wallet, color: AppTheme.primaryColor, size: 24, ), SizedBox(width: 8), Text( 'Résumé des cotisations', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: AppTheme.textPrimary, ), ), ], ), const SizedBox(height: 20), Row( children: [ Expanded( child: _buildSummaryItem( 'Total dû', _formatAmount(totalDu), AppTheme.infoColor, Icons.receipt_long, ), ), Expanded( child: _buildSummaryItem( 'Payé', _formatAmount(totalPaye), AppTheme.successColor, Icons.check_circle, ), ), ], ), const SizedBox(height: 12), Row( children: [ Expanded( child: _buildSummaryItem( 'Restant', _formatAmount(totalRestant), totalRestant > 0 ? AppTheme.warningColor : AppTheme.successColor, Icons.pending, ), ), Expanded( child: _buildSummaryItem( 'En retard', '$cotisationsEnRetard', cotisationsEnRetard > 0 ? AppTheme.errorColor : AppTheme.successColor, Icons.warning, ), ), ], ), const SizedBox(height: 16), LinearProgressIndicator( value: totalDu > 0 ? totalPaye / totalDu : 0, backgroundColor: AppTheme.backgroundLight, valueColor: AlwaysStoppedAnimation( totalPaye == totalDu ? AppTheme.successColor : AppTheme.primaryColor, ), ), const SizedBox(height: 8), Text( '$cotisationsPayees/${cotisations.length} cotisations payées', style: const TextStyle( fontSize: 12, color: AppTheme.textSecondary, ), ), ], ), ), ); } Widget _buildSummaryItem(String label, String value, Color color, IconData icon) { return Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: color.withOpacity(0.1), borderRadius: BorderRadius.circular(8), border: Border.all(color: color.withOpacity(0.3)), ), child: Column( children: [ Icon(icon, color: color, size: 20), const SizedBox(height: 4), Text( value, style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: color, ), ), Text( label, style: const TextStyle( fontSize: 12, color: AppTheme.textSecondary, ), ), ], ), ); } Widget _buildCotisationsList() { if (cotisations.isEmpty) { return Card( elevation: 1, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), child: const Padding( padding: EdgeInsets.all(32), child: Column( children: [ Icon( Icons.receipt_long_outlined, size: 48, color: AppTheme.textHint, ), SizedBox(height: 16), Text( 'Aucune cotisation', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: AppTheme.textPrimary, ), ), SizedBox(height: 8), Text( 'Ce membre n\'a pas encore de cotisations enregistrées.', textAlign: TextAlign.center, style: TextStyle( fontSize: 14, color: AppTheme.textSecondary, ), ), ], ), ), ); } return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Row( children: [ Icon( Icons.list_alt, color: AppTheme.primaryColor, size: 20, ), SizedBox(width: 8), Text( 'Historique des cotisations', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: AppTheme.textPrimary, ), ), ], ), const SizedBox(height: 12), ...cotisations.map((cotisation) => _buildCotisationCard(cotisation)), ], ); } Widget _buildCotisationCard(CotisationModel cotisation) { return Card( elevation: 1, margin: const EdgeInsets.only(bottom: 8), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( cotisation.periode ?? 'Période non définie', style: const TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: AppTheme.textPrimary, ), ), const SizedBox(height: 4), Text( cotisation.typeCotisation, style: const TextStyle( fontSize: 12, color: AppTheme.textSecondary, ), ), ], ), ), _buildStatusBadge(cotisation), ], ), const SizedBox(height: 12), Row( children: [ Expanded( child: _buildCotisationDetail( 'Montant dû', _formatAmount(cotisation.montantDu), Icons.receipt, ), ), Expanded( child: _buildCotisationDetail( 'Montant payé', _formatAmount(cotisation.montantPaye), Icons.payment, ), ), ], ), const SizedBox(height: 8), Row( children: [ Expanded( child: _buildCotisationDetail( 'Échéance', DateFormat('dd/MM/yyyy').format(cotisation.dateEcheance), Icons.schedule, ), ), if (cotisation.datePaiement != null) Expanded( child: _buildCotisationDetail( 'Payé le', DateFormat('dd/MM/yyyy').format(cotisation.datePaiement!), Icons.check_circle, ), ), ], ), ], ), ), ); } Widget _buildStatusBadge(CotisationModel cotisation) { Color color; String label; switch (cotisation.statut) { case 'PAYEE': color = AppTheme.successColor; label = 'Payée'; break; case 'EN_ATTENTE': color = AppTheme.warningColor; label = 'En attente'; break; case 'EN_RETARD': color = AppTheme.errorColor; label = 'En retard'; break; case 'PARTIELLEMENT_PAYEE': color = AppTheme.infoColor; label = 'Partielle'; break; default: color = AppTheme.textSecondary; label = cotisation.statut; } return Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: color.withOpacity(0.1), borderRadius: BorderRadius.circular(12), border: Border.all(color: color.withOpacity(0.3)), ), child: Text( label, style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, color: color, ), ), ); } Widget _buildCotisationDetail(String label, String value, IconData icon) { return Row( children: [ Icon(icon, size: 14, color: AppTheme.textSecondary), const SizedBox(width: 4), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( label, style: const TextStyle( fontSize: 10, color: AppTheme.textSecondary, ), ), Text( value, style: const TextStyle( fontSize: 12, fontWeight: FontWeight.w500, color: AppTheme.textPrimary, ), ), ], ), ), ], ); } String _formatAmount(double amount) { return NumberFormat.currency( locale: 'fr_FR', symbol: 'FCFA', decimalDigits: 0, ).format(amount); } }